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