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 "irregexp/imported/regexp-bytecodes.h"
8 
9 namespace v8 {
10 namespace internal {
11 
12 namespace {
13 
14 struct BytecodeArgument {
15   int offset;
16   int length;
17 
BytecodeArgumentv8::internal::__anon18e917590111::BytecodeArgument18   BytecodeArgument(int offset, int length) : offset(offset), length(length) {}
19 };
20 
21 struct BytecodeArgumentMapping : BytecodeArgument {
22   int new_length;
23 
BytecodeArgumentMappingv8::internal::__anon18e917590111::BytecodeArgumentMapping24   BytecodeArgumentMapping(int offset, int length, int new_length)
25       : BytecodeArgument(offset, length), new_length(new_length) {}
26 };
27 
28 struct BytecodeArgumentCheck : BytecodeArgument {
29   enum CheckType { kCheckAddress = 0, kCheckValue };
30   CheckType type;
31   int check_offset;
32   int check_length;
33 
BytecodeArgumentCheckv8::internal::__anon18e917590111::BytecodeArgumentCheck34   BytecodeArgumentCheck(int offset, int length, int check_offset)
35       : BytecodeArgument(offset, length),
36         type(kCheckAddress),
37         check_offset(check_offset) {}
BytecodeArgumentCheckv8::internal::__anon18e917590111::BytecodeArgumentCheck38   BytecodeArgumentCheck(int offset, int length, int check_offset,
39                         int check_length)
40       : BytecodeArgument(offset, length),
41         type(kCheckValue),
42         check_offset(check_offset),
43         check_length(check_length) {}
44 };
45 
46 // Trie-Node for storing bytecode sequences we want to optimize.
47 class BytecodeSequenceNode {
48  public:
49   // Dummy bytecode used when we need to store/return a bytecode but it's not a
50   // valid bytecode in the current context.
51   static constexpr int kDummyBytecode = -1;
52 
53   BytecodeSequenceNode(int bytecode, Zone* zone);
54   // Adds a new node as child of the current node if it isn't a child already.
55   BytecodeSequenceNode& FollowedBy(int bytecode);
56   // Marks the end of a sequence and sets optimized bytecode to replace all
57   // bytecodes of the sequence with.
58   BytecodeSequenceNode& ReplaceWith(int bytecode);
59   // Maps arguments of bytecodes in the sequence to the optimized bytecode.
60   // Order of invocation determines order of arguments in the optimized
61   // bytecode.
62   // Invoking this method is only allowed on nodes that mark the end of a valid
63   // sequence (i.e. after ReplaceWith()).
64   // bytecode_index_in_sequence: Zero-based index of the referred bytecode
65   // within the sequence (e.g. the bytecode passed to CreateSequence() has
66   // index 0).
67   // argument_offset: Zero-based offset to the argument within the bytecode
68   // (e.g. the first argument that's not packed with the bytecode has offset 4).
69   // argument_byte_length: Length of the argument.
70   // new_argument_byte_length: Length of the argument in the new bytecode
71   // (= argument_byte_length if omitted).
72   BytecodeSequenceNode& MapArgument(int bytecode_index_in_sequence,
73                                     int argument_offset,
74                                     int argument_byte_length,
75                                     int new_argument_byte_length = 0);
76   // Adds a check to the sequence node making it only a valid sequence when the
77   // argument of the current bytecode at the specified offset matches the offset
78   // to check against.
79   // argument_offset: Zero-based offset to the argument within the bytecode
80   // (e.g. the first argument that's not packed with the bytecode has offset 4).
81   // argument_byte_length: Length of the argument.
82   // check_byte_offset: Zero-based offset relative to the beginning of the
83   // sequence that needs to match the value given by argument_offset. (e.g.
84   // check_byte_offset 0 matches the address of the first bytecode in the
85   // sequence).
86   BytecodeSequenceNode& IfArgumentEqualsOffset(int argument_offset,
87                                                int argument_byte_length,
88                                                int check_byte_offset);
89   // Adds a check to the sequence node making it only a valid sequence when the
90   // argument of the current bytecode at the specified offset matches the
91   // argument of another bytecode in the sequence.
92   // This is similar to IfArgumentEqualsOffset, except that this method matches
93   // the values of both arguments.
94   BytecodeSequenceNode& IfArgumentEqualsValueAtOffset(
95       int argument_offset, int argument_byte_length,
96       int other_bytecode_index_in_sequence, int other_argument_offset,
97       int other_argument_byte_length);
98   // Marks an argument as unused.
99   // All arguments that are not mapped explicitly have to be marked as unused.
100   // bytecode_index_in_sequence: Zero-based index of the referred bytecode
101   // within the sequence (e.g. the bytecode passed to CreateSequence() has
102   // index 0).
103   // argument_offset: Zero-based offset to the argument within the bytecode
104   // (e.g. the first argument that's not packed with the bytecode has offset 4).
105   // argument_byte_length: Length of the argument.
106   BytecodeSequenceNode& IgnoreArgument(int bytecode_index_in_sequence,
107                                        int argument_offset,
108                                        int argument_byte_length);
109   // Checks if the current node is valid for the sequence. I.e. all conditions
110   // set by IfArgumentEqualsOffset and IfArgumentEquals are fulfilled by this
111   // node for the actual bytecode sequence.
112   bool CheckArguments(const byte* bytecode, int pc);
113   // Returns whether this node marks the end of a valid sequence (i.e. can be
114   // replaced with an optimized bytecode).
115   bool IsSequence() const;
116   // Returns the length of the sequence in bytes.
117   int SequenceLength() const;
118   // Returns the optimized bytecode for the node or kDummyBytecode if it is not
119   // the end of a valid sequence.
120   int OptimizedBytecode() const;
121   // Returns the child of the current node matching the given bytecode or
122   // nullptr if no such child is found.
123   BytecodeSequenceNode* Find(int bytecode) const;
124   // Returns number of arguments mapped to the current node.
125   // Invoking this method is only allowed on nodes that mark the end of a valid
126   // sequence (i.e. if IsSequence())
127   size_t ArgumentSize() const;
128   // Returns the argument-mapping of the argument at index.
129   // Invoking this method is only allowed on nodes that mark the end of a valid
130   // sequence (i.e. if IsSequence())
131   BytecodeArgumentMapping ArgumentMapping(size_t index) const;
132   // Returns an iterator to begin of ignored arguments.
133   // Invoking this method is only allowed on nodes that mark the end of a valid
134   // sequence (i.e. if IsSequence())
135   ZoneLinkedList<BytecodeArgument>::iterator ArgumentIgnoredBegin() const;
136   // Returns an iterator to end of ignored arguments.
137   // Invoking this method is only allowed on nodes that mark the end of a valid
138   // sequence (i.e. if IsSequence())
139   ZoneLinkedList<BytecodeArgument>::iterator ArgumentIgnoredEnd() const;
140   // Returns whether the current node has ignored argument or not.
141   bool HasIgnoredArguments() const;
142 
143  private:
144   // Returns a node in the sequence specified by its index within the sequence.
145   BytecodeSequenceNode& GetNodeByIndexInSequence(int index_in_sequence);
146   Zone* zone() const;
147 
148   int bytecode_;
149   int bytecode_replacement_;
150   int index_in_sequence_;
151   int start_offset_;
152   BytecodeSequenceNode* parent_;
153   ZoneUnorderedMap<int, BytecodeSequenceNode*> children_;
154   ZoneVector<BytecodeArgumentMapping>* argument_mapping_;
155   ZoneLinkedList<BytecodeArgumentCheck>* argument_check_;
156   ZoneLinkedList<BytecodeArgument>* argument_ignored_;
157 
158   Zone* zone_;
159 };
160 
161 // These definitions are here in order to please the linker, which in debug mode
162 // sometimes requires static constants to be defined in .cc files.
163 constexpr int BytecodeSequenceNode::kDummyBytecode;
164 
165 class RegExpBytecodePeephole {
166  public:
167   RegExpBytecodePeephole(Zone* zone, size_t buffer_size,
168                          const ZoneUnorderedMap<int, int>& jump_edges);
169 
170   // Parses bytecode and fills the internal buffer with the potentially
171   // optimized bytecode. Returns true when optimizations were performed, false
172   // otherwise.
173   bool OptimizeBytecode(const byte* bytecode, int length);
174   // Copies the internal bytecode buffer to another buffer. The caller is
175   // responsible for allocating/freeing the memory.
176   void CopyOptimizedBytecode(byte* to_address) const;
177   int Length() const;
178 
179  private:
180   // Sets up all sequences that are going to be used.
181   void DefineStandardSequences();
182   // Starts a new bytecode sequence.
183   BytecodeSequenceNode& CreateSequence(int bytecode);
184   // Checks for optimization candidates at pc and emits optimized bytecode to
185   // the internal buffer. Returns the length of replaced bytecodes in bytes.
186   int TryOptimizeSequence(const byte* bytecode, int bytecode_length,
187                           int start_pc);
188   // Emits optimized bytecode to the internal buffer. start_pc points to the
189   // start of the sequence in bytecode and last_node is the last
190   // BytecodeSequenceNode of the matching sequence found.
191   void EmitOptimization(int start_pc, const byte* bytecode,
192                         const BytecodeSequenceNode& last_node);
193   // Adds a relative jump source fixup at pos.
194   // Jump source fixups are used to find offsets in the new bytecode that
195   // contain jump sources.
196   void AddJumpSourceFixup(int fixup, int pos);
197   // Adds a relative jump destination fixup at pos.
198   // Jump destination fixups are used to find offsets in the new bytecode that
199   // can be jumped to.
200   void AddJumpDestinationFixup(int fixup, int pos);
201   // Sets an absolute jump destination fixup at pos.
202   void SetJumpDestinationFixup(int fixup, int pos);
203   // Prepare internal structures used to fixup jumps.
204   void PrepareJumpStructures(const ZoneUnorderedMap<int, int>& jump_edges);
205   // Updates all jump targets in the new bytecode.
206   void FixJumps();
207   // Update a single jump.
208   void FixJump(int jump_source, int jump_destination);
209   void AddSentinelFixups(int pos);
210   template <typename T>
211   void EmitValue(T value);
212   template <typename T>
213   void OverwriteValue(int offset, T value);
214   void CopyRangeToOutput(const byte* orig_bytecode, int start, int length);
215   void SetRange(byte value, int count);
216   void EmitArgument(int start_pc, const byte* bytecode,
217                     BytecodeArgumentMapping arg);
218   int pc() const;
219   Zone* zone() const;
220 
221   ZoneVector<byte> optimized_bytecode_buffer_;
222   BytecodeSequenceNode* sequences_;
223   // Jumps used in old bytecode.
224   // Key: Jump source (offset where destination is stored in old bytecode)
225   // Value: Destination
226   ZoneMap<int, int> jump_edges_;
227   // Jumps used in new bytecode.
228   // Key: Jump source (offset where destination is stored in new bytecode)
229   // Value: Destination
230   ZoneMap<int, int> jump_edges_mapped_;
231   // Number of times a jump destination is used within the bytecode.
232   // Key: Jump destination (offset in old bytecode).
233   // Value: Number of times jump destination is used.
234   ZoneMap<int, int> jump_usage_counts_;
235   // Maps offsets in old bytecode to fixups of sources (delta to new bytecode).
236   // Key: Offset in old bytecode from where the fixup is valid.
237   // Value: Delta to map jump source from old bytecode to new bytecode in bytes.
238   ZoneMap<int, int> jump_source_fixups_;
239   // Maps offsets in old bytecode to fixups of destinations (delta to new
240   // bytecode).
241   // Key: Offset in old bytecode from where the fixup is valid.
242   // Value: Delta to map jump destinations from old bytecode to new bytecode in
243   // bytes.
244   ZoneMap<int, int> jump_destination_fixups_;
245 
246   Zone* zone_;
247 
248   DISALLOW_IMPLICIT_CONSTRUCTORS(RegExpBytecodePeephole);
249 };
250 
251 template <typename T>
GetValue(const byte * buffer,int pos)252 T GetValue(const byte* buffer, int pos) {
253   DCHECK(IsAligned(reinterpret_cast<Address>(buffer + pos), alignof(T)));
254   return *reinterpret_cast<const T*>(buffer + pos);
255 }
256 
GetArgumentValue(const byte * bytecode,int offset,int length)257 int32_t GetArgumentValue(const byte* bytecode, int offset, int length) {
258   switch (length) {
259     case 1:
260       return GetValue<byte>(bytecode, offset);
261       break;
262     case 2:
263       return GetValue<int16_t>(bytecode, offset);
264       break;
265     case 4:
266       return GetValue<int32_t>(bytecode, offset);
267       break;
268     default:
269       UNREACHABLE();
270   }
271 }
272 
BytecodeSequenceNode(int bytecode,Zone * zone)273 BytecodeSequenceNode::BytecodeSequenceNode(int bytecode, Zone* zone)
274     : bytecode_(bytecode),
275       bytecode_replacement_(kDummyBytecode),
276       index_in_sequence_(0),
277       start_offset_(0),
278       parent_(nullptr),
279       children_(ZoneUnorderedMap<int, BytecodeSequenceNode*>(zone)),
280       argument_mapping_(zone->New<ZoneVector<BytecodeArgumentMapping>>(zone)),
281       argument_check_(zone->New<ZoneLinkedList<BytecodeArgumentCheck>>(zone)),
282       argument_ignored_(zone->New<ZoneLinkedList<BytecodeArgument>>(zone)),
283       zone_(zone) {}
284 
FollowedBy(int bytecode)285 BytecodeSequenceNode& BytecodeSequenceNode::FollowedBy(int bytecode) {
286   DCHECK(0 <= bytecode && bytecode < kRegExpBytecodeCount);
287 
288   if (children_.find(bytecode) == children_.end()) {
289     BytecodeSequenceNode* new_node =
290         zone()->New<BytecodeSequenceNode>(bytecode, zone());
291     // If node is not the first in the sequence, set offsets and parent.
292     if (bytecode_ != kDummyBytecode) {
293       new_node->start_offset_ = start_offset_ + RegExpBytecodeLength(bytecode_);
294       new_node->index_in_sequence_ = index_in_sequence_ + 1;
295       new_node->parent_ = this;
296     }
297     children_[bytecode] = new_node;
298   }
299 
300   return *children_[bytecode];
301 }
302 
ReplaceWith(int bytecode)303 BytecodeSequenceNode& BytecodeSequenceNode::ReplaceWith(int bytecode) {
304   DCHECK(0 <= bytecode && bytecode < kRegExpBytecodeCount);
305 
306   bytecode_replacement_ = bytecode;
307 
308   return *this;
309 }
310 
MapArgument(int bytecode_index_in_sequence,int argument_offset,int argument_byte_length,int new_argument_byte_length)311 BytecodeSequenceNode& BytecodeSequenceNode::MapArgument(
312     int bytecode_index_in_sequence, int argument_offset,
313     int argument_byte_length, int new_argument_byte_length) {
314   DCHECK(IsSequence());
315   DCHECK_LE(bytecode_index_in_sequence, index_in_sequence_);
316 
317   BytecodeSequenceNode& ref_node =
318       GetNodeByIndexInSequence(bytecode_index_in_sequence);
319   DCHECK_LT(argument_offset, RegExpBytecodeLength(ref_node.bytecode_));
320 
321   int absolute_offset = ref_node.start_offset_ + argument_offset;
322   if (new_argument_byte_length == 0) {
323     new_argument_byte_length = argument_byte_length;
324   }
325 
326   argument_mapping_->push_back(BytecodeArgumentMapping{
327       absolute_offset, argument_byte_length, new_argument_byte_length});
328 
329   return *this;
330 }
331 
IfArgumentEqualsOffset(int argument_offset,int argument_byte_length,int check_byte_offset)332 BytecodeSequenceNode& BytecodeSequenceNode::IfArgumentEqualsOffset(
333     int argument_offset, int argument_byte_length, int check_byte_offset) {
334   DCHECK_LT(argument_offset, RegExpBytecodeLength(bytecode_));
335   DCHECK(argument_byte_length == 1 || argument_byte_length == 2 ||
336          argument_byte_length == 4);
337 
338   int absolute_offset = start_offset_ + argument_offset;
339 
340   argument_check_->push_back(BytecodeArgumentCheck{
341       absolute_offset, argument_byte_length, check_byte_offset});
342 
343   return *this;
344 }
345 
IfArgumentEqualsValueAtOffset(int argument_offset,int argument_byte_length,int other_bytecode_index_in_sequence,int other_argument_offset,int other_argument_byte_length)346 BytecodeSequenceNode& BytecodeSequenceNode::IfArgumentEqualsValueAtOffset(
347     int argument_offset, int argument_byte_length,
348     int other_bytecode_index_in_sequence, int other_argument_offset,
349     int other_argument_byte_length) {
350   DCHECK_LT(argument_offset, RegExpBytecodeLength(bytecode_));
351   DCHECK_LE(other_bytecode_index_in_sequence, index_in_sequence_);
352   DCHECK_EQ(argument_byte_length, other_argument_byte_length);
353 
354   BytecodeSequenceNode& ref_node =
355       GetNodeByIndexInSequence(other_bytecode_index_in_sequence);
356   DCHECK_LT(other_argument_offset, RegExpBytecodeLength(ref_node.bytecode_));
357 
358   int absolute_offset = start_offset_ + argument_offset;
359   int other_absolute_offset = ref_node.start_offset_ + other_argument_offset;
360 
361   argument_check_->push_back(
362       BytecodeArgumentCheck{absolute_offset, argument_byte_length,
363                             other_absolute_offset, other_argument_byte_length});
364 
365   return *this;
366 }
367 
IgnoreArgument(int bytecode_index_in_sequence,int argument_offset,int argument_byte_length)368 BytecodeSequenceNode& BytecodeSequenceNode::IgnoreArgument(
369     int bytecode_index_in_sequence, int argument_offset,
370     int argument_byte_length) {
371   DCHECK(IsSequence());
372   DCHECK_LE(bytecode_index_in_sequence, index_in_sequence_);
373 
374   BytecodeSequenceNode& ref_node =
375       GetNodeByIndexInSequence(bytecode_index_in_sequence);
376   DCHECK_LT(argument_offset, RegExpBytecodeLength(ref_node.bytecode_));
377 
378   int absolute_offset = ref_node.start_offset_ + argument_offset;
379 
380   argument_ignored_->push_back(
381       BytecodeArgument{absolute_offset, argument_byte_length});
382 
383   return *this;
384 }
385 
CheckArguments(const byte * bytecode,int pc)386 bool BytecodeSequenceNode::CheckArguments(const byte* bytecode, int pc) {
387   bool is_valid = true;
388   for (auto check_iter = argument_check_->begin();
389        check_iter != argument_check_->end() && is_valid; check_iter++) {
390     auto value =
391         GetArgumentValue(bytecode, pc + check_iter->offset, check_iter->length);
392     if (check_iter->type == BytecodeArgumentCheck::kCheckAddress) {
393       is_valid &= value == pc + check_iter->check_offset;
394     } else if (check_iter->type == BytecodeArgumentCheck::kCheckValue) {
395       auto other_value = GetArgumentValue(
396           bytecode, pc + check_iter->check_offset, check_iter->check_length);
397       is_valid &= value == other_value;
398     } else {
399       UNREACHABLE();
400     }
401   }
402   return is_valid;
403 }
404 
IsSequence() const405 bool BytecodeSequenceNode::IsSequence() const {
406   return bytecode_replacement_ != kDummyBytecode;
407 }
408 
SequenceLength() const409 int BytecodeSequenceNode::SequenceLength() const {
410   return start_offset_ + RegExpBytecodeLength(bytecode_);
411 }
412 
OptimizedBytecode() const413 int BytecodeSequenceNode::OptimizedBytecode() const {
414   return bytecode_replacement_;
415 }
416 
Find(int bytecode) const417 BytecodeSequenceNode* BytecodeSequenceNode::Find(int bytecode) const {
418   auto found = children_.find(bytecode);
419   if (found == children_.end()) return nullptr;
420   return found->second;
421 }
422 
ArgumentSize() const423 size_t BytecodeSequenceNode::ArgumentSize() const {
424   DCHECK(IsSequence());
425   return argument_mapping_->size();
426 }
427 
ArgumentMapping(size_t index) const428 BytecodeArgumentMapping BytecodeSequenceNode::ArgumentMapping(
429     size_t index) const {
430   DCHECK(IsSequence());
431   DCHECK(argument_mapping_ != nullptr);
432   DCHECK_LT(index, argument_mapping_->size());
433 
434   return argument_mapping_->at(index);
435 }
436 
437 ZoneLinkedList<BytecodeArgument>::iterator
ArgumentIgnoredBegin() const438 BytecodeSequenceNode::ArgumentIgnoredBegin() const {
439   DCHECK(IsSequence());
440   DCHECK(argument_ignored_ != nullptr);
441   return argument_ignored_->begin();
442 }
443 
444 ZoneLinkedList<BytecodeArgument>::iterator
ArgumentIgnoredEnd() const445 BytecodeSequenceNode::ArgumentIgnoredEnd() const {
446   DCHECK(IsSequence());
447   DCHECK(argument_ignored_ != nullptr);
448   return argument_ignored_->end();
449 }
450 
HasIgnoredArguments() const451 bool BytecodeSequenceNode::HasIgnoredArguments() const {
452   return argument_ignored_ != nullptr;
453 }
454 
GetNodeByIndexInSequence(int index_in_sequence)455 BytecodeSequenceNode& BytecodeSequenceNode::GetNodeByIndexInSequence(
456     int index_in_sequence) {
457   DCHECK_LE(index_in_sequence, index_in_sequence_);
458 
459   if (index_in_sequence < index_in_sequence_) {
460     DCHECK(parent_ != nullptr);
461     return parent_->GetNodeByIndexInSequence(index_in_sequence);
462   } else {
463     return *this;
464   }
465 }
466 
zone() const467 Zone* BytecodeSequenceNode::zone() const { return zone_; }
468 
RegExpBytecodePeephole(Zone * zone,size_t buffer_size,const ZoneUnorderedMap<int,int> & jump_edges)469 RegExpBytecodePeephole::RegExpBytecodePeephole(
470     Zone* zone, size_t buffer_size,
471     const ZoneUnorderedMap<int, int>& jump_edges)
472     : optimized_bytecode_buffer_(zone),
473       sequences_(zone->New<BytecodeSequenceNode>(
474           BytecodeSequenceNode::kDummyBytecode, zone)),
475       jump_edges_(zone),
476       jump_edges_mapped_(zone),
477       jump_usage_counts_(zone),
478       jump_source_fixups_(zone),
479       jump_destination_fixups_(zone),
480       zone_(zone) {
481   optimized_bytecode_buffer_.reserve(buffer_size);
482   PrepareJumpStructures(jump_edges);
483   DefineStandardSequences();
484   // Sentinel fixups at beginning of bytecode (position -1) so we don't have to
485   // check for end of iterator inside the fixup loop.
486   // In general fixups are deltas of original offsets of jump
487   // sources/destinations (in the old bytecode) to find them in the new
488   // bytecode. All jump targets are fixed after the new bytecode is fully
489   // emitted in the internal buffer.
490   AddSentinelFixups(-1);
491   // Sentinel fixups at end of (old) bytecode so we don't have to check for
492   // end of iterator inside the fixup loop.
493   DCHECK_LE(buffer_size, std::numeric_limits<int>::max());
494   AddSentinelFixups(static_cast<int>(buffer_size));
495 }
496 
DefineStandardSequences()497 void RegExpBytecodePeephole::DefineStandardSequences() {
498   // Commonly used sequences can be found by creating regexp bytecode traces
499   // (--trace-regexp-bytecodes) and using v8/tools/regexp-sequences.py.
500   CreateSequence(BC_LOAD_CURRENT_CHAR)
501       .FollowedBy(BC_CHECK_BIT_IN_TABLE)
502       .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
503       // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the
504       // first bytecode in this sequence.
505       .IfArgumentEqualsOffset(4, 4, 0)
506       .ReplaceWith(BC_SKIP_UNTIL_BIT_IN_TABLE)
507       .MapArgument(0, 1, 3)      // load offset
508       .MapArgument(2, 1, 3, 4)   // advance by
509       .MapArgument(1, 8, 16)     // bit table
510       .MapArgument(1, 4, 4)      // goto when match
511       .MapArgument(0, 4, 4)      // goto on failure
512       .IgnoreArgument(2, 4, 4);  // loop jump
513 
514   CreateSequence(BC_CHECK_CURRENT_POSITION)
515       .FollowedBy(BC_LOAD_CURRENT_CHAR_UNCHECKED)
516       .FollowedBy(BC_CHECK_CHAR)
517       .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
518       // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the
519       // first bytecode in this sequence.
520       .IfArgumentEqualsOffset(4, 4, 0)
521       .ReplaceWith(BC_SKIP_UNTIL_CHAR_POS_CHECKED)
522       .MapArgument(1, 1, 3)      // load offset
523       .MapArgument(3, 1, 3, 2)   // advance_by
524       .MapArgument(2, 1, 3, 2)   // c
525       .MapArgument(0, 1, 3, 4)   // eats at least
526       .MapArgument(2, 4, 4)      // goto when match
527       .MapArgument(0, 4, 4)      // goto on failure
528       .IgnoreArgument(3, 4, 4);  // loop jump
529 
530   CreateSequence(BC_CHECK_CURRENT_POSITION)
531       .FollowedBy(BC_LOAD_CURRENT_CHAR_UNCHECKED)
532       .FollowedBy(BC_AND_CHECK_CHAR)
533       .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
534       // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the
535       // first bytecode in this sequence.
536       .IfArgumentEqualsOffset(4, 4, 0)
537       .ReplaceWith(BC_SKIP_UNTIL_CHAR_AND)
538       .MapArgument(1, 1, 3)      // load offset
539       .MapArgument(3, 1, 3, 2)   // advance_by
540       .MapArgument(2, 1, 3, 2)   // c
541       .MapArgument(2, 4, 4)      // mask
542       .MapArgument(0, 1, 3, 4)   // eats at least
543       .MapArgument(2, 8, 4)      // goto when match
544       .MapArgument(0, 4, 4)      // goto on failure
545       .IgnoreArgument(3, 4, 4);  // loop jump
546 
547   // TODO(pthier): It might make sense for short sequences like this one to only
548   // optimize them if the resulting optimization is not longer than the current
549   // one. This could be the case if there are jumps inside the sequence and we
550   // have to replicate parts of the sequence. A method to mark such sequences
551   // might be useful.
552   CreateSequence(BC_LOAD_CURRENT_CHAR)
553       .FollowedBy(BC_CHECK_CHAR)
554       .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
555       // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the
556       // first bytecode in this sequence.
557       .IfArgumentEqualsOffset(4, 4, 0)
558       .ReplaceWith(BC_SKIP_UNTIL_CHAR)
559       .MapArgument(0, 1, 3)      // load offset
560       .MapArgument(2, 1, 3, 2)   // advance by
561       .MapArgument(1, 1, 3, 2)   // character
562       .MapArgument(1, 4, 4)      // goto when match
563       .MapArgument(0, 4, 4)      // goto on failure
564       .IgnoreArgument(2, 4, 4);  // loop jump
565 
566   CreateSequence(BC_LOAD_CURRENT_CHAR)
567       .FollowedBy(BC_CHECK_CHAR)
568       .FollowedBy(BC_CHECK_CHAR)
569       // Sequence is only valid if the jump targets of both CHECK_CHAR bytecodes
570       // are equal.
571       .IfArgumentEqualsValueAtOffset(4, 4, 1, 4, 4)
572       .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
573       // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the
574       // first bytecode in this sequence.
575       .IfArgumentEqualsOffset(4, 4, 0)
576       .ReplaceWith(BC_SKIP_UNTIL_CHAR_OR_CHAR)
577       .MapArgument(0, 1, 3)      // load offset
578       .MapArgument(3, 1, 3, 4)   // advance by
579       .MapArgument(1, 1, 3, 2)   // character 1
580       .MapArgument(2, 1, 3, 2)   // character 2
581       .MapArgument(1, 4, 4)      // goto when match
582       .MapArgument(0, 4, 4)      // goto on failure
583       .IgnoreArgument(2, 4, 4)   // goto when match 2
584       .IgnoreArgument(3, 4, 4);  // loop jump
585 
586   CreateSequence(BC_LOAD_CURRENT_CHAR)
587       .FollowedBy(BC_CHECK_GT)
588       // Sequence is only valid if the jump target of CHECK_GT is the first
589       // bytecode AFTER the whole sequence.
590       .IfArgumentEqualsOffset(4, 4, 56)
591       .FollowedBy(BC_CHECK_BIT_IN_TABLE)
592       // Sequence is only valid if the jump target of CHECK_BIT_IN_TABLE is
593       // the ADVANCE_CP_AND_GOTO bytecode at the end of the sequence.
594       .IfArgumentEqualsOffset(4, 4, 48)
595       .FollowedBy(BC_GOTO)
596       // Sequence is only valid if the jump target of GOTO is the same as the
597       // jump target of CHECK_GT (i.e. both jump to the first bytecode AFTER the
598       // whole sequence.
599       .IfArgumentEqualsValueAtOffset(4, 4, 1, 4, 4)
600       .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
601       // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the
602       // first bytecode in this sequence.
603       .IfArgumentEqualsOffset(4, 4, 0)
604       .ReplaceWith(BC_SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE)
605       .MapArgument(0, 1, 3)      // load offset
606       .MapArgument(4, 1, 3, 2)   // advance by
607       .MapArgument(1, 1, 3, 2)   // character
608       .MapArgument(2, 8, 16)     // bit table
609       .MapArgument(1, 4, 4)      // goto when match
610       .MapArgument(0, 4, 4)      // goto on failure
611       .IgnoreArgument(2, 4, 4)   // indirect loop jump
612       .IgnoreArgument(3, 4, 4)   // jump out of loop
613       .IgnoreArgument(4, 4, 4);  // loop jump
614 }
615 
OptimizeBytecode(const byte * bytecode,int length)616 bool RegExpBytecodePeephole::OptimizeBytecode(const byte* bytecode,
617                                               int length) {
618   int old_pc = 0;
619   bool did_optimize = false;
620 
621   while (old_pc < length) {
622     int replaced_len = TryOptimizeSequence(bytecode, length, old_pc);
623     if (replaced_len > 0) {
624       old_pc += replaced_len;
625       did_optimize = true;
626     } else {
627       int bc = bytecode[old_pc];
628       int bc_len = RegExpBytecodeLength(bc);
629       CopyRangeToOutput(bytecode, old_pc, bc_len);
630       old_pc += bc_len;
631     }
632   }
633 
634   if (did_optimize) {
635     FixJumps();
636   }
637 
638   return did_optimize;
639 }
640 
CopyOptimizedBytecode(byte * to_address) const641 void RegExpBytecodePeephole::CopyOptimizedBytecode(byte* to_address) const {
642   MemCopy(to_address, &(*optimized_bytecode_buffer_.begin()), Length());
643 }
644 
Length() const645 int RegExpBytecodePeephole::Length() const { return pc(); }
646 
CreateSequence(int bytecode)647 BytecodeSequenceNode& RegExpBytecodePeephole::CreateSequence(int bytecode) {
648   DCHECK(sequences_ != nullptr);
649   DCHECK(0 <= bytecode && bytecode < kRegExpBytecodeCount);
650 
651   return sequences_->FollowedBy(bytecode);
652 }
653 
TryOptimizeSequence(const byte * bytecode,int bytecode_length,int start_pc)654 int RegExpBytecodePeephole::TryOptimizeSequence(const byte* bytecode,
655                                                 int bytecode_length,
656                                                 int start_pc) {
657   BytecodeSequenceNode* seq_node = sequences_;
658   BytecodeSequenceNode* valid_seq_end = nullptr;
659 
660   int current_pc = start_pc;
661 
662   // Check for the longest valid sequence matching any of the pre-defined
663   // sequences in the Trie data structure.
664   while (current_pc < bytecode_length) {
665     seq_node = seq_node->Find(bytecode[current_pc]);
666     if (seq_node == nullptr) break;
667     if (!seq_node->CheckArguments(bytecode, start_pc)) break;
668 
669     if (seq_node->IsSequence()) valid_seq_end = seq_node;
670     current_pc += RegExpBytecodeLength(bytecode[current_pc]);
671   }
672 
673   if (valid_seq_end) {
674     EmitOptimization(start_pc, bytecode, *valid_seq_end);
675     return valid_seq_end->SequenceLength();
676   }
677 
678   return 0;
679 }
680 
EmitOptimization(int start_pc,const byte * bytecode,const BytecodeSequenceNode & last_node)681 void RegExpBytecodePeephole::EmitOptimization(
682     int start_pc, const byte* bytecode, const BytecodeSequenceNode& last_node) {
683 #ifdef DEBUG
684   int optimized_start_pc = pc();
685 #endif
686   // Jump sources that are mapped or marked as unused will be deleted at the end
687   // of this method. We don't delete them immediately as we might need the
688   // information when we have to preserve bytecodes at the end.
689   // TODO(pthier): Replace with a stack-allocated data structure.
690   ZoneLinkedList<int> delete_jumps = ZoneLinkedList<int>(zone());
691 
692   uint32_t bc = last_node.OptimizedBytecode();
693   EmitValue(bc);
694 
695   for (size_t arg = 0; arg < last_node.ArgumentSize(); arg++) {
696     BytecodeArgumentMapping arg_map = last_node.ArgumentMapping(arg);
697     int arg_pos = start_pc + arg_map.offset;
698     // If we map any jump source we mark the old source for deletion and insert
699     // a new jump.
700     auto jump_edge_iter = jump_edges_.find(arg_pos);
701     if (jump_edge_iter != jump_edges_.end()) {
702       int jump_source = jump_edge_iter->first;
703       int jump_destination = jump_edge_iter->second;
704       // Add new jump edge add current position.
705       jump_edges_mapped_.emplace(Length(), jump_destination);
706       // Mark old jump edge for deletion.
707       delete_jumps.push_back(jump_source);
708       // Decrement usage count of jump destination.
709       auto jump_count_iter = jump_usage_counts_.find(jump_destination);
710       DCHECK(jump_count_iter != jump_usage_counts_.end());
711       int& usage_count = jump_count_iter->second;
712       --usage_count;
713     }
714     // TODO(pthier): DCHECK that mapped arguments are never sources of jumps
715     // to destinations inside the sequence.
716     EmitArgument(start_pc, bytecode, arg_map);
717   }
718   DCHECK_EQ(pc(), optimized_start_pc +
719                       RegExpBytecodeLength(last_node.OptimizedBytecode()));
720 
721   // Remove jumps from arguments we ignore.
722   if (last_node.HasIgnoredArguments()) {
723     for (auto ignored_arg = last_node.ArgumentIgnoredBegin();
724          ignored_arg != last_node.ArgumentIgnoredEnd(); ignored_arg++) {
725       auto jump_edge_iter = jump_edges_.find(start_pc + ignored_arg->offset);
726       if (jump_edge_iter != jump_edges_.end()) {
727         int jump_source = jump_edge_iter->first;
728         int jump_destination = jump_edge_iter->second;
729         // Mark old jump edge for deletion.
730         delete_jumps.push_back(jump_source);
731         // Decrement usage count of jump destination.
732         auto jump_count_iter = jump_usage_counts_.find(jump_destination);
733         DCHECK(jump_count_iter != jump_usage_counts_.end());
734         int& usage_count = jump_count_iter->second;
735         --usage_count;
736       }
737     }
738   }
739 
740   int fixup_length = RegExpBytecodeLength(bc) - last_node.SequenceLength();
741 
742   // Check if there are any jumps inside the old sequence.
743   // If so we have to keep the bytecodes that are jumped to around.
744   auto jump_destination_candidate = jump_usage_counts_.upper_bound(start_pc);
745   int jump_candidate_destination = jump_destination_candidate->first;
746   int jump_candidate_count = jump_destination_candidate->second;
747   // Jump destinations only jumped to from inside the sequence will be ignored.
748   while (jump_destination_candidate != jump_usage_counts_.end() &&
749          jump_candidate_count == 0) {
750     ++jump_destination_candidate;
751     jump_candidate_destination = jump_destination_candidate->first;
752     jump_candidate_count = jump_destination_candidate->second;
753   }
754 
755   int preserve_from = start_pc + last_node.SequenceLength();
756   if (jump_destination_candidate != jump_usage_counts_.end() &&
757       jump_candidate_destination < start_pc + last_node.SequenceLength()) {
758     preserve_from = jump_candidate_destination;
759     // Check if any jump in the sequence we are preserving has a jump
760     // destination inside the optimized sequence before the current position we
761     // want to preserve. If so we have to preserve all bytecodes starting at
762     // this jump destination.
763     for (auto jump_iter = jump_edges_.lower_bound(preserve_from);
764          jump_iter != jump_edges_.end() &&
765          jump_iter->first /* jump source */ <
766              start_pc + last_node.SequenceLength();
767          ++jump_iter) {
768       int jump_destination = jump_iter->second;
769       if (jump_destination > start_pc && jump_destination < preserve_from) {
770         preserve_from = jump_destination;
771       }
772     }
773 
774     // We preserve everything to the end of the sequence. This is conservative
775     // since it would be enough to preserve all bytecudes up to an unconditional
776     // jump.
777     int preserve_length = start_pc + last_node.SequenceLength() - preserve_from;
778     fixup_length += preserve_length;
779     // Jumps after the start of the preserved sequence need fixup.
780     AddJumpSourceFixup(fixup_length,
781                        start_pc + last_node.SequenceLength() - preserve_length);
782     // All jump targets after the start of the optimized sequence need to be
783     // fixed relative to the length of the optimized sequence including
784     // bytecodes we preserved.
785     AddJumpDestinationFixup(fixup_length, start_pc + 1);
786     // Jumps to the sequence we preserved need absolute fixup as they could
787     // occur before or after the sequence.
788     SetJumpDestinationFixup(pc() - preserve_from, preserve_from);
789     CopyRangeToOutput(bytecode, preserve_from, preserve_length);
790   } else {
791     AddJumpDestinationFixup(fixup_length, start_pc + 1);
792     // Jumps after the end of the old sequence need fixup.
793     AddJumpSourceFixup(fixup_length, start_pc + last_node.SequenceLength());
794   }
795 
796   // Delete jumps we definitely don't need anymore
797   for (int del : delete_jumps) {
798     if (del < preserve_from) {
799       jump_edges_.erase(del);
800     }
801   }
802 }
803 
AddJumpSourceFixup(int fixup,int pos)804 void RegExpBytecodePeephole::AddJumpSourceFixup(int fixup, int pos) {
805   auto previous_fixup = jump_source_fixups_.lower_bound(pos);
806   DCHECK(previous_fixup != jump_source_fixups_.end());
807   DCHECK(previous_fixup != jump_source_fixups_.begin());
808 
809   int previous_fixup_value = (--previous_fixup)->second;
810   jump_source_fixups_[pos] = previous_fixup_value + fixup;
811 }
812 
AddJumpDestinationFixup(int fixup,int pos)813 void RegExpBytecodePeephole::AddJumpDestinationFixup(int fixup, int pos) {
814   auto previous_fixup = jump_destination_fixups_.lower_bound(pos);
815   DCHECK(previous_fixup != jump_destination_fixups_.end());
816   DCHECK(previous_fixup != jump_destination_fixups_.begin());
817 
818   int previous_fixup_value = (--previous_fixup)->second;
819   jump_destination_fixups_[pos] = previous_fixup_value + fixup;
820 }
821 
SetJumpDestinationFixup(int fixup,int pos)822 void RegExpBytecodePeephole::SetJumpDestinationFixup(int fixup, int pos) {
823   auto previous_fixup = jump_destination_fixups_.lower_bound(pos);
824   DCHECK(previous_fixup != jump_destination_fixups_.end());
825   DCHECK(previous_fixup != jump_destination_fixups_.begin());
826 
827   int previous_fixup_value = (--previous_fixup)->second;
828   jump_destination_fixups_.emplace(pos, fixup);
829   jump_destination_fixups_.emplace(pos + 1, previous_fixup_value);
830 }
831 
PrepareJumpStructures(const ZoneUnorderedMap<int,int> & jump_edges)832 void RegExpBytecodePeephole::PrepareJumpStructures(
833     const ZoneUnorderedMap<int, int>& jump_edges) {
834   for (auto jump_edge : jump_edges) {
835     int jump_source = jump_edge.first;
836     int jump_destination = jump_edge.second;
837 
838     jump_edges_.emplace(jump_source, jump_destination);
839     jump_usage_counts_[jump_destination]++;
840   }
841 }
842 
FixJumps()843 void RegExpBytecodePeephole::FixJumps() {
844   int position_fixup = 0;
845   // Next position where fixup changes.
846   auto next_source_fixup = jump_source_fixups_.lower_bound(0);
847   int next_source_fixup_offset = next_source_fixup->first;
848   int next_source_fixup_value = next_source_fixup->second;
849 
850   for (auto jump_edge : jump_edges_) {
851     int jump_source = jump_edge.first;
852     int jump_destination = jump_edge.second;
853     while (jump_source >= next_source_fixup_offset) {
854       position_fixup = next_source_fixup_value;
855       ++next_source_fixup;
856       next_source_fixup_offset = next_source_fixup->first;
857       next_source_fixup_value = next_source_fixup->second;
858     }
859     jump_source += position_fixup;
860 
861     FixJump(jump_source, jump_destination);
862   }
863 
864   // Mapped jump edges don't need source fixups, as the position already is an
865   // offset in the new bytecode.
866   for (auto jump_edge : jump_edges_mapped_) {
867     int jump_source = jump_edge.first;
868     int jump_destination = jump_edge.second;
869 
870     FixJump(jump_source, jump_destination);
871   }
872 }
873 
FixJump(int jump_source,int jump_destination)874 void RegExpBytecodePeephole::FixJump(int jump_source, int jump_destination) {
875   int fixed_jump_destination =
876       jump_destination +
877       (--jump_destination_fixups_.upper_bound(jump_destination))->second;
878   DCHECK_LT(fixed_jump_destination, Length());
879 #ifdef DEBUG
880   // TODO(pthier): This check could be better if we track the bytecodes
881   // actually used and check if we jump to one of them.
882   byte jump_bc = optimized_bytecode_buffer_[fixed_jump_destination];
883   DCHECK_GT(jump_bc, 0);
884   DCHECK_LT(jump_bc, kRegExpBytecodeCount);
885 #endif
886 
887   if (jump_destination != fixed_jump_destination) {
888     OverwriteValue<uint32_t>(jump_source, fixed_jump_destination);
889   }
890 }
891 
AddSentinelFixups(int pos)892 void RegExpBytecodePeephole::AddSentinelFixups(int pos) {
893   jump_source_fixups_.emplace(pos, 0);
894   jump_destination_fixups_.emplace(pos, 0);
895 }
896 
897 template <typename T>
EmitValue(T value)898 void RegExpBytecodePeephole::EmitValue(T value) {
899   DCHECK(optimized_bytecode_buffer_.begin() + pc() ==
900          optimized_bytecode_buffer_.end());
901   byte* value_byte_iter = reinterpret_cast<byte*>(&value);
902   optimized_bytecode_buffer_.insert(optimized_bytecode_buffer_.end(),
903                                     value_byte_iter,
904                                     value_byte_iter + sizeof(T));
905 }
906 
907 template <typename T>
OverwriteValue(int offset,T value)908 void RegExpBytecodePeephole::OverwriteValue(int offset, T value) {
909   byte* value_byte_iter = reinterpret_cast<byte*>(&value);
910   byte* value_byte_iter_end = value_byte_iter + sizeof(T);
911   while (value_byte_iter < value_byte_iter_end) {
912     optimized_bytecode_buffer_[offset++] = *value_byte_iter++;
913   }
914 }
915 
CopyRangeToOutput(const byte * orig_bytecode,int start,int length)916 void RegExpBytecodePeephole::CopyRangeToOutput(const byte* orig_bytecode,
917                                                int start, int length) {
918   DCHECK(optimized_bytecode_buffer_.begin() + pc() ==
919          optimized_bytecode_buffer_.end());
920   optimized_bytecode_buffer_.insert(optimized_bytecode_buffer_.end(),
921                                     orig_bytecode + start,
922                                     orig_bytecode + start + length);
923 }
924 
SetRange(byte value,int count)925 void RegExpBytecodePeephole::SetRange(byte value, int count) {
926   DCHECK(optimized_bytecode_buffer_.begin() + pc() ==
927          optimized_bytecode_buffer_.end());
928   optimized_bytecode_buffer_.insert(optimized_bytecode_buffer_.end(), count,
929                                     value);
930 }
931 
EmitArgument(int start_pc,const byte * bytecode,BytecodeArgumentMapping arg)932 void RegExpBytecodePeephole::EmitArgument(int start_pc, const byte* bytecode,
933                                           BytecodeArgumentMapping arg) {
934   int arg_pos = start_pc + arg.offset;
935   switch (arg.length) {
936     case 1:
937       DCHECK_EQ(arg.new_length, arg.length);
938       EmitValue(GetValue<byte>(bytecode, arg_pos));
939       break;
940     case 2:
941       DCHECK_EQ(arg.new_length, arg.length);
942       EmitValue(GetValue<uint16_t>(bytecode, arg_pos));
943       break;
944     case 3: {
945       // Length 3 only occurs in 'packed' arguments where the lowermost byte is
946       // the current bytecode, and the remaining 3 bytes are the packed value.
947       //
948       // We load 4 bytes from position - 1 and shift out the bytecode.
949 #ifdef V8_TARGET_BIG_ENDIAN
950       UNIMPLEMENTED();
951       int32_t val = 0;
952 #else
953       int32_t val = GetValue<int32_t>(bytecode, arg_pos - 1) >> kBitsPerByte;
954 #endif  // V8_TARGET_BIG_ENDIAN
955 
956       switch (arg.new_length) {
957         case 2:
958           EmitValue<uint16_t>(val);
959           break;
960         case 3: {
961           // Pack with previously emitted value.
962           auto prev_val =
963               GetValue<int32_t>(&(*optimized_bytecode_buffer_.begin()),
964                                 Length() - sizeof(uint32_t));
965 #ifdef V8_TARGET_BIG_ENDIAN
966       UNIMPLEMENTED();
967       USE(prev_val);
968 #else
969           DCHECK_EQ(prev_val & 0xFFFFFF00, 0);
970           OverwriteValue<uint32_t>(
971               pc() - sizeof(uint32_t),
972               (static_cast<uint32_t>(val) << 8) | (prev_val & 0xFF));
973 #endif  // V8_TARGET_BIG_ENDIAN
974           break;
975         }
976         case 4:
977           EmitValue<uint32_t>(val);
978           break;
979       }
980       break;
981     }
982     case 4:
983       DCHECK_EQ(arg.new_length, arg.length);
984       EmitValue(GetValue<uint32_t>(bytecode, arg_pos));
985       break;
986     case 8:
987       DCHECK_EQ(arg.new_length, arg.length);
988       EmitValue(GetValue<uint64_t>(bytecode, arg_pos));
989       break;
990     default:
991       CopyRangeToOutput(bytecode, arg_pos,
992                         std::min(arg.length, arg.new_length));
993       if (arg.length < arg.new_length) {
994         SetRange(0x00, arg.new_length - arg.length);
995       }
996       break;
997   }
998 }
999 
pc() const1000 int RegExpBytecodePeephole::pc() const {
1001   DCHECK_LE(optimized_bytecode_buffer_.size(), std::numeric_limits<int>::max());
1002   return static_cast<int>(optimized_bytecode_buffer_.size());
1003 }
1004 
zone() const1005 Zone* RegExpBytecodePeephole::zone() const { return zone_; }
1006 
1007 }  // namespace
1008 
1009 // static
OptimizeBytecode(Isolate * isolate,Zone * zone,Handle<String> source,const byte * bytecode,int length,const ZoneUnorderedMap<int,int> & jump_edges)1010 Handle<ByteArray> RegExpBytecodePeepholeOptimization::OptimizeBytecode(
1011     Isolate* isolate, Zone* zone, Handle<String> source, const byte* bytecode,
1012     int length, const ZoneUnorderedMap<int, int>& jump_edges) {
1013   RegExpBytecodePeephole peephole(zone, length, jump_edges);
1014   bool did_optimize = peephole.OptimizeBytecode(bytecode, length);
1015   Handle<ByteArray> array = isolate->factory()->NewByteArray(peephole.Length());
1016   peephole.CopyOptimizedBytecode(array->GetDataStartAddress());
1017 
1018   if (did_optimize && FLAG_trace_regexp_peephole_optimization) {
1019     PrintF("Original Bytecode:\n");
1020     RegExpBytecodeDisassemble(bytecode, length, source->ToCString().get());
1021     PrintF("Optimized Bytecode:\n");
1022     RegExpBytecodeDisassemble(array->GetDataStartAddress(), peephole.Length(),
1023                               source->ToCString().get());
1024   }
1025 
1026   return array;
1027 }
1028 
1029 }  // namespace internal
1030 }  // namespace v8
1031