// Copyright 2019 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file.
BytecodeArgument(int offset, int length) : offset(offset), length(length) {}
};
struct BytecodeArgumentMapping : BytecodeArgument { int new_length;
BytecodeArgumentMapping(int offset, int length, int new_length)
: BytecodeArgument(offset, length), new_length(new_length) {}
};
struct BytecodeArgumentCheck : BytecodeArgument { enum CheckType { kCheckAddress = 0, kCheckValue };
CheckType type; int check_offset; int check_length;
BytecodeArgumentCheck(int offset, int length, int check_offset)
: BytecodeArgument(offset, length),
type(kCheckAddress),
check_offset(check_offset) {}
BytecodeArgumentCheck(int offset, int length, int check_offset, int check_length)
: BytecodeArgument(offset, length),
type(kCheckValue),
check_offset(check_offset),
check_length(check_length) {}
};
// Trie-Node for storing bytecode sequences we want to optimize. class BytecodeSequenceNode { public: // Dummy bytecode used when we need to store/return a bytecode but it's not a // valid bytecode in the current context. static constexpr int kDummyBytecode = -1;
BytecodeSequenceNode(int bytecode, Zone* zone); // Adds a new node as child of the current node if it isn't a child already.
BytecodeSequenceNode& FollowedBy(int bytecode); // Marks the end of a sequence and sets optimized bytecode to replace all // bytecodes of the sequence with.
BytecodeSequenceNode& ReplaceWith(int bytecode); // Maps arguments of bytecodes in the sequence to the optimized bytecode. // Order of invocation determines order of arguments in the optimized // bytecode. // Invoking this method is only allowed on nodes that mark the end of a valid // sequence (i.e. after ReplaceWith()). // bytecode_index_in_sequence: Zero-based index of the referred bytecode // within the sequence (e.g. the bytecode passed to CreateSequence() has // index 0). // argument_offset: Zero-based offset to the argument within the bytecode // (e.g. the first argument that's not packed with the bytecode has offset 4). // argument_byte_length: Length of the argument. // new_argument_byte_length: Length of the argument in the new bytecode // (= argument_byte_length if omitted).
BytecodeSequenceNode& MapArgument(int bytecode_index_in_sequence, int argument_offset, int argument_byte_length, int new_argument_byte_length = 0); // Adds a check to the sequence node making it only a valid sequence when the // argument of the current bytecode at the specified offset matches the offset // to check against. // argument_offset: Zero-based offset to the argument within the bytecode // (e.g. the first argument that's not packed with the bytecode has offset 4). // argument_byte_length: Length of the argument. // check_byte_offset: Zero-based offset relative to the beginning of the // sequence that needs to match the value given by argument_offset. (e.g. // check_byte_offset 0 matches the address of the first bytecode in the // sequence).
BytecodeSequenceNode& IfArgumentEqualsOffset(int argument_offset, int argument_byte_length, int check_byte_offset); // Adds a check to the sequence node making it only a valid sequence when the // argument of the current bytecode at the specified offset matches the // argument of another bytecode in the sequence. // This is similar to IfArgumentEqualsOffset, except that this method matches // the values of both arguments.
BytecodeSequenceNode& IfArgumentEqualsValueAtOffset( int argument_offset, int argument_byte_length, int other_bytecode_index_in_sequence, int other_argument_offset, int other_argument_byte_length); // Marks an argument as unused. // All arguments that are not mapped explicitly have to be marked as unused. // bytecode_index_in_sequence: Zero-based index of the referred bytecode // within the sequence (e.g. the bytecode passed to CreateSequence() has // index 0). // argument_offset: Zero-based offset to the argument within the bytecode // (e.g. the first argument that's not packed with the bytecode has offset 4). // argument_byte_length: Length of the argument.
BytecodeSequenceNode& IgnoreArgument(int bytecode_index_in_sequence, int argument_offset, int argument_byte_length); // Checks if the current node is valid for the sequence. I.e. all conditions // set by IfArgumentEqualsOffset and IfArgumentEquals are fulfilled by this // node for the actual bytecode sequence. bool CheckArguments(const uint8_t* bytecode, int pc); // Returns whether this node marks the end of a valid sequence (i.e. can be // replaced with an optimized bytecode). bool IsSequence() const; // Returns the length of the sequence in bytes. int SequenceLength() const; // Returns the optimized bytecode for the node or kDummyBytecode if it is not // the end of a valid sequence. int OptimizedBytecode() const; // Returns the child of the current node matching the given bytecode or // nullptr if no such child is found.
BytecodeSequenceNode* Find(int bytecode) const; // Returns number of arguments mapped to the current node. // Invoking this method is only allowed on nodes that mark the end of a valid // sequence (i.e. if IsSequence())
size_t ArgumentSize() const; // Returns the argument-mapping of the argument at index. // Invoking this method is only allowed on nodes that mark the end of a valid // sequence (i.e. if IsSequence())
BytecodeArgumentMapping ArgumentMapping(size_t index) const; // Returns an iterator to begin of ignored arguments. // Invoking this method is only allowed on nodes that mark the end of a valid // sequence (i.e. if IsSequence())
ZoneLinkedList<BytecodeArgument>::iterator ArgumentIgnoredBegin() const; // Returns an iterator to end of ignored arguments. // Invoking this method is only allowed on nodes that mark the end of a valid // sequence (i.e. if IsSequence())
ZoneLinkedList<BytecodeArgument>::iterator ArgumentIgnoredEnd() const; // Returns whether the current node has ignored argument or not. bool HasIgnoredArguments() const;
private: // Returns a node in the sequence specified by its index within the sequence.
BytecodeSequenceNode& GetNodeByIndexInSequence(int index_in_sequence);
Zone* zone() const;
int bytecode_; int bytecode_replacement_; int index_in_sequence_; int start_offset_;
BytecodeSequenceNode* parent_;
ZoneUnorderedMap<int, BytecodeSequenceNode*> children_;
ZoneVector<BytecodeArgumentMapping>* argument_mapping_;
ZoneLinkedList<BytecodeArgumentCheck>* argument_check_;
ZoneLinkedList<BytecodeArgument>* argument_ignored_;
Zone* zone_;
};
// These definitions are here in order to please the linker, which in debug mode // sometimes requires static constants to be defined in .cc files.
constexpr int BytecodeSequenceNode::kDummyBytecode;
// Parses bytecode and fills the internal buffer with the potentially // optimized bytecode. Returns true when optimizations were performed, false // otherwise. bool OptimizeBytecode(const uint8_t* bytecode, int length); // Copies the internal bytecode buffer to another buffer. The caller is // responsible for allocating/freeing the memory. void CopyOptimizedBytecode(uint8_t* to_address) const; int Length() const;
private: // Sets up all sequences that are going to be used. void DefineStandardSequences(); // Starts a new bytecode sequence.
BytecodeSequenceNode& CreateSequence(int bytecode); // Checks for optimization candidates at pc and emits optimized bytecode to // the internal buffer. Returns the length of replaced bytecodes in bytes. int TryOptimizeSequence(const uint8_t* bytecode, int bytecode_length, int start_pc); // Emits optimized bytecode to the internal buffer. start_pc points to the // start of the sequence in bytecode and last_node is the last // BytecodeSequenceNode of the matching sequence found. void EmitOptimization(int start_pc, const uint8_t* bytecode, const BytecodeSequenceNode& last_node); // Adds a relative jump source fixup at pos. // Jump source fixups are used to find offsets in the new bytecode that // contain jump sources. void AddJumpSourceFixup(int fixup, int pos); // Adds a relative jump destination fixup at pos. // Jump destination fixups are used to find offsets in the new bytecode that // can be jumped to. void AddJumpDestinationFixup(int fixup, int pos); // Sets an absolute jump destination fixup at pos. void SetJumpDestinationFixup(int fixup, int pos); // Prepare internal structures used to fixup jumps. void PrepareJumpStructures(const ZoneUnorderedMap<int, int>& jump_edges); // Updates all jump targets in the new bytecode. void FixJumps(); // Update a single jump. void FixJump(int jump_source, int jump_destination); void AddSentinelFixups(int pos); template <typename T> void EmitValue(T value); template <typename T> void OverwriteValue(int offset, T value); void CopyRangeToOutput(const uint8_t* orig_bytecode, int start, int length); void SetRange(uint8_t value, int count); void EmitArgument(int start_pc, const uint8_t* bytecode,
BytecodeArgumentMapping arg); int pc() const;
Zone* zone() const;
ZoneVector<uint8_t> optimized_bytecode_buffer_;
BytecodeSequenceNode* sequences_; // Jumps used in old bytecode. // Key: Jump source (offset where destination is stored in old bytecode) // Value: Destination
ZoneMap<int, int> jump_edges_; // Jumps used in new bytecode. // Key: Jump source (offset where destination is stored in new bytecode) // Value: Destination
ZoneMap<int, int> jump_edges_mapped_; // Number of times a jump destination is used within the bytecode. // Key: Jump destination (offset in old bytecode). // Value: Number of times jump destination is used.
ZoneMap<int, int> jump_usage_counts_; // Maps offsets in old bytecode to fixups of sources (delta to new bytecode). // Key: Offset in old bytecode from where the fixup is valid. // Value: Delta to map jump source from old bytecode to new bytecode in bytes.
ZoneMap<int, int> jump_source_fixups_; // Maps offsets in old bytecode to fixups of destinations (delta to new // bytecode). // Key: Offset in old bytecode from where the fixup is valid. // Value: Delta to map jump destinations from old bytecode to new bytecode in // bytes.
ZoneMap<int, int> jump_destination_fixups_;
if (children_.find(bytecode) == children_.end()) {
BytecodeSequenceNode* new_node =
zone()->New<BytecodeSequenceNode>(bytecode, zone()); // If node is not the first in the sequence, set offsets and parent. if (bytecode_ != kDummyBytecode) {
new_node->start_offset_ = start_offset_ + RegExpBytecodeLength(bytecode_);
new_node->index_in_sequence_ = index_in_sequence_ + 1;
new_node->parent_ = this;
}
children_[bytecode] = new_node;
}
BytecodeSequenceNode& BytecodeSequenceNode::MapArgument( int bytecode_index_in_sequence, int argument_offset, int argument_byte_length, int new_argument_byte_length) {
DCHECK(IsSequence());
DCHECK_LE(bytecode_index_in_sequence, index_in_sequence_);
BytecodeSequenceNode& BytecodeSequenceNode::IfArgumentEqualsValueAtOffset( int argument_offset, int argument_byte_length, int other_bytecode_index_in_sequence, int other_argument_offset, int other_argument_byte_length) {
DCHECK_LT(argument_offset, RegExpBytecodeLength(bytecode_));
DCHECK_LE(other_bytecode_index_in_sequence, index_in_sequence_);
DCHECK_EQ(argument_byte_length, other_argument_byte_length);
BytecodeSequenceNode& BytecodeSequenceNode::IgnoreArgument( int bytecode_index_in_sequence, int argument_offset, int argument_byte_length) {
DCHECK(IsSequence());
DCHECK_LE(bytecode_index_in_sequence, index_in_sequence_);
RegExpBytecodePeephole::RegExpBytecodePeephole(
Zone* zone, size_t buffer_size, const ZoneUnorderedMap<int, int>& jump_edges)
: optimized_bytecode_buffer_(zone),
sequences_(zone->New<BytecodeSequenceNode>(
BytecodeSequenceNode::kDummyBytecode, zone)),
jump_edges_(zone),
jump_edges_mapped_(zone),
jump_usage_counts_(zone),
jump_source_fixups_(zone),
jump_destination_fixups_(zone),
zone_(zone) {
optimized_bytecode_buffer_.reserve(buffer_size);
PrepareJumpStructures(jump_edges);
DefineStandardSequences(); // Sentinel fixups at beginning of bytecode (position -1) so we don't have to // check for end of iterator inside the fixup loop. // In general fixups are deltas of original offsets of jump // sources/destinations (in the old bytecode) to find them in the new // bytecode. All jump targets are fixed after the new bytecode is fully // emitted in the internal buffer.
AddSentinelFixups(-1); // Sentinel fixups at end of (old) bytecode so we don't have to check for // end of iterator inside the fixup loop.
DCHECK_LE(buffer_size, std::numeric_limits<int>::max());
AddSentinelFixups(static_cast<int>(buffer_size));
}
void RegExpBytecodePeephole::DefineStandardSequences() { // Commonly used sequences can be found by creating regexp bytecode traces // (--trace-regexp-bytecodes) and using v8/tools/regexp-sequences.py.
CreateSequence(BC_LOAD_CURRENT_CHAR)
.FollowedBy(BC_CHECK_BIT_IN_TABLE)
.FollowedBy(BC_ADVANCE_CP_AND_GOTO) // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the // first bytecode in this sequence.
.IfArgumentEqualsOffset(4, 4, 0)
.ReplaceWith(BC_SKIP_UNTIL_BIT_IN_TABLE)
.MapArgument(0, 1, 3) // load offset
.MapArgument(2, 1, 3, 4) // advance by
.MapArgument(1, 8, 16) // bit table
.MapArgument(1, 4, 4) // goto when match
.MapArgument(0, 4, 4) // goto on failure
.IgnoreArgument(2, 4, 4); // loop jump
CreateSequence(BC_CHECK_CURRENT_POSITION)
.FollowedBy(BC_LOAD_CURRENT_CHAR_UNCHECKED)
.FollowedBy(BC_CHECK_CHAR)
.FollowedBy(BC_ADVANCE_CP_AND_GOTO) // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the // first bytecode in this sequence.
.IfArgumentEqualsOffset(4, 4, 0)
.ReplaceWith(BC_SKIP_UNTIL_CHAR_POS_CHECKED)
.MapArgument(1, 1, 3) // load offset
.MapArgument(3, 1, 3, 2) // advance_by
.MapArgument(2, 1, 3, 2) // c
.MapArgument(0, 1, 3, 4) // eats at least
.MapArgument(2, 4, 4) // goto when match
.MapArgument(0, 4, 4) // goto on failure
.IgnoreArgument(3, 4, 4); // loop jump
CreateSequence(BC_CHECK_CURRENT_POSITION)
.FollowedBy(BC_LOAD_CURRENT_CHAR_UNCHECKED)
.FollowedBy(BC_AND_CHECK_CHAR)
.FollowedBy(BC_ADVANCE_CP_AND_GOTO) // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the // first bytecode in this sequence.
.IfArgumentEqualsOffset(4, 4, 0)
.ReplaceWith(BC_SKIP_UNTIL_CHAR_AND)
.MapArgument(1, 1, 3) // load offset
.MapArgument(3, 1, 3, 2) // advance_by
.MapArgument(2, 1, 3, 2) // c
.MapArgument(2, 4, 4) // mask
.MapArgument(0, 1, 3, 4) // eats at least
.MapArgument(2, 8, 4) // goto when match
.MapArgument(0, 4, 4) // goto on failure
.IgnoreArgument(3, 4, 4); // loop jump
// TODO(pthier): It might make sense for short sequences like this one to only // optimize them if the resulting optimization is not longer than the current // one. This could be the case if there are jumps inside the sequence and we // have to replicate parts of the sequence. A method to mark such sequences // might be useful.
CreateSequence(BC_LOAD_CURRENT_CHAR)
.FollowedBy(BC_CHECK_CHAR)
.FollowedBy(BC_ADVANCE_CP_AND_GOTO) // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the // first bytecode in this sequence.
.IfArgumentEqualsOffset(4, 4, 0)
.ReplaceWith(BC_SKIP_UNTIL_CHAR)
.MapArgument(0, 1, 3) // load offset
.MapArgument(2, 1, 3, 2) // advance by
.MapArgument(1, 1, 3, 2) // character
.MapArgument(1, 4, 4) // goto when match
.MapArgument(0, 4, 4) // goto on failure
.IgnoreArgument(2, 4, 4); // loop jump
CreateSequence(BC_LOAD_CURRENT_CHAR)
.FollowedBy(BC_CHECK_CHAR)
.FollowedBy(BC_CHECK_CHAR) // Sequence is only valid if the jump targets of both CHECK_CHAR bytecodes // are equal.
.IfArgumentEqualsValueAtOffset(4, 4, 1, 4, 4)
.FollowedBy(BC_ADVANCE_CP_AND_GOTO) // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the // first bytecode in this sequence.
.IfArgumentEqualsOffset(4, 4, 0)
.ReplaceWith(BC_SKIP_UNTIL_CHAR_OR_CHAR)
.MapArgument(0, 1, 3) // load offset
.MapArgument(3, 1, 3, 4) // advance by
.MapArgument(1, 1, 3, 2) // character 1
.MapArgument(2, 1, 3, 2) // character 2
.MapArgument(1, 4, 4) // goto when match
.MapArgument(0, 4, 4) // goto on failure
.IgnoreArgument(2, 4, 4) // goto when match 2
.IgnoreArgument(3, 4, 4); // loop jump
CreateSequence(BC_LOAD_CURRENT_CHAR)
.FollowedBy(BC_CHECK_GT) // Sequence is only valid if the jump target of CHECK_GT is the first // bytecode AFTER the whole sequence.
.IfArgumentEqualsOffset(4, 4, 56)
.FollowedBy(BC_CHECK_BIT_IN_TABLE) // Sequence is only valid if the jump target of CHECK_BIT_IN_TABLE is // the ADVANCE_CP_AND_GOTO bytecode at the end of the sequence.
.IfArgumentEqualsOffset(4, 4, 48)
.FollowedBy(BC_GOTO) // Sequence is only valid if the jump target of GOTO is the same as the // jump target of CHECK_GT (i.e. both jump to the first bytecode AFTER the // whole sequence.
.IfArgumentEqualsValueAtOffset(4, 4, 1, 4, 4)
.FollowedBy(BC_ADVANCE_CP_AND_GOTO) // Sequence is only valid if the jump target of ADVANCE_CP_AND_GOTO is the // first bytecode in this sequence.
.IfArgumentEqualsOffset(4, 4, 0)
.ReplaceWith(BC_SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE)
.MapArgument(0, 1, 3) // load offset
.MapArgument(4, 1, 3, 2) // advance by
.MapArgument(1, 1, 3, 2) // character
.MapArgument(2, 8, 16) // bit table
.MapArgument(1, 4, 4) // goto when match
.MapArgument(0, 4, 4) // goto on failure
.IgnoreArgument(2, 4, 4) // indirect loop jump
.IgnoreArgument(3, 4, 4) // jump out of loop
.IgnoreArgument(4, 4, 4); // loop jump
}
bool RegExpBytecodePeephole::OptimizeBytecode(const uint8_t* bytecode, int length) { int old_pc = 0; bool did_optimize = false;
while (old_pc < length) { int replaced_len = TryOptimizeSequence(bytecode, length, old_pc); if (replaced_len > 0) {
old_pc += replaced_len;
did_optimize = true;
} else { int bc = bytecode[old_pc]; int bc_len = RegExpBytecodeLength(bc);
CopyRangeToOutput(bytecode, old_pc, bc_len);
old_pc += bc_len;
}
}
int RegExpBytecodePeephole::TryOptimizeSequence(const uint8_t* bytecode, int bytecode_length, int start_pc) {
BytecodeSequenceNode* seq_node = sequences_;
BytecodeSequenceNode* valid_seq_end = nullptr;
int current_pc = start_pc;
// Check for the longest valid sequence matching any of the pre-defined // sequences in the Trie data structure. while (current_pc < bytecode_length) {
seq_node = seq_node->Find(bytecode[current_pc]); if (seq_node == nullptr) break; if (!seq_node->CheckArguments(bytecode, start_pc)) break;
if (seq_node->IsSequence()) valid_seq_end = seq_node;
current_pc += RegExpBytecodeLength(bytecode[current_pc]);
}
if (valid_seq_end) {
EmitOptimization(start_pc, bytecode, *valid_seq_end); return valid_seq_end->SequenceLength();
}
return 0;
}
void RegExpBytecodePeephole::EmitOptimization( int start_pc, const uint8_t* bytecode, const BytecodeSequenceNode& last_node) { #ifdef DEBUG int optimized_start_pc = pc(); #endif // Jump sources that are mapped or marked as unused will be deleted at the end // of this method. We don't delete them immediately as we might need the // information when we have to preserve bytecodes at the end. // TODO(pthier): Replace with a stack-allocated data structure.
ZoneLinkedList<int> delete_jumps = ZoneLinkedList<int>(zone());
uint32_t bc = last_node.OptimizedBytecode();
EmitValue(bc);
for (size_t arg = 0; arg < last_node.ArgumentSize(); arg++) {
BytecodeArgumentMapping arg_map = last_node.ArgumentMapping(arg); int arg_pos = start_pc + arg_map.offset; // If we map any jump source we mark the old source for deletion and insert // a new jump. auto jump_edge_iter = jump_edges_.find(arg_pos); if (jump_edge_iter != jump_edges_.end()) { int jump_source = jump_edge_iter->first; int jump_destination = jump_edge_iter->second; // Add new jump edge add current position.
jump_edges_mapped_.emplace(Length(), jump_destination); // Mark old jump edge for deletion.
delete_jumps.push_back(jump_source); // Decrement usage count of jump destination. auto jump_count_iter = jump_usage_counts_.find(jump_destination);
DCHECK(jump_count_iter != jump_usage_counts_.end()); int& usage_count = jump_count_iter->second;
--usage_count;
} // TODO(pthier): DCHECK that mapped arguments are never sources of jumps // to destinations inside the sequence.
EmitArgument(start_pc, bytecode, arg_map);
}
DCHECK_EQ(pc(), optimized_start_pc +
RegExpBytecodeLength(last_node.OptimizedBytecode()));
// Remove jumps from arguments we ignore. if (last_node.HasIgnoredArguments()) { for (auto ignored_arg = last_node.ArgumentIgnoredBegin();
ignored_arg != last_node.ArgumentIgnoredEnd(); ignored_arg++) { auto jump_edge_iter = jump_edges_.find(start_pc + ignored_arg->offset); if (jump_edge_iter != jump_edges_.end()) { int jump_source = jump_edge_iter->first; int jump_destination = jump_edge_iter->second; // Mark old jump edge for deletion.
delete_jumps.push_back(jump_source); // Decrement usage count of jump destination. auto jump_count_iter = jump_usage_counts_.find(jump_destination);
DCHECK(jump_count_iter != jump_usage_counts_.end()); int& usage_count = jump_count_iter->second;
--usage_count;
}
}
}
int fixup_length = RegExpBytecodeLength(bc) - last_node.SequenceLength();
// Check if there are any jumps inside the old sequence. // If so we have to keep the bytecodes that are jumped to around. auto jump_destination_candidate = jump_usage_counts_.upper_bound(start_pc); int jump_candidate_destination = jump_destination_candidate->first; int jump_candidate_count = jump_destination_candidate->second; // Jump destinations only jumped to from inside the sequence will be ignored. while (jump_destination_candidate != jump_usage_counts_.end() &&
jump_candidate_count == 0) {
++jump_destination_candidate;
jump_candidate_destination = jump_destination_candidate->first;
jump_candidate_count = jump_destination_candidate->second;
}
int preserve_from = start_pc + last_node.SequenceLength(); if (jump_destination_candidate != jump_usage_counts_.end() &&
jump_candidate_destination < start_pc + last_node.SequenceLength()) {
preserve_from = jump_candidate_destination; // Check if any jump in the sequence we are preserving has a jump // destination inside the optimized sequence before the current position we // want to preserve. If so we have to preserve all bytecodes starting at // this jump destination. for (auto jump_iter = jump_edges_.lower_bound(preserve_from);
jump_iter != jump_edges_.end() &&
jump_iter->first /* jump source */ <
start_pc + last_node.SequenceLength();
++jump_iter) { int jump_destination = jump_iter->second; if (jump_destination > start_pc && jump_destination < preserve_from) {
preserve_from = jump_destination;
}
}
// We preserve everything to the end of the sequence. This is conservative // since it would be enough to preserve all bytecudes up to an unconditional // jump. int preserve_length = start_pc + last_node.SequenceLength() - preserve_from;
fixup_length += preserve_length; // Jumps after the start of the preserved sequence need fixup.
AddJumpSourceFixup(fixup_length,
start_pc + last_node.SequenceLength() - preserve_length); // All jump targets after the start of the optimized sequence need to be // fixed relative to the length of the optimized sequence including // bytecodes we preserved.
AddJumpDestinationFixup(fixup_length, start_pc + 1); // Jumps to the sequence we preserved need absolute fixup as they could // occur before or after the sequence.
SetJumpDestinationFixup(pc() - preserve_from, preserve_from);
CopyRangeToOutput(bytecode, preserve_from, preserve_length);
} else {
AddJumpDestinationFixup(fixup_length, start_pc + 1); // Jumps after the end of the old sequence need fixup.
AddJumpSourceFixup(fixup_length, start_pc + last_node.SequenceLength());
}
// Delete jumps we definitely don't need anymore for (int del : delete_jumps) { if (del < preserve_from) {
jump_edges_.erase(del);
}
}
}
void RegExpBytecodePeephole::AddJumpSourceFixup(int fixup, int pos) { auto previous_fixup = jump_source_fixups_.lower_bound(pos);
DCHECK(previous_fixup != jump_source_fixups_.end());
DCHECK(previous_fixup != jump_source_fixups_.begin());
int previous_fixup_value = (--previous_fixup)->second;
jump_source_fixups_[pos] = previous_fixup_value + fixup;
}
void RegExpBytecodePeephole::AddJumpDestinationFixup(int fixup, int pos) { auto previous_fixup = jump_destination_fixups_.lower_bound(pos);
DCHECK(previous_fixup != jump_destination_fixups_.end());
DCHECK(previous_fixup != jump_destination_fixups_.begin());
int previous_fixup_value = (--previous_fixup)->second;
jump_destination_fixups_[pos] = previous_fixup_value + fixup;
}
void RegExpBytecodePeephole::SetJumpDestinationFixup(int fixup, int pos) { auto previous_fixup = jump_destination_fixups_.lower_bound(pos);
DCHECK(previous_fixup != jump_destination_fixups_.end());
DCHECK(previous_fixup != jump_destination_fixups_.begin());
void RegExpBytecodePeephole::FixJumps() { int position_fixup = 0; // Next position where fixup changes. auto next_source_fixup = jump_source_fixups_.lower_bound(0); int next_source_fixup_offset = next_source_fixup->first; int next_source_fixup_value = next_source_fixup->second;
for (auto jump_edge : jump_edges_) { int jump_source = jump_edge.first; int jump_destination = jump_edge.second; while (jump_source >= next_source_fixup_offset) {
position_fixup = next_source_fixup_value;
++next_source_fixup;
next_source_fixup_offset = next_source_fixup->first;
next_source_fixup_value = next_source_fixup->second;
}
jump_source += position_fixup;
FixJump(jump_source, jump_destination);
}
// Mapped jump edges don't need source fixups, as the position already is an // offset in the new bytecode. for (auto jump_edge : jump_edges_mapped_) { int jump_source = jump_edge.first; int jump_destination = jump_edge.second;
FixJump(jump_source, jump_destination);
}
}
void RegExpBytecodePeephole::FixJump(int jump_source, int jump_destination) { int fixed_jump_destination =
jump_destination +
(--jump_destination_fixups_.upper_bound(jump_destination))->second;
DCHECK_LT(fixed_jump_destination, Length()); #ifdef DEBUG // TODO(pthier): This check could be better if we track the bytecodes // actually used and check if we jump to one of them.
uint8_t jump_bc = optimized_bytecode_buffer_[fixed_jump_destination];
DCHECK_GT(jump_bc, 0);
DCHECK_LT(jump_bc, kRegExpBytecodeCount); #endif
if (jump_destination != fixed_jump_destination) {
OverwriteValue<uint32_t>(jump_source, fixed_jump_destination);
}
}
void RegExpBytecodePeephole::EmitArgument(int start_pc, const uint8_t* bytecode,
BytecodeArgumentMapping arg) { int arg_pos = start_pc + arg.offset; switch (arg.length) { case 1:
DCHECK_EQ(arg.new_length, arg.length);
EmitValue(GetValue<uint8_t>(bytecode, arg_pos)); break; case 2:
DCHECK_EQ(arg.new_length, arg.length);
EmitValue(GetValue<uint16_t>(bytecode, arg_pos)); break; case 3: { // Length 3 only occurs in 'packed' arguments where the lowermost byte is // the current bytecode, and the remaining 3 bytes are the packed value. // // We load 4 bytes from position - 1 and shift out the bytecode. #ifdef V8_TARGET_BIG_ENDIAN
UNIMPLEMENTED();
int32_t val = 0; #else
int32_t val = GetValue<int32_t>(bytecode, arg_pos - 1) >> kBitsPerByte; #endif// V8_TARGET_BIG_ENDIAN
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.