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