tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

regexp-bytecode-peephole.cc (41297B)


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