Bug Summary

File:root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc
Warning:line 937, column 7
Value stored to 'preserve_from' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name regexp-bytecode-peephole.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/irregexp -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/irregexp -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src -I /root/firefox-clang/js/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -D V8_INTL_SUPPORT -D V8_ENABLE_REGEXP_DIAGNOSTICS -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-error=type-limits -Wno-error=return-type -Wno-sign-compare -Wno-c++11-narrowing -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc
1// Copyright 2019 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "irregexp/imported/regexp-bytecode-peephole.h"
6
7#include <limits>
8#include <memory>
9#include <optional>
10#include <unordered_map>
11#include <vector>
12
13#include "irregexp/imported/regexp-bytecode-generator-inl.h"
14#include "irregexp/imported/regexp-bytecode-generator.h"
15#include "irregexp/imported/regexp-bytecodes-inl.h"
16#include "irregexp/imported/regexp-bytecodes.h"
17
18namespace v8 {
19namespace internal {
20namespace regexp {
21
22namespace {
23
24class BytecodeArgument {
25 public:
26 BytecodeArgument(int offset, int length) : offset_(offset), length_(length) {}
27
28 int offset() const { return offset_; }
29 int length() const { return length_; }
30
31 private:
32 // TODO(jgruber): This should store {offset,type} as well.
33 // TODO(jgruber): Consider changing offset_ to be relative to the current
34 // bytecode instead of the start of the bytecode sequence that is being
35 // optimized. It is confusing that src/dst offsets have different semantics.
36 int offset_;
37 int length_;
38};
39
40// Describes a bytecode operand for use in a peephole sequence.
41struct OpInfo {
42 uint16_t offset;
43 BytecodeOperandType type;
44 constexpr int size() const { return Bytecodes::Size(type); }
45
46 // Usage:
47 // OpInfo::Get<BytecodeOperands<BYTECODE>,
48 // BytecodeOperands<BYTECODE>::Operand::OPERAND>()
49 template <class kBytecodeOperands, auto kOperand>
50 static OpInfo Get() {
51 static constexpr int kOffset = kBytecodeOperands::Offset(kOperand);
52 static constexpr BytecodeOperandType kType =
53 kBytecodeOperands::Type(kOperand);
54 DCHECK_LE(static_cast<uint32_t>(kOffset),do { static_assert( mozilla::detail::AssertionConditionType<
decltype((static_cast<uint32_t>(kOffset)) <= (std::numeric_limits
<decltype(offset)>::max()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((static_cast<uint32_t>
(kOffset)) <= (std::numeric_limits<decltype(offset)>
::max())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(static_cast<uint32_t>(kOffset)) <= (std::numeric_limits<decltype(offset)>::max())"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 55); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(static_cast<uint32_t>(kOffset)) <= (std::numeric_limits<decltype(offset)>::max())"
")"); do { MOZ_CrashSequence(__null, 55); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
55 std::numeric_limits<decltype(offset)>::max())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((static_cast<uint32_t>(kOffset)) <= (std::numeric_limits
<decltype(offset)>::max()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((static_cast<uint32_t>
(kOffset)) <= (std::numeric_limits<decltype(offset)>
::max())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(static_cast<uint32_t>(kOffset)) <= (std::numeric_limits<decltype(offset)>::max())"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 55); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(static_cast<uint32_t>(kOffset)) <= (std::numeric_limits<decltype(offset)>::max())"
")"); do { MOZ_CrashSequence(__null, 55); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
56 return {kOffset, kType};
57 }
58};
59static_assert(sizeof(OpInfo) <= kSystemPointerSize); // Passed by value.
60
61class BytecodeArgumentMapping : public BytecodeArgument {
62 public:
63 enum class Type : uint8_t { kDefault, kOffsetAfterSequence };
64
65 BytecodeArgumentMapping(int offset, int length, OpInfo op_info)
66 : BytecodeArgument(offset, length),
67 type_(Type::kDefault),
68 op_info_(op_info) {}
69
70 BytecodeArgumentMapping(Type type, OpInfo op_info)
71 : BytecodeArgument(-1, -1), type_(type), op_info_(op_info) {
72 DCHECK_NE(type, Type::kDefault)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((type) != (Type::kDefault))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((type) != (Type::kDefault)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(type) != (Type::kDefault)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 72); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(type) != (Type::kDefault)"
")"); do { MOZ_CrashSequence(__null, 72); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
73 }
74
75 Type type() const { return type_; }
76 int new_offset() const { return op_info_.offset; }
77 BytecodeOperandType new_operand_type() const { return op_info_.type; }
78 int new_length() const { return op_info_.size(); }
79
80 private:
81 Type type_;
82 OpInfo op_info_;
83};
84
85struct BytecodeArgumentCheck : public BytecodeArgument {
86 enum CheckType { kCheckAddress = 0, kCheckValue };
87 CheckType type;
88 int check_offset;
89 int check_length;
90
91 BytecodeArgumentCheck(int offset, int length, int check_offset)
92 : BytecodeArgument(offset, length),
93 type(kCheckAddress),
94 check_offset(check_offset) {}
95 BytecodeArgumentCheck(int offset, int length, int check_offset,
96 int check_length)
97 : BytecodeArgument(offset, length),
98 type(kCheckValue),
99 check_offset(check_offset),
100 check_length(check_length) {}
101};
102
103// Trie-Node for storing bytecode sequences we want to optimize.
104class BytecodeSequenceNode {
105 public:
106 explicit BytecodeSequenceNode(std::optional<Bytecode> bytecode);
107 // Adds a new node as child of the current node if it isn't a child already.
108 BytecodeSequenceNode& FollowedBy(Bytecode bytecode);
109 // Marks the end of a sequence and sets optimized bytecode to replace all
110 // bytecodes of the sequence with.
111 BytecodeSequenceNode& ReplaceWith(Bytecode bytecode);
112 // Maps arguments of bytecodes in the sequence to the optimized bytecode.
113 // Order of invocation determines order of arguments in the optimized
114 // bytecode.
115 // Invoking this method is only allowed on nodes that mark the end of a valid
116 // sequence (i.e. after ReplaceWith()).
117 // to_op_info: Operand info of the argument in the optimized bytecode.
118 // from_bytecode_sequence_index: Zero-based index of the referred bytecode
119 // within the sequence (e.g. the bytecode passed to CreateSequence() has
120 // index 0).
121 // from_op_info: Operand info of the argument in the referred bytecode.
122 BytecodeSequenceNode& MapArgument(OpInfo to_op_info,
123 int from_bytecode_sequence_index,
124 OpInfo from_op_info);
125
126 // Emits the offset after the whole sequence.
127 // This should be used for every sequence that doesn't end in an unconditional
128 // jump. The offset isn't statically known, as bytecodes might be preserved
129 // after the sequence if they were jump targets from bytecodes outside the
130 // sequence. The emitted offset is after these potentially preserved
131 // bytecodes.
132 BytecodeSequenceNode& EmitOffsetAfterSequence(OpInfo op_info);
133 // Verifies that we've created mappings in the order they are specified.
134 bool BytecodeArgumentMappingCreatedInOrder(OpInfo op_info);
135 // Adds a check to the sequence node making it only a valid sequence when the
136 // argument of the current bytecode at the specified offset matches the offset
137 // to check against.
138 // op_info: Operand info of the argument to check.
139 // check_byte_offset: Zero-based offset relative to the beginning of the
140 // sequence that needs to match the value given by argument_offset. (e.g.
141 // check_byte_offset 0 matches the address of the first bytecode in the
142 // sequence).
143 BytecodeSequenceNode& IfArgumentEqualsOffset(OpInfo op_info,
144 int check_byte_offset);
145
146 // Adds a check to the sequence node making it only a valid sequence when the
147 // argument of the current bytecode at the specified offset matches the
148 // argument of another bytecode in the sequence.
149 // This is similar to IfArgumentEqualsOffset, except that this method matches
150 // the values of both arguments.
151 BytecodeSequenceNode& IfArgumentEqualsValueAtOffset(
152 OpInfo this_op_info, int other_bytecode_index_in_sequence,
153 OpInfo other_op_info);
154
155 // Marks an argument as unused.
156 // All arguments that are not mapped explicitly have to be marked as unused.
157 // bytecode_index_in_sequence: Zero-based index of the referred bytecode
158 // within the sequence (e.g. the bytecode passed to CreateSequence() has
159 // index 0).
160 // op_info: Operand info of the argument to ignore.
161 BytecodeSequenceNode& IgnoreArgument(int bytecode_index_in_sequence,
162 OpInfo op_info);
163 // Checks if the current node is valid for the sequence. I.e. all conditions
164 // set by IfArgumentEqualsOffset and IfArgumentEquals are fulfilled by this
165 // node for the actual bytecode sequence.
166 bool CheckArguments(const uint8_t* bytecode, int pc) const;
167 // Returns whether this node marks the end of a valid sequence (i.e. can be
168 // replaced with an optimized bytecode).
169 bool IsSequence() const;
170 // Returns the length of the sequence in bytes.
171 int SequenceLength() const;
172 // Returns the optimized bytecode for the node.
173 Bytecode OptimizedBytecode() const;
174 // Returns the child of the current node matching the given bytecode or
175 // nullptr if no such child is found.
176 BytecodeSequenceNode* Find(Bytecode bytecode) const;
177 // Returns number of arguments mapped to the current node.
178 // Invoking this method is only allowed on nodes that mark the end of a valid
179 // sequence (i.e. if IsSequence())
180 size_t ArgumentSize() const;
181 // Returns the argument-mapping of the argument at index.
182 // Invoking this method is only allowed on nodes that mark the end of a valid
183 // sequence (i.e. if IsSequence())
184 BytecodeArgumentMapping ArgumentMapping(size_t index) const;
185 // Returns an iterator to begin of ignored arguments.
186 // Invoking this method is only allowed on nodes that mark the end of a valid
187 // sequence (i.e. if IsSequence())
188 std::vector<BytecodeArgument>::const_iterator ArgumentIgnoredBegin() const;
189 // Returns an iterator to end of ignored arguments.
190 // Invoking this method is only allowed on nodes that mark the end of a valid
191 // sequence (i.e. if IsSequence())
192 std::vector<BytecodeArgument>::const_iterator ArgumentIgnoredEnd() const;
193 // Returns whether the current node has ignored argument or not.
194 bool HasIgnoredArguments() const;
195
196 private:
197 // Returns a node in the sequence specified by its index within the sequence.
198 BytecodeSequenceNode& GetNodeByIndexInSequence(int index_in_sequence);
199
200 std::optional<Bytecode> bytecode_;
201 std::optional<Bytecode> bytecode_replacement_;
202 int index_in_sequence_;
203 int start_offset_;
204 BytecodeSequenceNode* parent_;
205 std::unordered_map<Bytecode, std::unique_ptr<BytecodeSequenceNode>> children_;
206 std::vector<BytecodeArgumentMapping> argument_mapping_;
207 std::vector<BytecodeArgumentCheck> argument_check_;
208 std::vector<BytecodeArgument> argument_ignored_;
209 // True, iff the node has been fully created (including conditions). This
210 // implies that it is invalid to add further conditions.
211 // Note that it currently *is* valid to define the replacement bytecode and
212 // its arguments mappings after a node has been sealed.
213 bool sealed_ = false;
214};
215
216class BytecodePeepholeSequences {
217 public:
218 BytecodePeepholeSequences();
219 const BytecodeSequenceNode* sequences() const { return &sequences_; }
220
221 private:
222 void DefineStandardSequences();
223 BytecodeSequenceNode& CreateSequence(Bytecode bytecode);
224
225 BytecodeSequenceNode sequences_;
226};
227
228class BytecodePeephole {
229 public:
230 // Parses bytecode and fills the internal buffer with the potentially
231 // optimized bytecode. Returns true when optimizations were performed, false
232 // otherwise.
233 static bool OptimizeBytecode(Zone* zone, const BytecodeWriter* src_writer,
234 BytecodeWriter* dst_writer);
235
236 private:
237 BytecodePeephole(Zone* zone, const BytecodeWriter* src_writer,
238 BytecodeWriter* dst_writer);
239
240 // Checks for optimization candidates at pc and emits optimized bytecode to
241 // the internal buffer. Returns the length of replaced bytecodes in bytes.
242 int TryOptimizeSequence(const uint8_t* bytecode, int bytecode_length,
243 int start_pc);
244 // Emits optimized bytecode to the internal buffer. start_pc points to the
245 // start of the sequence in bytecode and last_node is the last
246 // BytecodeSequenceNode of the matching sequence found.
247 void EmitOptimization(int start_pc, const uint8_t* bytecode,
248 const BytecodeSequenceNode& last_node);
249 // Adds a relative jump destination fixup at pos.
250 // Jump destination fixups are used to find offsets in the new bytecode that
251 // can be jumped to.
252 void AddJumpDestinationFixup(int fixup, int pos);
253 // Sets an absolute jump destination fixup at pos.
254 void SetJumpDestinationFixup(int fixup, int pos);
255 // Updates all jump targets in the new bytecode.
256 void FixJumps();
257 void EmitArgument(int start_pc, const uint8_t* bytecode,
258 BytecodeArgumentMapping arg);
259 int pc() const;
260 Zone* zone() const;
261
262 BytecodeWriter* const dst_writer_;
263 const BytecodeWriter* const src_writer_;
264
265 // TODO(jgruber): We should also replace all of these raw offsets with
266 // OpInfo. That should allow us to not expose the "raw" Emit publicly in the
267 // Writer.
268 // Number of times a jump destination is used within the bytecode.
269 // Key: Jump destination (offset in old bytecode).
270 // Value: Number of times jump destination is used.
271 ZoneMap<int, int> jump_usage_counts_;
272 // Maps offsets in old bytecode to fixups of destinations (delta to new
273 // bytecode).
274 // Key: Offset in old bytecode from where the fixup is valid.
275 // Value: Delta to map jump destinations from old bytecode to new bytecode in
276 // bytes.
277 ZoneMap<int, int> jump_destination_fixups_;
278
279 Zone* const zone_;
280
281 // Points at the first pc in src_writer that has not yet been emitted. Used
282 // for batch copying unchanged regions of the incoming bytecodes.
283 int next_src_pc_to_emit_;
284
285 DISALLOW_IMPLICIT_CONSTRUCTORS(BytecodePeephole)BytecodePeephole() = delete; BytecodePeephole(const BytecodePeephole
&) = delete; BytecodePeephole& operator=(const BytecodePeephole
&) = delete
;
286};
287
288template <typename T>
289T GetValue(const uint8_t* buffer, int pos) {
290 DCHECK(IsAligned(reinterpret_cast<Address>(buffer + pos), alignof(T)))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsAligned(reinterpret_cast<Address>(buffer + pos
), alignof(T)))>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(IsAligned(reinterpret_cast<
Address>(buffer + pos), alignof(T))))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("IsAligned(reinterpret_cast<Address>(buffer + pos), alignof(T))"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 290); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsAligned(reinterpret_cast<Address>(buffer + pos), alignof(T))"
")"); do { MOZ_CrashSequence(__null, 290); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
291 return *reinterpret_cast<const T*>(buffer + pos);
292}
293
294int32_t GetArgumentValue(const uint8_t* bytecode, int offset, int length) {
295 switch (length) {
296 case 1:
297 return GetValue<uint8_t>(bytecode, offset);
298 case 2:
299 return GetValue<int16_t>(bytecode, offset);
300 case 4:
301 return GetValue<int32_t>(bytecode, offset);
302 default:
303 UNREACHABLE()do { do { } while (false); MOZ_ReportCrash("" "unreachable code"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 303); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable code"
")"); do { MOZ_CrashSequence(__null, 303); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
304 }
305}
306
307BytecodeSequenceNode::BytecodeSequenceNode(std::optional<Bytecode> bytecode)
308 : bytecode_(bytecode),
309 bytecode_replacement_(std::nullopt),
310 index_in_sequence_(0),
311 start_offset_(0),
312 parent_(nullptr) {}
313
314BytecodeSequenceNode& BytecodeSequenceNode::FollowedBy(Bytecode bytecode) {
315 sealed_ = true;
316 if (children_.find(bytecode) == children_.end()) {
317 auto new_node = std::make_unique<BytecodeSequenceNode>(bytecode);
318 // If node is not the first in the sequence, set offsets and parent.
319 if (bytecode_.has_value()) {
320 new_node->start_offset_ =
321 start_offset_ + Bytecodes::Size(bytecode_.value());
322 new_node->index_in_sequence_ = index_in_sequence_ + 1;
323 new_node->parent_ = this;
324 }
325 children_[bytecode] = std::move(new_node);
326 }
327
328 BytecodeSequenceNode* node = children_[bytecode].get();
329 // If this fails, the node was previously created as part of another
330 // sequence. We can reuse it, but only if there are no condition for both the
331 // previous and the current use.
332 // TODO(jgruber): We could also reuse the node if non-empty previous and
333 // current conditions are identical, but that's harder to check.
334 // TODO(jgruber): Ideally conditions would become part of the tree (ie nodes
335 // with different conditions are siblings), but this changes runtime behavior
336 // of the peephole scanning algorithm to DFS. Sequence creation would also
337 // need to handle deduplication. All possible, but not trivial.
338 DCHECK(node->argument_check_.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(node->argument_check_.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(node->argument_check_.empty
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("node->argument_check_.empty()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 338); AnnotateMozCrashReason("MOZ_ASSERT" "(" "node->argument_check_.empty()"
")"); do { MOZ_CrashSequence(__null, 338); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
339 return *node;
340}
341
342BytecodeSequenceNode& BytecodeSequenceNode::ReplaceWith(Bytecode bytecode) {
343 DCHECK(!bytecode_replacement_.has_value())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!bytecode_replacement_.has_value())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!bytecode_replacement_.has_value
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!bytecode_replacement_.has_value()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 343); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!bytecode_replacement_.has_value()"
")"); do { MOZ_CrashSequence(__null, 343); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
344 bytecode_replacement_ = bytecode;
345 return *this;
346}
347
348BytecodeSequenceNode& BytecodeSequenceNode::MapArgument(
349 OpInfo to_op_info, int from_bytecode_sequence_index, OpInfo from_op_info) {
350 int src_offset = from_op_info.offset;
351 int src_size = from_op_info.size();
352
353 DCHECK_LE(from_bytecode_sequence_index, index_in_sequence_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((from_bytecode_sequence_index) <= (index_in_sequence_
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((from_bytecode_sequence_index) <= (index_in_sequence_
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(from_bytecode_sequence_index) <= (index_in_sequence_)",
"/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 353); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(from_bytecode_sequence_index) <= (index_in_sequence_)"
")"); do { MOZ_CrashSequence(__null, 353); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
354 DCHECK(BytecodeArgumentMappingCreatedInOrder(to_op_info))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(BytecodeArgumentMappingCreatedInOrder(to_op_info))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(BytecodeArgumentMappingCreatedInOrder(to_op_info))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("BytecodeArgumentMappingCreatedInOrder(to_op_info)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "BytecodeArgumentMappingCreatedInOrder(to_op_info)"
")"); do { MOZ_CrashSequence(__null, 354); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
355
356 BytecodeSequenceNode& ref_node =
357 GetNodeByIndexInSequence(from_bytecode_sequence_index);
358 DCHECK_LT(src_offset, Bytecodes::Size(ref_node.bytecode_.value()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((src_offset) < (Bytecodes::Size(ref_node.bytecode_
.value())))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!((src_offset) < (Bytecodes::Size(
ref_node.bytecode_.value()))))), 0))) { do { } while (false);
MOZ_ReportAssertionFailure("(src_offset) < (Bytecodes::Size(ref_node.bytecode_.value()))"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 358); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(src_offset) < (Bytecodes::Size(ref_node.bytecode_.value()))"
")"); do { MOZ_CrashSequence(__null, 358); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
359
360 int offset_from_start_of_sequence = ref_node.start_offset_ + src_offset;
361 argument_mapping_.push_back(BytecodeArgumentMapping{
362 offset_from_start_of_sequence, src_size, to_op_info});
363 return *this;
364}
365
366BytecodeSequenceNode& BytecodeSequenceNode::EmitOffsetAfterSequence(
367 OpInfo op_info) {
368 DCHECK(BytecodeArgumentMappingCreatedInOrder(op_info))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(BytecodeArgumentMappingCreatedInOrder(op_info))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(BytecodeArgumentMappingCreatedInOrder(op_info)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("BytecodeArgumentMappingCreatedInOrder(op_info)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 368); AnnotateMozCrashReason("MOZ_ASSERT" "(" "BytecodeArgumentMappingCreatedInOrder(op_info)"
")"); do { MOZ_CrashSequence(__null, 368); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
369 argument_mapping_.push_back(BytecodeArgumentMapping{
370 BytecodeArgumentMapping::Type::kOffsetAfterSequence, op_info});
371 return *this;
372}
373
374bool BytecodeSequenceNode::BytecodeArgumentMappingCreatedInOrder(
375 OpInfo op_info) {
376 DCHECK(IsSequence())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsSequence())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsSequence()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("IsSequence()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsSequence()"
")"); do { MOZ_CrashSequence(__null, 376); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
377 if (argument_mapping_.empty()) return true;
378
379 const BytecodeArgumentMapping& m = argument_mapping_.back();
380 int offset_after_last = m.new_offset() + m.new_length();
381 // TODO(jgruber): It'd be more precise to distinguish between special and
382 // basic operand types, but we currently don't expose that information
383 // except through templates.
384 int dst_size = op_info.size();
385 int alignment = std::min(dst_size, kBytecodeAlignment);
386 return RoundUp(offset_after_last, alignment) == op_info.offset;
387}
388
389BytecodeSequenceNode& BytecodeSequenceNode::IfArgumentEqualsOffset(
390 OpInfo op_info, int check_byte_offset) {
391 DCHECK(!sealed_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!sealed_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!sealed_))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!sealed_", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 391); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!sealed_" ")"
); do { MOZ_CrashSequence(__null, 391); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
392 int size = op_info.size();
393 int offset = op_info.offset;
394
395 DCHECK_LT(offset, Bytecodes::Size(bytecode_.value()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((offset) < (Bytecodes::Size(bytecode_.value())))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((offset) < (Bytecodes::Size(bytecode_.value()))))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("(offset) < (Bytecodes::Size(bytecode_.value()))"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 395); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(offset) < (Bytecodes::Size(bytecode_.value()))"
")"); do { MOZ_CrashSequence(__null, 395); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
396 DCHECK(size == 1 || size == 2 || size == 4)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(size == 1 || size == 2 || size == 4)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(size == 1 || size == 2 || size
== 4))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("size == 1 || size == 2 || size == 4", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 396); AnnotateMozCrashReason("MOZ_ASSERT" "(" "size == 1 || size == 2 || size == 4"
")"); do { MOZ_CrashSequence(__null, 396); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
397
398 int offset_from_start_of_sequence = start_offset_ + offset;
399
400 argument_check_.push_back(BytecodeArgumentCheck{offset_from_start_of_sequence,
401 size, check_byte_offset});
402
403 return *this;
404}
405
406BytecodeSequenceNode& BytecodeSequenceNode::IfArgumentEqualsValueAtOffset(
407 OpInfo this_op_info, int other_bytecode_index_in_sequence,
408 OpInfo other_op_info) {
409 DCHECK(!sealed_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!sealed_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!sealed_))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!sealed_", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 409); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!sealed_" ")"
); do { MOZ_CrashSequence(__null, 409); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
410 int size_1 = this_op_info.size();
411 int size_2 = other_op_info.size();
412
413 DCHECK_LT(this_op_info.offset, Bytecodes::Size(bytecode_.value()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((this_op_info.offset) < (Bytecodes::Size(bytecode_
.value())))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!((this_op_info.offset) < (Bytecodes
::Size(bytecode_.value()))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(this_op_info.offset) < (Bytecodes::Size(bytecode_.value()))"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 413); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(this_op_info.offset) < (Bytecodes::Size(bytecode_.value()))"
")"); do { MOZ_CrashSequence(__null, 413); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
414 DCHECK_LE(other_bytecode_index_in_sequence, index_in_sequence_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((other_bytecode_index_in_sequence) <= (index_in_sequence_
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((other_bytecode_index_in_sequence) <= (index_in_sequence_
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(other_bytecode_index_in_sequence) <= (index_in_sequence_)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 414); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(other_bytecode_index_in_sequence) <= (index_in_sequence_)"
")"); do { MOZ_CrashSequence(__null, 414); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
415 DCHECK_EQ(size_1, size_2)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((size_1) == (size_2))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((size_1) == (size_2)))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("(size_1) == (size_2)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 415); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(size_1) == (size_2)"
")"); do { MOZ_CrashSequence(__null, 415); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
416
417 BytecodeSequenceNode& ref_node =
418 GetNodeByIndexInSequence(other_bytecode_index_in_sequence);
419 DCHECK_LT(other_op_info.offset, Bytecodes::Size(ref_node.bytecode_.value()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((other_op_info.offset) < (Bytecodes::Size(ref_node
.bytecode_.value())))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((other_op_info.offset) < (
Bytecodes::Size(ref_node.bytecode_.value()))))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("(other_op_info.offset) < (Bytecodes::Size(ref_node.bytecode_.value()))"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 419); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(other_op_info.offset) < (Bytecodes::Size(ref_node.bytecode_.value()))"
")"); do { MOZ_CrashSequence(__null, 419); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
420
421 int offset_from_start_of_sequence = start_offset_ + this_op_info.offset;
422 int other_offset_from_start_of_sequence =
423 ref_node.start_offset_ + other_op_info.offset;
424
425 argument_check_.push_back(
426 BytecodeArgumentCheck{offset_from_start_of_sequence, size_1,
427 other_offset_from_start_of_sequence, size_2});
428
429 return *this;
430}
431
432BytecodeSequenceNode& BytecodeSequenceNode::IgnoreArgument(
433 int bytecode_index_in_sequence, OpInfo op_info) {
434 int size = op_info.size();
435 int offset = op_info.offset;
436
437 DCHECK(IsSequence())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsSequence())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsSequence()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("IsSequence()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 437); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsSequence()"
")"); do { MOZ_CrashSequence(__null, 437); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
438 DCHECK_LE(bytecode_index_in_sequence, index_in_sequence_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((bytecode_index_in_sequence) <= (index_in_sequence_
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((bytecode_index_in_sequence) <= (index_in_sequence_
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(bytecode_index_in_sequence) <= (index_in_sequence_)", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 438); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(bytecode_index_in_sequence) <= (index_in_sequence_)"
")"); do { MOZ_CrashSequence(__null, 438); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
439
440 BytecodeSequenceNode& ref_node =
441 GetNodeByIndexInSequence(bytecode_index_in_sequence);
442 DCHECK_LT(offset, Bytecodes::Size(ref_node.bytecode_.value()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((offset) < (Bytecodes::Size(ref_node.bytecode_.value
())))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((offset) < (Bytecodes::Size(ref_node.bytecode_.value
()))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(offset) < (Bytecodes::Size(ref_node.bytecode_.value()))"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 442); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(offset) < (Bytecodes::Size(ref_node.bytecode_.value()))"
")"); do { MOZ_CrashSequence(__null, 442); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
443
444 int offset_from_start_of_sequence = ref_node.start_offset_ + offset;
445
446 argument_ignored_.push_back(
447 BytecodeArgument{offset_from_start_of_sequence, size});
448
449 return *this;
450}
451
452bool BytecodeSequenceNode::CheckArguments(const uint8_t* bytecode,
453 int pc) const {
454 bool is_valid = true;
455 for (auto check_iter = argument_check_.begin();
456 check_iter != argument_check_.end() && is_valid; check_iter++) {
457 auto value = GetArgumentValue(bytecode, pc + check_iter->offset(),
458 check_iter->length());
459 if (check_iter->type == BytecodeArgumentCheck::kCheckAddress) {
460 is_valid &= value == pc + check_iter->check_offset;
461 } else if (check_iter->type == BytecodeArgumentCheck::kCheckValue) {
462 auto other_value = GetArgumentValue(
463 bytecode, pc + check_iter->check_offset, check_iter->check_length);
464 is_valid &= value == other_value;
465 } else {
466 UNREACHABLE()do { do { } while (false); MOZ_ReportCrash("" "unreachable code"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 466); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable code"
")"); do { MOZ_CrashSequence(__null, 466); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
467 }
468 }
469 return is_valid;
470}
471
472bool BytecodeSequenceNode::IsSequence() const {
473 return bytecode_replacement_.has_value();
474}
475
476int BytecodeSequenceNode::SequenceLength() const {
477 return start_offset_ + Bytecodes::Size(bytecode_.value());
478}
479
480Bytecode BytecodeSequenceNode::OptimizedBytecode() const {
481 return bytecode_replacement_.value();
482}
483
484BytecodeSequenceNode* BytecodeSequenceNode::Find(Bytecode bytecode) const {
485 auto found = children_.find(bytecode);
486 if (found == children_.end()) return nullptr;
487 return found->second.get();
488}
489
490size_t BytecodeSequenceNode::ArgumentSize() const {
491 DCHECK(IsSequence())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsSequence())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsSequence()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("IsSequence()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsSequence()"
")"); do { MOZ_CrashSequence(__null, 491); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
492 return argument_mapping_.size();
493}
494
495BytecodeArgumentMapping BytecodeSequenceNode::ArgumentMapping(
496 size_t index) const {
497 DCHECK(IsSequence())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsSequence())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsSequence()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("IsSequence()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsSequence()"
")"); do { MOZ_CrashSequence(__null, 497); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
498 DCHECK_LT(index, argument_mapping_.size())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((index) < (argument_mapping_.size()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((index) < (argument_mapping_.size())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("(index) < (argument_mapping_.size())"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 498); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(index) < (argument_mapping_.size())"
")"); do { MOZ_CrashSequence(__null, 498); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
499
500 return argument_mapping_.at(index);
501}
502
503std::vector<BytecodeArgument>::const_iterator
504BytecodeSequenceNode::ArgumentIgnoredBegin() const {
505 DCHECK(IsSequence())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsSequence())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsSequence()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("IsSequence()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsSequence()"
")"); do { MOZ_CrashSequence(__null, 505); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
506 return argument_ignored_.begin();
507}
508
509std::vector<BytecodeArgument>::const_iterator
510BytecodeSequenceNode::ArgumentIgnoredEnd() const {
511 DCHECK(IsSequence())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsSequence())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsSequence()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("IsSequence()", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsSequence()"
")"); do { MOZ_CrashSequence(__null, 511); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
512 return argument_ignored_.end();
513}
514
515bool BytecodeSequenceNode::HasIgnoredArguments() const {
516 return !argument_ignored_.empty();
517}
518
519BytecodeSequenceNode& BytecodeSequenceNode::GetNodeByIndexInSequence(
520 int index_in_sequence) {
521 DCHECK_LE(index_in_sequence, index_in_sequence_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((index_in_sequence) <= (index_in_sequence_))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((index_in_sequence) <= (index_in_sequence_)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("(index_in_sequence) <= (index_in_sequence_)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 521); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(index_in_sequence) <= (index_in_sequence_)"
")"); do { MOZ_CrashSequence(__null, 521); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
522
523 if (index_in_sequence < index_in_sequence_) {
524 DCHECK(parent_ != nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(parent_ != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(parent_ != nullptr))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("parent_ != nullptr"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 524); AnnotateMozCrashReason("MOZ_ASSERT" "(" "parent_ != nullptr"
")"); do { MOZ_CrashSequence(__null, 524); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
525 return parent_->GetNodeByIndexInSequence(index_in_sequence);
526 } else {
527 return *this;
528 }
529}
530
531BytecodePeepholeSequences::BytecodePeepholeSequences()
532 : sequences_(std::nullopt) {
533 DefineStandardSequences();
534}
535
536BytecodeSequenceNode& BytecodePeepholeSequences::CreateSequence(
537 Bytecode bytecode) {
538 return sequences_.FollowedBy(bytecode);
539}
540
541BytecodePeephole::BytecodePeephole(Zone* zone, const BytecodeWriter* src_writer,
542 BytecodeWriter* dst_writer)
543 : dst_writer_(dst_writer),
544 src_writer_(src_writer),
545 jump_usage_counts_(zone),
546 jump_destination_fixups_(zone),
547 zone_(zone),
548 next_src_pc_to_emit_(0) {
549 dst_writer_->buffer().reserve(src_writer_->length());
550 // Prepare jump usage counts.
551 for (auto jump_edge : src_writer_->jump_edges()) {
552 int jump_destination = jump_edge.second;
553 jump_usage_counts_[jump_destination]++;
554 }
555 // Sentinel fixups at beginning of bytecode (position -1) so we don't have to
556 // check for end of iterator inside the fixup loop.
557 // In general fixups are deltas of original offsets of jump
558 // sources/destinations (in the old bytecode) to find them in the new
559 // bytecode. All jump targets are fixed after the new bytecode is fully
560 // emitted in the internal buffer.
561 jump_destination_fixups_.emplace(-1, 0);
562 // Sentinel fixups at end of (old) bytecode so we don't have to check for
563 // end of iterator inside the fixup loop.
564 DCHECK_LE(src_writer_->length(), std::numeric_limits<int>::max())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((src_writer_->length()) <= (std::numeric_limits
<int>::max()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((src_writer_->length()) <=
(std::numeric_limits<int>::max())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("(src_writer_->length()) <= (std::numeric_limits<int>::max())"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 564); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(src_writer_->length()) <= (std::numeric_limits<int>::max())"
")"); do { MOZ_CrashSequence(__null, 564); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
565 jump_destination_fixups_.emplace(static_cast<int>(src_writer_->length()), 0);
566}
567
568void BytecodePeepholeSequences::DefineStandardSequences() {
569 using B = Bytecode;
570#define I(BYTECODE, OPERAND) \
571 OpInfo::Get<BytecodeOperands<BYTECODE>, \
572 BytecodeOperands<BYTECODE>::Operand::OPERAND>()
573#define T(OPERAND) I(Target, OPERAND)
574
575 // Commonly used sequences can be found by creating regexp bytecode traces
576 // (--trace-regexp-bytecodes) and using v8/tools/regexp-sequences.py.
577
578 {
579 static constexpr auto Target = B::kSkipUntilBitInTable;
580 CreateSequence(B::kLoadCurrentCharacter)
581 .FollowedBy(B::kCheckBitInTable)
582 .FollowedBy(B::kAdvanceCpAndGoto)
583 .IfArgumentEqualsOffset(I(B::kAdvanceCpAndGoto, on_goto), 0)
584 .ReplaceWith(Target)
585 .MapArgument(T(cp_offset), 0, I(B::kLoadCurrentCharacter, cp_offset))
586 .MapArgument(T(advance_by), 2, I(B::kAdvanceCpAndGoto, by))
587 .MapArgument(T(table), 1, I(B::kCheckBitInTable, table))
588 .MapArgument(T(bounds_check_offset), 0,
589 I(B::kLoadCurrentCharacter, bounds_check_offset))
590 .MapArgument(T(on_match), 1, I(B::kCheckBitInTable, on_bit_set))
591 .MapArgument(T(on_no_match), 0, I(B::kLoadCurrentCharacter, on_failure))
592 .IgnoreArgument(2, I(B::kAdvanceCpAndGoto, on_goto));
593 }
594
595 {
596 static constexpr auto Target = B::kSkipUntilCharAnd;
597 CreateSequence(B::kLoadCurrentCharacter)
598 .FollowedBy(B::kCheckCharacterAfterAnd)
599 .FollowedBy(B::kAdvanceCpAndGoto)
600 .IfArgumentEqualsOffset(I(B::kAdvanceCpAndGoto, on_goto), 0)
601 .ReplaceWith(Target)
602 .MapArgument(T(cp_offset), 0, I(B::kLoadCurrentCharacter, cp_offset))
603 .MapArgument(T(advance_by), 2, I(B::kAdvanceCpAndGoto, by))
604 .MapArgument(T(character), 1, I(B::kCheckCharacterAfterAnd, character))
605 .MapArgument(T(mask), 1, I(B::kCheckCharacterAfterAnd, mask))
606 .MapArgument(T(bounds_check_offset), 0,
607 I(B::kLoadCurrentCharacter, bounds_check_offset))
608 .MapArgument(T(on_match), 1, I(B::kCheckCharacterAfterAnd, on_equal))
609 .MapArgument(T(on_no_match), 0, I(B::kLoadCurrentCharacter, on_failure))
610 .IgnoreArgument(2, I(B::kAdvanceCpAndGoto, on_goto));
611 }
612
613 // TODO(pthier): It might make sense for short sequences like this one to only
614 // optimize them if the resulting optimization is not longer than the current
615 // one. This could be the case if there are jumps inside the sequence and we
616 // have to replicate parts of the sequence. A method to mark such sequences
617 // might be useful.
618 {
619 static constexpr auto Target = B::kSkipUntilChar;
620 CreateSequence(B::kLoadCurrentCharacter)
621 .FollowedBy(B::kCheckCharacter)
622 .FollowedBy(B::kAdvanceCpAndGoto)
623 .IfArgumentEqualsOffset(I(B::kAdvanceCpAndGoto, on_goto), 0)
624 .ReplaceWith(Target)
625 .MapArgument(T(cp_offset), 0, I(B::kLoadCurrentCharacter, cp_offset))
626 .MapArgument(T(advance_by), 2, I(B::kAdvanceCpAndGoto, by))
627 .MapArgument(T(character), 1, I(B::kCheckCharacter, character))
628 .MapArgument(T(bounds_check_offset), 0,
629 I(B::kLoadCurrentCharacter, bounds_check_offset))
630 .MapArgument(T(on_match), 1, I(B::kCheckCharacter, on_equal))
631 .MapArgument(T(on_no_match), 0, I(B::kLoadCurrentCharacter, on_failure))
632 .IgnoreArgument(2, I(B::kAdvanceCpAndGoto, on_goto));
633 }
634
635 {
636 static constexpr auto Target = B::kSkipUntilCharOrChar;
637 CreateSequence(B::kLoadCurrentCharacter)
638 .FollowedBy(B::kCheckCharacter)
639 .FollowedBy(B::kCheckCharacter)
640 .IfArgumentEqualsValueAtOffset(I(B::kCheckCharacter, on_equal), 1,
641 I(B::kCheckCharacter, on_equal))
642 .FollowedBy(B::kAdvanceCpAndGoto)
643 .IfArgumentEqualsOffset(I(B::kAdvanceCpAndGoto, on_goto), 0)
644 .ReplaceWith(Target)
645 .MapArgument(T(cp_offset), 0, I(B::kLoadCurrentCharacter, cp_offset))
646 .MapArgument(T(advance_by), 3, I(B::kAdvanceCpAndGoto, by))
647 .MapArgument(T(char1), 1, I(B::kCheckCharacter, character))
648 .MapArgument(T(char2), 2, I(B::kCheckCharacter, character))
649 .MapArgument(T(bounds_check_offset), 0,
650 I(B::kLoadCurrentCharacter, bounds_check_offset))
651 .MapArgument(T(on_match), 1, I(B::kCheckCharacter, on_equal))
652 .MapArgument(T(on_no_match), 0, I(B::kLoadCurrentCharacter, on_failure))
653 .IgnoreArgument(2, I(B::kCheckCharacter, on_equal))
654 .IgnoreArgument(3, I(B::kAdvanceCpAndGoto, on_goto));
655 }
656
657 {
658 static constexpr auto Target = B::kSkipUntilGtOrNotBitInTable;
659 CreateSequence(B::kLoadCurrentCharacter)
660 .FollowedBy(B::kCheckCharacterGT)
661 // Sequence is only valid if the jump target of kCheckCharacterGT is the
662 // first bytecode AFTER the whole sequence.
663 .IfArgumentEqualsOffset(I(B::kCheckCharacterGT, on_greater), 60)
664 .FollowedBy(B::kCheckBitInTable)
665 // Sequence is only valid if the jump target of kCheckBitInTable is
666 // the kAdvanceCpAndGoto bytecode at the end of the sequence.
667 .IfArgumentEqualsOffset(I(B::kCheckBitInTable, on_bit_set), 52)
668 .FollowedBy(B::kGoTo)
669 // Sequence is only valid if the jump target of kGoTo is the same as the
670 // jump target of kCheckCharacterGT (i.e. both jump to the first
671 // bytecode AFTER the whole sequence.
672 .IfArgumentEqualsValueAtOffset(I(B::kGoTo, label), 1,
673 I(B::kCheckCharacterGT, on_greater))
674 .FollowedBy(B::kAdvanceCpAndGoto)
675 .IfArgumentEqualsOffset(I(B::kAdvanceCpAndGoto, on_goto), 0)
676 .ReplaceWith(Target)
677 .MapArgument(T(cp_offset), 0, I(B::kLoadCurrentCharacter, cp_offset))
678 .MapArgument(T(advance_by), 4, I(B::kAdvanceCpAndGoto, by))
679 .MapArgument(T(character), 1, I(B::kCheckCharacterGT, limit))
680 .MapArgument(T(table), 2, I(B::kCheckBitInTable, table))
681 .MapArgument(T(bounds_check_offset), 0,
682 I(B::kLoadCurrentCharacter, bounds_check_offset))
683 .MapArgument(T(on_match), 1, I(B::kCheckCharacterGT, on_greater))
684 .MapArgument(T(on_no_match), 0, I(B::kLoadCurrentCharacter, on_failure))
685 .IgnoreArgument(2, I(B::kCheckBitInTable, on_bit_set))
686 .IgnoreArgument(3, I(B::kGoTo, label))
687 .IgnoreArgument(4, I(B::kAdvanceCpAndGoto, on_goto));
688 }
689 {
690 static constexpr auto Target = B::kSkipUntilOneOfMasked;
691 CreateSequence(B::kLoad4CurrentChars)
692 .FollowedBy(B::kAndCheck4Chars)
693 // Jump target is the offset of the next AndCheck4Chars (right after
694 // AdvanceCpAndGoto).
695 .IfArgumentEqualsOffset(I(B::kAndCheck4Chars, on_equal), 0x24)
696 .FollowedBy(B::kAdvanceCpAndGoto)
697 .IfArgumentEqualsOffset(I(B::kAdvanceCpAndGoto, on_goto), 0)
698 .FollowedBy(B::kAndCheck4Chars)
699 .FollowedBy(B::kAndCheckNot4Chars)
700 // Jump target is AdvanceCpAndGoto.
701 .IfArgumentEqualsOffset(I(B::kAndCheckNot4Chars, on_not_equal), 0x1c)
702 .ReplaceWith(Target)
703 .MapArgument(T(cp_offset), 0, I(B::kLoad4CurrentChars, cp_offset))
704 .MapArgument(T(advance_by), 2, I(B::kAdvanceCpAndGoto, by))
705 .MapArgument(T(both_chars), 1, I(B::kAndCheck4Chars, characters))
706 .MapArgument(T(both_mask), 1, I(B::kAndCheck4Chars, mask))
707 .MapArgument(T(max_offset), 0,
708 I(B::kLoad4CurrentChars, bounds_check_offset))
709 .MapArgument(T(chars1), 3, I(B::kAndCheck4Chars, characters))
710 .MapArgument(T(mask1), 3, I(B::kAndCheck4Chars, mask))
711 .MapArgument(T(chars2), 4, I(B::kAndCheckNot4Chars, characters))
712 .MapArgument(T(mask2), 4, I(B::kAndCheckNot4Chars, mask))
713 .MapArgument(T(on_match1), 3, I(B::kAndCheck4Chars, on_equal))
714 .EmitOffsetAfterSequence(T(on_match2))
715 .MapArgument(T(on_failure), 0, I(B::kLoad4CurrentChars, on_failure))
716 .IgnoreArgument(2, I(B::kAdvanceCpAndGoto, on_goto))
717 .IgnoreArgument(1, I(B::kAndCheck4Chars, on_equal));
718 }
719 // The original bytecode sequence for kSkipUntilOneOfMasked3 is:
720 //
721 // sequence offset name
722 // bc0 0 SkipUntilBitInTable
723 // bc1 24 Load4CurrentChars
724 // bc2 30 AndCheck4Chars
725 // bc3 40 AdvanceCpAndGoto
726 // bc4 48 Load4CurrentChars
727 // bc5 54 AndCheck4Chars
728 // bc6 64 AndCheck4Chars
729 // bc7 74 AndCheckNot4Chars
730 {
731 static constexpr int kOffsetOfBc0SkipUntilBitInTable = 0x0;
732 static constexpr int kOffsetOfBc1Load4CurrentChars = 0x24;
733 static constexpr int kOffsetOfBc3AdvanceCpAndGoto = 0x40;
734 static constexpr auto Target = B::kSkipUntilOneOfMasked3;
735 BytecodeSequenceNode& s0 =
736 CreateSequence(B::kSkipUntilBitInTable)
737 .IfArgumentEqualsOffset(I(B::kSkipUntilBitInTable, on_match),
738 kOffsetOfBc1Load4CurrentChars)
739 .IfArgumentEqualsOffset(I(B::kSkipUntilBitInTable, on_no_match),
740 kOffsetOfBc1Load4CurrentChars);
741
742 DCHECK_EQ(s0.SequenceLength(), kOffsetOfBc1Load4CurrentChars)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((s0.SequenceLength()) == (kOffsetOfBc1Load4CurrentChars
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((s0.SequenceLength()) == (kOffsetOfBc1Load4CurrentChars
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(s0.SequenceLength()) == (kOffsetOfBc1Load4CurrentChars)", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 742); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(s0.SequenceLength()) == (kOffsetOfBc1Load4CurrentChars)"
")"); do { MOZ_CrashSequence(__null, 742); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
743 static constexpr int kOffsetOfBc4Load4CurrentChars = 0x48;
744 BytecodeSequenceNode& s1 =
745 s0.FollowedBy(B::kLoad4CurrentChars)
746 .FollowedBy(B::kAndCheck4Chars)
747 .IfArgumentEqualsOffset(I(B::kAndCheck4Chars, on_equal),
748 kOffsetOfBc4Load4CurrentChars);
749
750 DCHECK_EQ(s1.SequenceLength(), kOffsetOfBc3AdvanceCpAndGoto)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((s1.SequenceLength()) == (kOffsetOfBc3AdvanceCpAndGoto
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((s1.SequenceLength()) == (kOffsetOfBc3AdvanceCpAndGoto
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(s1.SequenceLength()) == (kOffsetOfBc3AdvanceCpAndGoto)", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 750); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(s1.SequenceLength()) == (kOffsetOfBc3AdvanceCpAndGoto)"
")"); do { MOZ_CrashSequence(__null, 750); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
751 BytecodeSequenceNode& s2 =
752 s1.FollowedBy(B::kAdvanceCpAndGoto)
753 .IfArgumentEqualsOffset(I(B::kAdvanceCpAndGoto, on_goto),
754 kOffsetOfBc0SkipUntilBitInTable);
755
756 DCHECK_EQ(s2.SequenceLength(), kOffsetOfBc4Load4CurrentChars)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((s2.SequenceLength()) == (kOffsetOfBc4Load4CurrentChars
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((s2.SequenceLength()) == (kOffsetOfBc4Load4CurrentChars
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(s2.SequenceLength()) == (kOffsetOfBc4Load4CurrentChars)", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 756); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(s2.SequenceLength()) == (kOffsetOfBc4Load4CurrentChars)"
")"); do { MOZ_CrashSequence(__null, 756); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
757 BytecodeSequenceNode& s3 =
758 s2.FollowedBy(B::kLoad4CurrentChars)
759 .IfArgumentEqualsOffset(I(B::kLoad4CurrentChars, on_failure),
760 kOffsetOfBc3AdvanceCpAndGoto)
761 .FollowedBy(B::kAndCheck4Chars)
762 .FollowedBy(B::kAndCheck4Chars)
763 .FollowedBy(B::kAndCheckNot4Chars)
764 .IfArgumentEqualsOffset(I(B::kAndCheckNot4Chars, on_not_equal),
765 kOffsetOfBc3AdvanceCpAndGoto);
766
767 s3.ReplaceWith(Target)
768 .MapArgument(T(bc0_cp_offset), 0, I(B::kSkipUntilBitInTable, cp_offset))
769 .MapArgument(T(bc0_advance_by), 0,
770 I(B::kSkipUntilBitInTable, advance_by))
771 .MapArgument(T(bc0_table), 0, I(B::kSkipUntilBitInTable, table))
772 .IgnoreArgument(0, I(B::kSkipUntilBitInTable, on_match))
773 .IgnoreArgument(0, I(B::kSkipUntilBitInTable, on_no_match))
774 .IgnoreArgument(0, I(B::kSkipUntilBitInTable, bounds_check_offset))
775 .MapArgument(T(bc1_bounds_check_offset), 1,
776 I(B::kLoad4CurrentChars, bounds_check_offset))
777 .MapArgument(T(bc1_on_failure), 1, I(B::kLoad4CurrentChars, on_failure))
778 .MapArgument(T(bc1_cp_offset), 1, I(B::kLoad4CurrentChars, cp_offset))
779 .MapArgument(T(bc2_characters), 2, I(B::kAndCheck4Chars, characters))
780 .MapArgument(T(bc2_mask), 2, I(B::kAndCheck4Chars, mask))
781 .IgnoreArgument(2, I(B::kAndCheck4Chars, on_equal))
782 .MapArgument(T(bc3_by), 3, I(B::kAdvanceCpAndGoto, by))
783 .IgnoreArgument(3, I(B::kAdvanceCpAndGoto, on_goto))
784 .MapArgument(T(bc4_bounds_check_offset), 4,
785 I(B::kLoad4CurrentChars, bounds_check_offset))
786 .MapArgument(T(bc4_cp_offset), 4, I(B::kLoad4CurrentChars, cp_offset))
787 .IgnoreArgument(4, I(B::kLoad4CurrentChars, on_failure))
788 .MapArgument(T(bc5_characters), 5, I(B::kAndCheck4Chars, characters))
789 .MapArgument(T(bc5_mask), 5, I(B::kAndCheck4Chars, mask))
790 .MapArgument(T(bc5_on_equal), 5, I(B::kAndCheck4Chars, on_equal))
791 .MapArgument(T(bc6_characters), 6, I(B::kAndCheck4Chars, characters))
792 .MapArgument(T(bc6_mask), 6, I(B::kAndCheck4Chars, mask))
793 .MapArgument(T(bc6_on_equal), 6, I(B::kAndCheck4Chars, on_equal))
794 .MapArgument(T(bc7_characters), 7, I(B::kAndCheckNot4Chars, characters))
795 .MapArgument(T(bc7_mask), 7, I(B::kAndCheckNot4Chars, mask))
796 .IgnoreArgument(7, I(B::kAndCheckNot4Chars, on_not_equal))
797 .EmitOffsetAfterSequence(T(fallthrough_jump_target));
798 }
799
800#undef I
801#undef T
802}
803bool BytecodePeephole::OptimizeBytecode(Zone* zone,
804 const BytecodeWriter* src_writer,
805 BytecodeWriter* dst_writer) {
806 BytecodePeephole p(zone, src_writer, dst_writer);
807
808 const uint8_t* bytecode = src_writer->buffer().data();
809 // TODO(375937549): Convert length to uint32_t.
810 int length = static_cast<int>(src_writer->length());
811
812 int old_pc = 0;
813 bool did_optimize = false;
814
815 while (old_pc < length) {
816 int replaced_len = p.TryOptimizeSequence(bytecode, length, old_pc);
817 if (replaced_len > 0) {
818 old_pc += replaced_len;
819 did_optimize = true;
820 } else {
821 int bc_len = Bytecodes::Size(bytecode[old_pc]);
822 old_pc += bc_len;
823 }
824 }
825
826 if (did_optimize) {
827 // If we optimized anything, we must flush the remaining unoptimized bytes.
828 // If we didn't optimize anything, we leave the dst_writer empty and the
829 // caller will continue using src_writer (effectively a no-op pass).
830 if (old_pc > p.next_src_pc_to_emit_) {
831 dst_writer->EmitRawBytecodeStream(src_writer, p.next_src_pc_to_emit_,
832 old_pc - p.next_src_pc_to_emit_);
833 }
834 p.FixJumps();
835 }
836
837 return did_optimize;
838}
839
840DEFINE_LAZY_LEAKY_OBJECT_GETTER(BytecodePeepholeSequences, GetStandardSequences)BytecodePeepholeSequences* GetStandardSequences() { static ::
v8::base::LeakyObject<BytecodePeepholeSequences> object
{}; return object.get(); }
841
842int BytecodePeephole::TryOptimizeSequence(const uint8_t* bytecode,
843 int bytecode_length, int start_pc) {
844 const BytecodeSequenceNode* seq_node = GetStandardSequences()->sequences();
845 const BytecodeSequenceNode* valid_seq_end = nullptr;
846
847 int current_pc = start_pc;
848
849 // Check for the longest valid sequence matching any of the pre-defined
850 // sequences in the Trie data structure.
851 while (current_pc < bytecode_length) {
852 seq_node = seq_node->Find(Bytecodes::FromByte(bytecode[current_pc]));
853 if (seq_node == nullptr) break;
854 if (!seq_node->CheckArguments(bytecode, start_pc)) break;
855
856 if (seq_node->IsSequence()) valid_seq_end = seq_node;
857 current_pc += Bytecodes::Size(bytecode[current_pc]);
858 }
859
860 if (valid_seq_end) {
861 EmitOptimization(start_pc, bytecode, *valid_seq_end);
862 return valid_seq_end->SequenceLength();
863 }
864
865 return 0;
866}
867
868void BytecodePeephole::EmitOptimization(int start_pc, const uint8_t* bytecode,
869 const BytecodeSequenceNode& last_node) {
870 // Flush any sequence of bytecodes which we haven't emitted yet.
871 if (start_pc > next_src_pc_to_emit_) {
872 dst_writer_->EmitRawBytecodeStream(src_writer_, next_src_pc_to_emit_,
873 start_pc - next_src_pc_to_emit_);
874 }
875 const int sequence_length = last_node.SequenceLength();
876 next_src_pc_to_emit_ = start_pc + sequence_length;
877
878 // Update usage counts for all jumps originating in the sequence we are about
879 // to replace. Counts for the target locations are decremented here. Emitting
880 // kJumpTarget below will again increment for newly emitted destinations.
881 auto edge_it = src_writer_->jump_edges().lower_bound(start_pc);
882 while (edge_it != src_writer_->jump_edges().end() &&
883 edge_it->first < start_pc + sequence_length) {
884 int target = edge_it->second;
885 auto count_it = jump_usage_counts_.find(target);
886 DCHECK_NE(count_it, jump_usage_counts_.end())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((count_it) != (jump_usage_counts_.end()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((count_it) != (jump_usage_counts_.end())))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("(count_it) != (jump_usage_counts_.end())"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 886); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(count_it) != (jump_usage_counts_.end())"
")"); do { MOZ_CrashSequence(__null, 886); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
887 count_it->second--;
888 edge_it++;
889 }
890
891 int optimized_start_pc = pc();
892 // List of offsets in the optimized sequence that need to be patched to the
893 // offset value right after the optimized sequence.
894 ZoneLinkedList<uint32_t> after_sequence_offsets(zone());
895
896 const Bytecode bc = last_node.OptimizedBytecode();
897 dst_writer_->EmitBytecode(bc);
898
899 for (size_t arg_idx = 0; arg_idx < last_node.ArgumentSize(); arg_idx++) {
900 BytecodeArgumentMapping arg_map = last_node.ArgumentMapping(arg_idx);
901 if (arg_map.type() == BytecodeArgumentMapping::Type::kDefault) {
902 if (arg_map.new_operand_type() == BytecodeOperandType::kJumpTarget) {
903 int target = GetArgumentValue(bytecode, start_pc + arg_map.offset(),
904 arg_map.length());
905 jump_usage_counts_[target]++;
906 }
907 EmitArgument(start_pc, bytecode, arg_map);
908 } else {
909 DCHECK_EQ(arg_map.type(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype((arg_map.type()) == (BytecodeArgumentMapping::Type::
kOffsetAfterSequence))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((arg_map.type()) == (BytecodeArgumentMapping
::Type::kOffsetAfterSequence)))), 0))) { do { } while (false)
; MOZ_ReportAssertionFailure("(arg_map.type()) == (BytecodeArgumentMapping::Type::kOffsetAfterSequence)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(arg_map.type()) == (BytecodeArgumentMapping::Type::kOffsetAfterSequence)"
")"); do { MOZ_CrashSequence(__null, 910); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
910 BytecodeArgumentMapping::Type::kOffsetAfterSequence)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((arg_map.type()) == (BytecodeArgumentMapping::Type::
kOffsetAfterSequence))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((arg_map.type()) == (BytecodeArgumentMapping
::Type::kOffsetAfterSequence)))), 0))) { do { } while (false)
; MOZ_ReportAssertionFailure("(arg_map.type()) == (BytecodeArgumentMapping::Type::kOffsetAfterSequence)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(arg_map.type()) == (BytecodeArgumentMapping::Type::kOffsetAfterSequence)"
")"); do { MOZ_CrashSequence(__null, 910); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
911 after_sequence_offsets.push_back(optimized_start_pc +
912 arg_map.new_offset());
913 // Reserve space to overwrite later with the pc after this sequence.
914 dst_writer_->Emit<uint32_t>(0, arg_map.new_offset());
915 }
916 }
917
918 // Final alignment.
919 dst_writer_->Finalize(bc);
920 DCHECK_EQ(pc(), optimized_start_pc + Bytecodes::Size(bc))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((pc()) == (optimized_start_pc + Bytecodes::Size(bc))
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((pc()) == (optimized_start_pc + Bytecodes::Size(bc))
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"(pc()) == (optimized_start_pc + Bytecodes::Size(bc))", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 920); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(pc()) == (optimized_start_pc + Bytecodes::Size(bc))"
")"); do { MOZ_CrashSequence(__null, 920); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
921
922 int fixup_length = Bytecodes::Size(bc) - sequence_length;
923
924 // Check if there are any jumps inside the old sequence.
925 // If so we have to keep the bytecodes that are jumped to around.
926 auto jump_destination_candidate = jump_usage_counts_.upper_bound(start_pc);
927 int jump_candidate_destination = jump_destination_candidate->first;
928 int jump_candidate_count = jump_destination_candidate->second;
929 // Jump destinations only jumped to from inside the sequence will be ignored.
930 while (jump_destination_candidate != jump_usage_counts_.end() &&
931 jump_candidate_count == 0) {
932 ++jump_destination_candidate;
933 jump_candidate_destination = jump_destination_candidate->first;
934 jump_candidate_count = jump_destination_candidate->second;
935 }
936
937 int preserve_from = start_pc + sequence_length;
Value stored to 'preserve_from' during its initialization is never read
938 if (jump_destination_candidate != jump_usage_counts_.end() &&
939 jump_candidate_destination < start_pc + sequence_length) {
940 preserve_from = jump_candidate_destination;
941 // Check if any jump in the sequence we are preserving has a jump
942 // destination inside the optimized sequence before the current position we
943 // want to preserve. If so we have to preserve all bytecodes starting at
944 // this jump destination.
945 for (auto jump_iter = src_writer_->jump_edges().lower_bound(preserve_from);
946 jump_iter != src_writer_->jump_edges().end() &&
947 jump_iter->first /* jump source */ < start_pc + sequence_length;
948 ++jump_iter) {
949 int jump_destination = jump_iter->second;
950 if (jump_destination > start_pc && jump_destination < preserve_from) {
951 preserve_from = jump_destination;
952 }
953 }
954
955 // We preserve everything to the end of the sequence. This is conservative
956 // since it would be enough to preserve all bytecodes up to an unconditional
957 // jump.
958 int preserve_length = start_pc + sequence_length - preserve_from;
959 fixup_length += preserve_length;
960 // All jump targets after the start of the optimized sequence need to be
961 // fixed relative to the length of the optimized sequence including
962 // bytecodes we preserved.
963 AddJumpDestinationFixup(fixup_length, start_pc + 1);
964 // Jumps to the sequence we preserved need absolute fixup as they could
965 // occur before or after the sequence.
966 SetJumpDestinationFixup(pc() - preserve_from, preserve_from);
967 dst_writer_->EmitRawBytecodeStream(src_writer_, preserve_from,
968 preserve_length);
969 } else {
970 AddJumpDestinationFixup(fixup_length, start_pc + 1);
971 }
972
973 for (uint32_t offset : after_sequence_offsets) {
974 DCHECK_EQ(dst_writer_->buffer()[offset], 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((dst_writer_->buffer()[offset]) == (0))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!((dst_writer_->buffer()[offset]) == (0)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("(dst_writer_->buffer()[offset]) == (0)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 974); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(dst_writer_->buffer()[offset]) == (0)"
")"); do { MOZ_CrashSequence(__null, 974); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
975 dst_writer_->OverwriteValue<uint32_t>(pc(), offset);
976 // Register the offset in jump_edges_ so that subsequent peephole passes
977 // adjust it when bytecodes shift.
978 dst_writer_->jump_edges().emplace(offset, start_pc + sequence_length);
979 }
980}
981
982void BytecodePeephole::AddJumpDestinationFixup(int fixup, int pos) {
983 auto previous_fixup = jump_destination_fixups_.lower_bound(pos);
984 DCHECK(previous_fixup != jump_destination_fixups_.end())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(previous_fixup != jump_destination_fixups_.end())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(previous_fixup != jump_destination_fixups_.end()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("previous_fixup != jump_destination_fixups_.end()"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "previous_fixup != jump_destination_fixups_.end()"
")"); do { MOZ_CrashSequence(__null, 984); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
985 DCHECK(previous_fixup != jump_destination_fixups_.begin())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(previous_fixup != jump_destination_fixups_.begin())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(previous_fixup != jump_destination_fixups_.begin()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("previous_fixup != jump_destination_fixups_.begin()"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 985); AnnotateMozCrashReason("MOZ_ASSERT" "(" "previous_fixup != jump_destination_fixups_.begin()"
")"); do { MOZ_CrashSequence(__null, 985); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
986
987 int previous_fixup_value = (--previous_fixup)->second;
988 jump_destination_fixups_[pos] = previous_fixup_value + fixup;
989}
990
991void BytecodePeephole::SetJumpDestinationFixup(int fixup, int pos) {
992 auto previous_fixup = jump_destination_fixups_.lower_bound(pos);
993 DCHECK(previous_fixup != jump_destination_fixups_.end())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(previous_fixup != jump_destination_fixups_.end())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(previous_fixup != jump_destination_fixups_.end()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("previous_fixup != jump_destination_fixups_.end()"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "previous_fixup != jump_destination_fixups_.end()"
")"); do { MOZ_CrashSequence(__null, 993); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
994 DCHECK(previous_fixup != jump_destination_fixups_.begin())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(previous_fixup != jump_destination_fixups_.begin())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(previous_fixup != jump_destination_fixups_.begin()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("previous_fixup != jump_destination_fixups_.begin()"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "previous_fixup != jump_destination_fixups_.begin()"
")"); do { MOZ_CrashSequence(__null, 994); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
995
996 int previous_fixup_value = (--previous_fixup)->second;
997 jump_destination_fixups_.emplace(pos, fixup);
998 jump_destination_fixups_.emplace(pos + 1, previous_fixup_value);
999}
1000
1001void BytecodePeephole::FixJumps() {
1002 for (auto jump_edge : dst_writer_->jump_edges()) {
1003 int jump_source = jump_edge.first;
1004 int jump_destination = jump_edge.second;
1005 int fixed_jump_destination =
1006 jump_destination +
1007 (--jump_destination_fixups_.upper_bound(jump_destination))->second;
1008 DCHECK_LT(fixed_jump_destination, pc())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((fixed_jump_destination) < (pc()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((fixed_jump_destination) <
(pc())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(fixed_jump_destination) < (pc())", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 1008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(fixed_jump_destination) < (pc())"
")"); do { MOZ_CrashSequence(__null, 1008); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1009 // TODO(pthier): This check could be better if we track the bytecodes
1010 // actually used and check if we jump to one of them.
1011 DCHECK(Bytecodes::IsValidJumpTarget(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Bytecodes::IsValidJumpTarget( dst_writer_->buffer
()[fixed_jump_destination]))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(Bytecodes::IsValidJumpTarget
( dst_writer_->buffer()[fixed_jump_destination])))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("Bytecodes::IsValidJumpTarget( dst_writer_->buffer()[fixed_jump_destination])"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 1012); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Bytecodes::IsValidJumpTarget( dst_writer_->buffer()[fixed_jump_destination])"
")"); do { MOZ_CrashSequence(__null, 1012); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1012 dst_writer_->buffer()[fixed_jump_destination]))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Bytecodes::IsValidJumpTarget( dst_writer_->buffer
()[fixed_jump_destination]))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(Bytecodes::IsValidJumpTarget
( dst_writer_->buffer()[fixed_jump_destination])))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("Bytecodes::IsValidJumpTarget( dst_writer_->buffer()[fixed_jump_destination])"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 1012); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Bytecodes::IsValidJumpTarget( dst_writer_->buffer()[fixed_jump_destination])"
")"); do { MOZ_CrashSequence(__null, 1012); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1013
1014 if (jump_destination != fixed_jump_destination) {
1015 dst_writer_->PatchJump(fixed_jump_destination, jump_source);
1016 }
1017 }
1018}
1019
1020void BytecodePeephole::EmitArgument(int start_pc, const uint8_t* bytecode,
1021 BytecodeArgumentMapping arg) {
1022 const BytecodeOperandType type = arg.new_operand_type();
1023
1024 switch (type) {
1025#define CASE(Name, ...) \
1026 case BytecodeOperandType::k##Name: { \
1027 DCHECK_LE(arg.length(), kInt32Size)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((arg.length()) <= (kInt32Size))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((arg.length()) <= (kInt32Size
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("(arg.length()) <= (kInt32Size)", "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 1027); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(arg.length()) <= (kInt32Size)"
")"); do { MOZ_CrashSequence(__null, 1027); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
; \
1028 using CType = OperandTypeTraits<BytecodeOperandType::k##Name>::kCType; \
1029 CType value = static_cast<CType>( \
1030 GetArgumentValue(bytecode, start_pc + arg.offset(), arg.length())); \
1031 dst_writer_->EmitOperand<BytecodeOperandType::k##Name>(value, \
1032 arg.new_offset()); \
1033 } break;
1034 BASIC_BYTECODE_OPERAND_TYPE_LIST(CASE)CASE(Int16, int16_t) CASE(Int32, int32_t) CASE(Uint32, uint32_t
) CASE(Char, base::uc16) CASE(JumpTarget, uint32_t)
1035 BASIC_BYTECODE_OPERAND_TYPE_LIMITS_LIST(CASE)CASE(Offset, int16_t, RegExpMacroAssembler::kMinCPOffset, RegExpMacroAssembler
::kMaxCPOffset) CASE(BoundsCheckOffset, int32_t, RegExpMacroAssembler
::kMinCPOffset, RegExpMacroAssembler::kMaxCPOffset + RegExpMacroAssembler
::kMaxEatsAtLeastValue) CASE(Register, uint16_t, 0, RegExpMacroAssembler
::kMaxRegister) CASE(StackCheckFlag, RegExpMacroAssembler::StackCheckFlag
, RegExpMacroAssembler::StackCheckFlag::kNoStackLimitCheck, RegExpMacroAssembler
::StackCheckFlag::kCheckStackLimit) CASE(StandardCharacterSet
, StandardCharacterSet, StandardCharacterSet::kEverything, StandardCharacterSet
::kWord)
1036#undef CASE
1037 case BytecodeOperandType::kBitTable: {
1038 DCHECK_EQ(arg.length(), 16)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((arg.length()) == (16))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((arg.length()) == (16)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("(arg.length()) == (16)"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 1038); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(arg.length()) == (16)"
")"); do { MOZ_CrashSequence(__null, 1038); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1039 dst_writer_->EmitOperand<BytecodeOperandType::kBitTable>(
1040 bytecode + start_pc + arg.offset(), arg.new_offset());
1041 } break;
1042 default:
1043 UNREACHABLE()do { do { } while (false); MOZ_ReportCrash("" "unreachable code"
, "/root/firefox-clang/js/src/irregexp/imported/regexp-bytecode-peephole.cc"
, 1043); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable code"
")"); do { MOZ_CrashSequence(__null, 1043); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1044 }
1045}
1046
1047int BytecodePeephole::pc() const { return dst_writer_->pc(); }
1048
1049Zone* BytecodePeephole::zone() const { return zone_; }
1050
1051} // namespace
1052
1053// static
1054DirectHandle<TrustedByteArray> BytecodePeepholeOptimization::OptimizeBytecode(
1055 Isolate* isolate, Zone* zone, DirectHandle<RegExpData> re_data,
1056 BytecodeWriter* src_writer) {
1057 BytecodeWriter dst_writer(zone);
1058
1059 // Preserve the original bytecode for tracing if needed.
1060 std::optional<ZoneVector<uint8_t>> original_bytecode;
1061 if (v8_flagsjs::jit::JitOptions.trace_regexp_peephole_optimization) {
1062 const ZoneVector<uint8_t>& src_buffer = src_writer->buffer();
1063 const auto begin = src_buffer.begin();
1064 original_bytecode.emplace(begin, begin + src_writer->length(), zone);
1065 }
1066
1067 const bool did_optimize =
1068 BytecodePeephole::OptimizeBytecode(zone, src_writer, &dst_writer);
1069 // The result is in dst_writer iff a peephole rule fired; otherwise the
1070 // unchanged input bytecode is still in src_writer.
1071 BytecodeWriter* result = did_optimize ? &dst_writer : src_writer;
1072 const uint8_t* optimized_bytecode = result->buffer().data();
1073 uint32_t optimized_length = result->length();
1074
1075 DirectHandle<TrustedByteArray> array =
1076 isolate->factory()->NewTrustedByteArray(optimized_length);
1077 MemCopymemcpy(array->begin(), optimized_bytecode, optimized_length);
1078
1079 if (did_optimize && v8_flagsjs::jit::JitOptions.trace_regexp_peephole_optimization) {
1080 std::unique_ptr<char[]> pattern_cstring =
1081 re_data->escaped_source()->ToCString();
1082 PrintF("Original Bytecode:\n");
1083 RegExpBytecodeDisassemble(original_bytecode->data(),
1084 static_cast<uint32_t>(original_bytecode->size()),
1085 pattern_cstring.get());
1086 PrintF("Optimized Bytecode:\n");
1087 RegExpBytecodeDisassemble(array->begin(), optimized_length,
1088 pattern_cstring.get());
1089 }
1090
1091 return array;
1092}
1093
1094} // namespace regexp
1095} // namespace internal
1096} // namespace v8