// Copyright 2015 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.
|
|
#include "src/interpreter/bytecode-array-builder.h"
|
|
#include "src/globals.h"
|
#include "src/interpreter/bytecode-array-writer.h"
|
#include "src/interpreter/bytecode-jump-table.h"
|
#include "src/interpreter/bytecode-label.h"
|
#include "src/interpreter/bytecode-node.h"
|
#include "src/interpreter/bytecode-register-optimizer.h"
|
#include "src/interpreter/bytecode-source-info.h"
|
#include "src/interpreter/interpreter-intrinsics.h"
|
#include "src/objects-inl.h"
|
|
namespace v8 {
|
namespace internal {
|
namespace interpreter {
|
|
class RegisterTransferWriter final
|
: public NON_EXPORTED_BASE(BytecodeRegisterOptimizer::BytecodeWriter),
|
public NON_EXPORTED_BASE(ZoneObject) {
|
public:
|
RegisterTransferWriter(BytecodeArrayBuilder* builder) : builder_(builder) {}
|
~RegisterTransferWriter() override {}
|
|
void EmitLdar(Register input) override { builder_->OutputLdarRaw(input); }
|
|
void EmitStar(Register output) override { builder_->OutputStarRaw(output); }
|
|
void EmitMov(Register input, Register output) override {
|
builder_->OutputMovRaw(input, output);
|
}
|
|
private:
|
BytecodeArrayBuilder* builder_;
|
};
|
|
BytecodeArrayBuilder::BytecodeArrayBuilder(
|
Zone* zone, int parameter_count, int locals_count,
|
FeedbackVectorSpec* feedback_vector_spec,
|
SourcePositionTableBuilder::RecordingMode source_position_mode)
|
: zone_(zone),
|
feedback_vector_spec_(feedback_vector_spec),
|
bytecode_generated_(false),
|
constant_array_builder_(zone),
|
handler_table_builder_(zone),
|
return_seen_in_block_(false),
|
parameter_count_(parameter_count),
|
local_register_count_(locals_count),
|
register_allocator_(fixed_register_count()),
|
bytecode_array_writer_(zone, &constant_array_builder_,
|
source_position_mode),
|
register_optimizer_(nullptr) {
|
DCHECK_GE(parameter_count_, 0);
|
DCHECK_GE(local_register_count_, 0);
|
|
if (FLAG_ignition_reo) {
|
register_optimizer_ = new (zone) BytecodeRegisterOptimizer(
|
zone, ®ister_allocator_, fixed_register_count(), parameter_count,
|
new (zone) RegisterTransferWriter(this));
|
}
|
}
|
|
Register BytecodeArrayBuilder::Parameter(int parameter_index) const {
|
DCHECK_GE(parameter_index, 0);
|
// The parameter indices are shifted by 1 (receiver is the
|
// first entry).
|
return Register::FromParameterIndex(parameter_index + 1, parameter_count());
|
}
|
|
Register BytecodeArrayBuilder::Receiver() const {
|
return Register::FromParameterIndex(0, parameter_count());
|
}
|
|
Register BytecodeArrayBuilder::Local(int index) const {
|
// TODO(marja): Make a DCHECK once crbug.com/706234 is fixed.
|
CHECK_LT(index, locals_count());
|
return Register(index);
|
}
|
|
Handle<BytecodeArray> BytecodeArrayBuilder::ToBytecodeArray(Isolate* isolate) {
|
DCHECK(return_seen_in_block_);
|
DCHECK(!bytecode_generated_);
|
bytecode_generated_ = true;
|
|
int register_count = total_register_count();
|
|
if (register_optimizer_) {
|
register_optimizer_->Flush();
|
register_count = register_optimizer_->maxiumum_register_index() + 1;
|
}
|
|
Handle<ByteArray> handler_table =
|
handler_table_builder()->ToHandlerTable(isolate);
|
return bytecode_array_writer_.ToBytecodeArray(
|
isolate, register_count, parameter_count(), handler_table);
|
}
|
|
BytecodeSourceInfo BytecodeArrayBuilder::CurrentSourcePosition(
|
Bytecode bytecode) {
|
BytecodeSourceInfo source_position;
|
if (latest_source_info_.is_valid()) {
|
// Statement positions need to be emitted immediately. Expression
|
// positions can be pushed back until a bytecode is found that can
|
// throw (if expression position filtering is turned on). We only
|
// invalidate the existing source position information if it is used.
|
if (latest_source_info_.is_statement() ||
|
!FLAG_ignition_filter_expression_positions ||
|
!Bytecodes::IsWithoutExternalSideEffects(bytecode)) {
|
source_position = latest_source_info_;
|
latest_source_info_.set_invalid();
|
}
|
}
|
return source_position;
|
}
|
|
void BytecodeArrayBuilder::SetDeferredSourceInfo(
|
BytecodeSourceInfo source_info) {
|
if (!source_info.is_valid()) return;
|
deferred_source_info_ = source_info;
|
}
|
|
void BytecodeArrayBuilder::AttachOrEmitDeferredSourceInfo(BytecodeNode* node) {
|
if (!deferred_source_info_.is_valid()) return;
|
if (!node->source_info().is_valid()) {
|
node->set_source_info(deferred_source_info_);
|
} else if (deferred_source_info_.is_statement() &&
|
node->source_info().is_expression()) {
|
BytecodeSourceInfo source_position = node->source_info();
|
source_position.MakeStatementPosition(source_position.source_position());
|
node->set_source_info(source_position);
|
}
|
deferred_source_info_.set_invalid();
|
}
|
|
void BytecodeArrayBuilder::Write(BytecodeNode* node) {
|
AttachOrEmitDeferredSourceInfo(node);
|
bytecode_array_writer_.Write(node);
|
}
|
|
void BytecodeArrayBuilder::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
|
AttachOrEmitDeferredSourceInfo(node);
|
bytecode_array_writer_.WriteJump(node, label);
|
}
|
|
void BytecodeArrayBuilder::WriteSwitch(BytecodeNode* node,
|
BytecodeJumpTable* jump_table) {
|
AttachOrEmitDeferredSourceInfo(node);
|
bytecode_array_writer_.WriteSwitch(node, jump_table);
|
}
|
|
void BytecodeArrayBuilder::OutputLdarRaw(Register reg) {
|
uint32_t operand = static_cast<uint32_t>(reg.ToOperand());
|
BytecodeNode node(BytecodeNode::Ldar(BytecodeSourceInfo(), operand));
|
Write(&node);
|
}
|
|
void BytecodeArrayBuilder::OutputStarRaw(Register reg) {
|
uint32_t operand = static_cast<uint32_t>(reg.ToOperand());
|
BytecodeNode node(BytecodeNode::Star(BytecodeSourceInfo(), operand));
|
Write(&node);
|
}
|
|
void BytecodeArrayBuilder::OutputMovRaw(Register src, Register dest) {
|
uint32_t operand0 = static_cast<uint32_t>(src.ToOperand());
|
uint32_t operand1 = static_cast<uint32_t>(dest.ToOperand());
|
BytecodeNode node(
|
BytecodeNode::Mov(BytecodeSourceInfo(), operand0, operand1));
|
Write(&node);
|
}
|
|
namespace {
|
|
template <OperandTypeInfo type_info>
|
class UnsignedOperandHelper {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
size_t value) {
|
DCHECK(IsValid(value));
|
return static_cast<uint32_t>(value);
|
}
|
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder, int value) {
|
DCHECK_GE(value, 0);
|
return Convert(builder, static_cast<size_t>(value));
|
}
|
|
private:
|
static bool IsValid(size_t value) {
|
switch (type_info) {
|
case OperandTypeInfo::kFixedUnsignedByte:
|
return value <= kMaxUInt8;
|
case OperandTypeInfo::kFixedUnsignedShort:
|
return value <= kMaxUInt16;
|
case OperandTypeInfo::kScalableUnsignedByte:
|
return value <= kMaxUInt32;
|
default:
|
UNREACHABLE();
|
}
|
}
|
};
|
|
template <OperandType>
|
class OperandHelper {};
|
|
#define DEFINE_UNSIGNED_OPERAND_HELPER(Name, Type) \
|
template <> \
|
class OperandHelper<OperandType::k##Name> \
|
: public UnsignedOperandHelper<Type> {};
|
UNSIGNED_FIXED_SCALAR_OPERAND_TYPE_LIST(DEFINE_UNSIGNED_OPERAND_HELPER)
|
UNSIGNED_SCALABLE_SCALAR_OPERAND_TYPE_LIST(DEFINE_UNSIGNED_OPERAND_HELPER)
|
#undef DEFINE_UNSIGNED_OPERAND_HELPER
|
|
template <>
|
class OperandHelper<OperandType::kImm> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder, int value) {
|
return static_cast<uint32_t>(value);
|
}
|
};
|
|
template <>
|
class OperandHelper<OperandType::kReg> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
Register reg) {
|
return builder->GetInputRegisterOperand(reg);
|
}
|
};
|
|
template <>
|
class OperandHelper<OperandType::kRegList> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
RegisterList reg_list) {
|
return builder->GetInputRegisterListOperand(reg_list);
|
}
|
};
|
|
template <>
|
class OperandHelper<OperandType::kRegPair> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
RegisterList reg_list) {
|
DCHECK_EQ(reg_list.register_count(), 2);
|
return builder->GetInputRegisterListOperand(reg_list);
|
}
|
};
|
|
template <>
|
class OperandHelper<OperandType::kRegOut> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
Register reg) {
|
return builder->GetOutputRegisterOperand(reg);
|
}
|
};
|
|
template <>
|
class OperandHelper<OperandType::kRegOutList> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
RegisterList reg_list) {
|
return builder->GetOutputRegisterListOperand(reg_list);
|
}
|
};
|
|
template <>
|
class OperandHelper<OperandType::kRegOutPair> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
RegisterList reg_list) {
|
DCHECK_EQ(2, reg_list.register_count());
|
return builder->GetOutputRegisterListOperand(reg_list);
|
}
|
};
|
|
template <>
|
class OperandHelper<OperandType::kRegOutTriple> {
|
public:
|
V8_INLINE static uint32_t Convert(BytecodeArrayBuilder* builder,
|
RegisterList reg_list) {
|
DCHECK_EQ(3, reg_list.register_count());
|
return builder->GetOutputRegisterListOperand(reg_list);
|
}
|
};
|
|
} // namespace
|
|
template <Bytecode bytecode, AccumulatorUse accumulator_use,
|
OperandType... operand_types>
|
class BytecodeNodeBuilder {
|
public:
|
template <typename... Operands>
|
V8_INLINE static BytecodeNode Make(BytecodeArrayBuilder* builder,
|
Operands... operands) {
|
static_assert(sizeof...(Operands) <= Bytecodes::kMaxOperands,
|
"too many operands for bytecode");
|
builder->PrepareToOutputBytecode<bytecode, accumulator_use>();
|
// The "OperandHelper<operand_types>::Convert(builder, operands)..." will
|
// expand both the OperandType... and Operands... parameter packs e.g. for:
|
// BytecodeNodeBuilder<OperandType::kReg, OperandType::kImm>::Make<
|
// Register, int>(..., Register reg, int immediate)
|
// the code will expand into:
|
// OperandHelper<OperandType::kReg>::Convert(builder, reg),
|
// OperandHelper<OperandType::kImm>::Convert(builder, immediate),
|
return BytecodeNode::Create<bytecode, accumulator_use, operand_types...>(
|
builder->CurrentSourcePosition(bytecode),
|
OperandHelper<operand_types>::Convert(builder, operands)...);
|
}
|
};
|
|
#define DEFINE_BYTECODE_OUTPUT(name, ...) \
|
template <typename... Operands> \
|
BytecodeNode BytecodeArrayBuilder::Create##name##Node( \
|
Operands... operands) { \
|
return BytecodeNodeBuilder<Bytecode::k##name, __VA_ARGS__>::Make( \
|
this, operands...); \
|
} \
|
\
|
template <typename... Operands> \
|
void BytecodeArrayBuilder::Output##name(Operands... operands) { \
|
BytecodeNode node(Create##name##Node(operands...)); \
|
Write(&node); \
|
} \
|
\
|
template <typename... Operands> \
|
void BytecodeArrayBuilder::Output##name(BytecodeLabel* label, \
|
Operands... operands) { \
|
DCHECK(Bytecodes::IsJump(Bytecode::k##name)); \
|
BytecodeNode node(Create##name##Node(operands...)); \
|
WriteJump(&node, label); \
|
LeaveBasicBlock(); \
|
}
|
BYTECODE_LIST(DEFINE_BYTECODE_OUTPUT)
|
#undef DEFINE_BYTECODE_OUTPUT
|
|
void BytecodeArrayBuilder::OutputSwitchOnSmiNoFeedback(
|
BytecodeJumpTable* jump_table) {
|
BytecodeNode node(CreateSwitchOnSmiNoFeedbackNode(
|
jump_table->constant_pool_index(), jump_table->size(),
|
jump_table->case_value_base()));
|
WriteSwitch(&node, jump_table);
|
LeaveBasicBlock();
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperation(Token::Value op,
|
Register reg,
|
int feedback_slot) {
|
switch (op) {
|
case Token::Value::ADD:
|
OutputAdd(reg, feedback_slot);
|
break;
|
case Token::Value::SUB:
|
OutputSub(reg, feedback_slot);
|
break;
|
case Token::Value::MUL:
|
OutputMul(reg, feedback_slot);
|
break;
|
case Token::Value::DIV:
|
OutputDiv(reg, feedback_slot);
|
break;
|
case Token::Value::MOD:
|
OutputMod(reg, feedback_slot);
|
break;
|
case Token::Value::EXP:
|
OutputExp(reg, feedback_slot);
|
break;
|
case Token::Value::BIT_OR:
|
OutputBitwiseOr(reg, feedback_slot);
|
break;
|
case Token::Value::BIT_XOR:
|
OutputBitwiseXor(reg, feedback_slot);
|
break;
|
case Token::Value::BIT_AND:
|
OutputBitwiseAnd(reg, feedback_slot);
|
break;
|
case Token::Value::SHL:
|
OutputShiftLeft(reg, feedback_slot);
|
break;
|
case Token::Value::SAR:
|
OutputShiftRight(reg, feedback_slot);
|
break;
|
case Token::Value::SHR:
|
OutputShiftRightLogical(reg, feedback_slot);
|
break;
|
default:
|
UNREACHABLE();
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::BinaryOperationSmiLiteral(
|
Token::Value op, Smi* literal, int feedback_slot) {
|
switch (op) {
|
case Token::Value::ADD:
|
OutputAddSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::SUB:
|
OutputSubSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::MUL:
|
OutputMulSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::DIV:
|
OutputDivSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::MOD:
|
OutputModSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::EXP:
|
OutputExpSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::BIT_OR:
|
OutputBitwiseOrSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::BIT_XOR:
|
OutputBitwiseXorSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::BIT_AND:
|
OutputBitwiseAndSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::SHL:
|
OutputShiftLeftSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::SAR:
|
OutputShiftRightSmi(literal->value(), feedback_slot);
|
break;
|
case Token::Value::SHR:
|
OutputShiftRightLogicalSmi(literal->value(), feedback_slot);
|
break;
|
default:
|
UNREACHABLE();
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::UnaryOperation(Token::Value op,
|
int feedback_slot) {
|
switch (op) {
|
case Token::Value::INC:
|
OutputInc(feedback_slot);
|
break;
|
case Token::Value::DEC:
|
OutputDec(feedback_slot);
|
break;
|
case Token::Value::ADD:
|
OutputToNumber(feedback_slot);
|
break;
|
case Token::Value::SUB:
|
OutputNegate(feedback_slot);
|
break;
|
case Token::Value::BIT_NOT:
|
OutputBitwiseNot(feedback_slot);
|
break;
|
default:
|
UNREACHABLE();
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LogicalNot(ToBooleanMode mode) {
|
if (mode == ToBooleanMode::kAlreadyBoolean) {
|
OutputLogicalNot();
|
} else {
|
DCHECK_EQ(mode, ToBooleanMode::kConvertToBoolean);
|
OutputToBooleanLogicalNot();
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::TypeOf() {
|
OutputTypeOf();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::GetSuperConstructor(Register out) {
|
OutputGetSuperConstructor(out);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(
|
Token::Value op, Register reg, int feedback_slot) {
|
switch (op) {
|
case Token::Value::EQ:
|
OutputTestEqual(reg, feedback_slot);
|
break;
|
case Token::Value::EQ_STRICT:
|
OutputTestEqualStrict(reg, feedback_slot);
|
break;
|
case Token::Value::LT:
|
OutputTestLessThan(reg, feedback_slot);
|
break;
|
case Token::Value::GT:
|
OutputTestGreaterThan(reg, feedback_slot);
|
break;
|
case Token::Value::LTE:
|
OutputTestLessThanOrEqual(reg, feedback_slot);
|
break;
|
case Token::Value::GTE:
|
OutputTestGreaterThanOrEqual(reg, feedback_slot);
|
break;
|
case Token::Value::INSTANCEOF:
|
OutputTestInstanceOf(reg, feedback_slot);
|
break;
|
default:
|
UNREACHABLE();
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareOperation(Token::Value op,
|
Register reg) {
|
switch (op) {
|
case Token::Value::IN:
|
OutputTestIn(reg);
|
break;
|
default:
|
UNREACHABLE();
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareReference(Register reg) {
|
OutputTestReferenceEqual(reg);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareUndetectable() {
|
OutputTestUndetectable();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareUndefined() {
|
OutputTestUndefined();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareNull() {
|
OutputTestNull();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareNil(Token::Value op,
|
NilValue nil) {
|
if (op == Token::EQ) {
|
return CompareUndetectable();
|
} else {
|
DCHECK_EQ(Token::EQ_STRICT, op);
|
if (nil == kUndefinedValue) {
|
return CompareUndefined();
|
} else {
|
DCHECK_EQ(kNullValue, nil);
|
return CompareNull();
|
}
|
}
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CompareTypeOf(
|
TestTypeOfFlags::LiteralFlag literal_flag) {
|
DCHECK_NE(literal_flag, TestTypeOfFlags::LiteralFlag::kOther);
|
OutputTestTypeOf(TestTypeOfFlags::Encode(literal_flag));
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadConstantPoolEntry(
|
size_t entry) {
|
OutputLdaConstant(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
|
v8::internal::Smi* smi) {
|
int32_t raw_smi = smi->value();
|
if (raw_smi == 0) {
|
OutputLdaZero();
|
} else {
|
OutputLdaSmi(raw_smi);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(double value) {
|
size_t entry = GetConstantPoolEntry(value);
|
OutputLdaConstant(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
|
const AstRawString* raw_string) {
|
size_t entry = GetConstantPoolEntry(raw_string);
|
OutputLdaConstant(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(const Scope* scope) {
|
size_t entry = GetConstantPoolEntry(scope);
|
OutputLdaConstant(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(AstBigInt bigint) {
|
size_t entry = GetConstantPoolEntry(bigint);
|
OutputLdaConstant(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(AstSymbol symbol) {
|
size_t entry;
|
switch (symbol) {
|
case AstSymbol::kHomeObjectSymbol:
|
entry = HomeObjectSymbolConstantPoolEntry();
|
break;
|
// No default case so that we get a warning if AstSymbol changes
|
}
|
OutputLdaConstant(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadUndefined() {
|
OutputLdaUndefined();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNull() {
|
OutputLdaNull();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTheHole() {
|
OutputLdaTheHole();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadTrue() {
|
OutputLdaTrue();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadFalse() {
|
OutputLdaFalse();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadBoolean(bool value) {
|
return value ? LoadTrue() : LoadFalse();
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAccumulatorWithRegister(
|
Register reg) {
|
if (register_optimizer_) {
|
// Defer source info so that if we elide the bytecode transfer, we attach
|
// the source info to a subsequent bytecode if it exists.
|
SetDeferredSourceInfo(CurrentSourcePosition(Bytecode::kLdar));
|
register_optimizer_->DoLdar(reg);
|
} else {
|
OutputLdar(reg);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
|
Register reg) {
|
if (register_optimizer_) {
|
// Defer source info so that if we elide the bytecode transfer, we attach
|
// the source info to a subsequent bytecode if it exists.
|
SetDeferredSourceInfo(CurrentSourcePosition(Bytecode::kStar));
|
register_optimizer_->DoStar(reg);
|
} else {
|
OutputStar(reg);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::MoveRegister(Register from,
|
Register to) {
|
DCHECK(from != to);
|
if (register_optimizer_) {
|
// Defer source info so that if we elide the bytecode transfer, we attach
|
// the source info to a subsequent bytecode if it exists.
|
SetDeferredSourceInfo(CurrentSourcePosition(Bytecode::kMov));
|
register_optimizer_->DoMov(from, to);
|
} else {
|
OutputMov(from, to);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(const AstRawString* name,
|
int feedback_slot,
|
TypeofMode typeof_mode) {
|
size_t name_index = GetConstantPoolEntry(name);
|
// Ensure that typeof mode is in sync with the IC slot kind.
|
DCHECK_EQ(GetTypeofModeFromSlotKind(feedback_vector_spec()->GetKind(
|
FeedbackVector::ToSlot(feedback_slot))),
|
typeof_mode);
|
if (typeof_mode == INSIDE_TYPEOF) {
|
OutputLdaGlobalInsideTypeof(name_index, feedback_slot);
|
} else {
|
DCHECK_EQ(typeof_mode, NOT_INSIDE_TYPEOF);
|
OutputLdaGlobal(name_index, feedback_slot);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreGlobal(
|
const AstRawString* name, int feedback_slot) {
|
size_t name_index = GetConstantPoolEntry(name);
|
OutputStaGlobal(name_index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadContextSlot(
|
Register context, int slot_index, int depth,
|
ContextSlotMutability mutability) {
|
if (context.is_current_context() && depth == 0) {
|
if (mutability == kImmutableSlot) {
|
OutputLdaImmutableCurrentContextSlot(slot_index);
|
} else {
|
DCHECK_EQ(kMutableSlot, mutability);
|
OutputLdaCurrentContextSlot(slot_index);
|
}
|
} else if (mutability == kImmutableSlot) {
|
OutputLdaImmutableContextSlot(context, slot_index, depth);
|
} else {
|
DCHECK_EQ(mutability, kMutableSlot);
|
OutputLdaContextSlot(context, slot_index, depth);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreContextSlot(Register context,
|
int slot_index,
|
int depth) {
|
if (context.is_current_context() && depth == 0) {
|
OutputStaCurrentContextSlot(slot_index);
|
} else {
|
OutputStaContextSlot(context, slot_index, depth);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupSlot(
|
const AstRawString* name, TypeofMode typeof_mode) {
|
size_t name_index = GetConstantPoolEntry(name);
|
if (typeof_mode == INSIDE_TYPEOF) {
|
OutputLdaLookupSlotInsideTypeof(name_index);
|
} else {
|
DCHECK_EQ(typeof_mode, NOT_INSIDE_TYPEOF);
|
OutputLdaLookupSlot(name_index);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupContextSlot(
|
const AstRawString* name, TypeofMode typeof_mode, int slot_index,
|
int depth) {
|
size_t name_index = GetConstantPoolEntry(name);
|
if (typeof_mode == INSIDE_TYPEOF) {
|
OutputLdaLookupContextSlotInsideTypeof(name_index, slot_index, depth);
|
} else {
|
DCHECK(typeof_mode == NOT_INSIDE_TYPEOF);
|
OutputLdaLookupContextSlot(name_index, slot_index, depth);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLookupGlobalSlot(
|
const AstRawString* name, TypeofMode typeof_mode, int feedback_slot,
|
int depth) {
|
size_t name_index = GetConstantPoolEntry(name);
|
if (typeof_mode == INSIDE_TYPEOF) {
|
OutputLdaLookupGlobalSlotInsideTypeof(name_index, feedback_slot, depth);
|
} else {
|
DCHECK(typeof_mode == NOT_INSIDE_TYPEOF);
|
OutputLdaLookupGlobalSlot(name_index, feedback_slot, depth);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreLookupSlot(
|
const AstRawString* name, LanguageMode language_mode,
|
LookupHoistingMode lookup_hoisting_mode) {
|
size_t name_index = GetConstantPoolEntry(name);
|
uint8_t flags =
|
StoreLookupSlotFlags::Encode(language_mode, lookup_hoisting_mode);
|
OutputStaLookupSlot(name_index, flags);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
|
Register object, const AstRawString* name, int feedback_slot) {
|
size_t name_index = GetConstantPoolEntry(name);
|
OutputLdaNamedProperty(object, name_index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
|
Register object, int feedback_slot) {
|
OutputLdaKeyedProperty(object, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadIteratorProperty(
|
Register object, int feedback_slot) {
|
size_t name_index = IteratorSymbolConstantPoolEntry();
|
OutputLdaNamedProperty(object, name_index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAsyncIteratorProperty(
|
Register object, int feedback_slot) {
|
size_t name_index = AsyncIteratorSymbolConstantPoolEntry();
|
OutputLdaNamedProperty(object, name_index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreDataPropertyInLiteral(
|
Register object, Register name, DataPropertyInLiteralFlags flags,
|
int feedback_slot) {
|
OutputStaDataPropertyInLiteral(object, name, flags, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CollectTypeProfile(int position) {
|
OutputCollectTypeProfile(position);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
|
Register object, size_t name_index, int feedback_slot,
|
LanguageMode language_mode) {
|
// Ensure that language mode is in sync with the IC slot kind.
|
DCHECK_EQ(GetLanguageModeFromSlotKind(feedback_vector_spec()->GetKind(
|
FeedbackVector::ToSlot(feedback_slot))),
|
language_mode);
|
OutputStaNamedProperty(object, name_index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
|
Register object, const AstRawString* name, int feedback_slot,
|
LanguageMode language_mode) {
|
size_t name_index = GetConstantPoolEntry(name);
|
return StoreNamedProperty(object, name_index, feedback_slot, language_mode);
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedOwnProperty(
|
Register object, const AstRawString* name, int feedback_slot) {
|
size_t name_index = GetConstantPoolEntry(name);
|
// Ensure that the store operation is in sync with the IC slot kind.
|
DCHECK_EQ(
|
FeedbackSlotKind::kStoreOwnNamed,
|
feedback_vector_spec()->GetKind(FeedbackVector::ToSlot(feedback_slot)));
|
OutputStaNamedOwnProperty(object, name_index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
|
Register object, Register key, int feedback_slot,
|
LanguageMode language_mode) {
|
// Ensure that language mode is in sync with the IC slot kind.
|
DCHECK_EQ(GetLanguageModeFromSlotKind(feedback_vector_spec()->GetKind(
|
FeedbackVector::ToSlot(feedback_slot))),
|
language_mode);
|
OutputStaKeyedProperty(object, key, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreInArrayLiteral(
|
Register array, Register index, int feedback_slot) {
|
OutputStaInArrayLiteral(array, index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreHomeObjectProperty(
|
Register object, int feedback_slot, LanguageMode language_mode) {
|
size_t name_index = HomeObjectSymbolConstantPoolEntry();
|
return StoreNamedProperty(object, name_index, feedback_slot, language_mode);
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreClassFieldsInitializer(
|
Register constructor, int feedback_slot) {
|
size_t name_index = ClassFieldsSymbolConstantPoolEntry();
|
return StoreNamedProperty(constructor, name_index, feedback_slot,
|
LanguageMode::kStrict);
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadClassFieldsInitializer(
|
Register constructor, int feedback_slot) {
|
size_t name_index = ClassFieldsSymbolConstantPoolEntry();
|
OutputLdaNamedProperty(constructor, name_index, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
|
size_t shared_function_info_entry, int slot, int flags) {
|
OutputCreateClosure(shared_function_info_entry, slot, flags);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateBlockContext(
|
const Scope* scope) {
|
size_t entry = GetConstantPoolEntry(scope);
|
OutputCreateBlockContext(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateCatchContext(
|
Register exception, const Scope* scope) {
|
size_t scope_index = GetConstantPoolEntry(scope);
|
OutputCreateCatchContext(exception, scope_index);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateFunctionContext(
|
const Scope* scope, int slots) {
|
size_t scope_index = GetConstantPoolEntry(scope);
|
OutputCreateFunctionContext(scope_index, slots);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateEvalContext(
|
const Scope* scope, int slots) {
|
size_t scope_index = GetConstantPoolEntry(scope);
|
OutputCreateEvalContext(scope_index, slots);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateWithContext(
|
Register object, const Scope* scope) {
|
size_t scope_index = GetConstantPoolEntry(scope);
|
OutputCreateWithContext(object, scope_index);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArguments(
|
CreateArgumentsType type) {
|
switch (type) {
|
case CreateArgumentsType::kMappedArguments:
|
OutputCreateMappedArguments();
|
break;
|
case CreateArgumentsType::kUnmappedArguments:
|
OutputCreateUnmappedArguments();
|
break;
|
case CreateArgumentsType::kRestParameter:
|
OutputCreateRestParameter();
|
break;
|
default:
|
UNREACHABLE();
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateRegExpLiteral(
|
const AstRawString* pattern, int literal_index, int flags) {
|
size_t pattern_entry = GetConstantPoolEntry(pattern);
|
OutputCreateRegExpLiteral(pattern_entry, literal_index, flags);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateEmptyArrayLiteral(
|
int literal_index) {
|
OutputCreateEmptyArrayLiteral(literal_index);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateArrayLiteral(
|
size_t constant_elements_entry, int literal_index, int flags) {
|
OutputCreateArrayLiteral(constant_elements_entry, literal_index, flags);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateObjectLiteral(
|
size_t constant_properties_entry, int literal_index, int flags,
|
Register output) {
|
OutputCreateObjectLiteral(constant_properties_entry, literal_index, flags,
|
output);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateEmptyObjectLiteral() {
|
OutputCreateEmptyObjectLiteral();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CloneObject(Register source,
|
int flags,
|
int feedback_slot) {
|
OutputCloneObject(source, flags, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::GetTemplateObject(
|
size_t template_object_description_entry, int feedback_slot) {
|
OutputGetTemplateObject(template_object_description_entry, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::PushContext(Register context) {
|
OutputPushContext(context);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::PopContext(Register context) {
|
OutputPopContext(context);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ToObject(Register out) {
|
OutputToObject(out);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ToName(Register out) {
|
OutputToName(out);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ToString() {
|
OutputToString();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ToNumber(int feedback_slot) {
|
OutputToNumber(feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ToNumeric(int feedback_slot) {
|
OutputToNumeric(feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeLabel* label) {
|
// Flush the register optimizer when binding a label to ensure all
|
// expected registers are valid when jumping to this label.
|
if (register_optimizer_) register_optimizer_->Flush();
|
bytecode_array_writer_.BindLabel(label);
|
LeaveBasicBlock();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(const BytecodeLabel& target,
|
BytecodeLabel* label) {
|
bytecode_array_writer_.BindLabel(target, label);
|
LeaveBasicBlock();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Bind(BytecodeJumpTable* jump_table,
|
int case_value) {
|
// Flush the register optimizer when binding a jump table entry to ensure
|
// all expected registers are valid when jumping to this location.
|
if (register_optimizer_) register_optimizer_->Flush();
|
bytecode_array_writer_.BindJumpTableEntry(jump_table, case_value);
|
LeaveBasicBlock();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Jump(BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
OutputJump(label, 0);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfTrue(ToBooleanMode mode,
|
BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
if (mode == ToBooleanMode::kAlreadyBoolean) {
|
OutputJumpIfTrue(label, 0);
|
} else {
|
DCHECK_EQ(mode, ToBooleanMode::kConvertToBoolean);
|
OutputJumpIfToBooleanTrue(label, 0);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfFalse(ToBooleanMode mode,
|
BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
if (mode == ToBooleanMode::kAlreadyBoolean) {
|
OutputJumpIfFalse(label, 0);
|
} else {
|
DCHECK_EQ(mode, ToBooleanMode::kConvertToBoolean);
|
OutputJumpIfToBooleanFalse(label, 0);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNull(BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
OutputJumpIfNull(label, 0);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotNull(
|
BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
OutputJumpIfNotNull(label, 0);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfUndefined(
|
BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
OutputJumpIfUndefined(label, 0);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotUndefined(
|
BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
OutputJumpIfNotUndefined(label, 0);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNil(BytecodeLabel* label,
|
Token::Value op,
|
NilValue nil) {
|
if (op == Token::EQ) {
|
// TODO(rmcilroy): Implement JumpIfUndetectable.
|
return CompareUndetectable().JumpIfTrue(ToBooleanMode::kAlreadyBoolean,
|
label);
|
} else {
|
DCHECK_EQ(Token::EQ_STRICT, op);
|
if (nil == kUndefinedValue) {
|
return JumpIfUndefined(label);
|
} else {
|
DCHECK_EQ(kNullValue, nil);
|
return JumpIfNull(label);
|
}
|
}
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfNotNil(BytecodeLabel* label,
|
Token::Value op,
|
NilValue nil) {
|
if (op == Token::EQ) {
|
// TODO(rmcilroy): Implement JumpIfUndetectable.
|
return CompareUndetectable().JumpIfFalse(ToBooleanMode::kAlreadyBoolean,
|
label);
|
} else {
|
DCHECK_EQ(Token::EQ_STRICT, op);
|
if (nil == kUndefinedValue) {
|
return JumpIfNotUndefined(label);
|
} else {
|
DCHECK_EQ(kNullValue, nil);
|
return JumpIfNotNull(label);
|
}
|
}
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpIfJSReceiver(
|
BytecodeLabel* label) {
|
DCHECK(!label->is_bound());
|
OutputJumpIfJSReceiver(label, 0);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::JumpLoop(BytecodeLabel* label,
|
int loop_depth) {
|
DCHECK(label->is_bound());
|
OutputJumpLoop(label, 0, loop_depth);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::SwitchOnSmiNoFeedback(
|
BytecodeJumpTable* jump_table) {
|
OutputSwitchOnSmiNoFeedback(jump_table);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StackCheck(int position) {
|
if (position != kNoSourcePosition) {
|
// We need to attach a non-breakable source position to a stack
|
// check, so we simply add it as expression position. There can be
|
// a prior statement position from constructs like:
|
//
|
// do var x; while (false);
|
//
|
// A Nop could be inserted for empty statements, but since no code
|
// is associated with these positions, instead we force the stack
|
// check's expression position which eliminates the empty
|
// statement's position.
|
latest_source_info_.ForceExpressionPosition(position);
|
}
|
OutputStackCheck();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::SetPendingMessage() {
|
OutputSetPendingMessage();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Throw() {
|
OutputThrow();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ReThrow() {
|
OutputReThrow();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Abort(AbortReason reason) {
|
DCHECK_LT(reason, AbortReason::kLastErrorMessage);
|
DCHECK_GE(reason, AbortReason::kNoReason);
|
OutputAbort(static_cast<int>(reason));
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
|
OutputReturn();
|
return_seen_in_block_ = true;
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ThrowReferenceErrorIfHole(
|
const AstRawString* name) {
|
size_t entry = GetConstantPoolEntry(name);
|
OutputThrowReferenceErrorIfHole(entry);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ThrowSuperNotCalledIfHole() {
|
OutputThrowSuperNotCalledIfHole();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ThrowSuperAlreadyCalledIfNotHole() {
|
OutputThrowSuperAlreadyCalledIfNotHole();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Debugger() {
|
OutputDebugger();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::IncBlockCounter(
|
int coverage_array_slot) {
|
OutputIncBlockCounter(coverage_array_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInEnumerate(Register receiver) {
|
OutputForInEnumerate(receiver);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare(
|
RegisterList cache_info_triple, int feedback_slot) {
|
DCHECK_EQ(3, cache_info_triple.register_count());
|
OutputForInPrepare(cache_info_triple, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInContinue(
|
Register index, Register cache_length) {
|
OutputForInContinue(index, cache_length);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(
|
Register receiver, Register index, RegisterList cache_type_array_pair,
|
int feedback_slot) {
|
DCHECK_EQ(2, cache_type_array_pair.register_count());
|
OutputForInNext(receiver, index, cache_type_array_pair, feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) {
|
OutputForInStep(index);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreModuleVariable(int cell_index,
|
int depth) {
|
OutputStaModuleVariable(cell_index, depth);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadModuleVariable(int cell_index,
|
int depth) {
|
OutputLdaModuleVariable(cell_index, depth);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::SuspendGenerator(
|
Register generator, RegisterList registers, int suspend_id) {
|
OutputSuspendGenerator(generator, registers, registers.register_count(),
|
suspend_id);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::SwitchOnGeneratorState(
|
Register generator, BytecodeJumpTable* jump_table) {
|
DCHECK_EQ(jump_table->case_value_base(), 0);
|
BytecodeNode node(CreateSwitchOnGeneratorStateNode(
|
generator, jump_table->constant_pool_index(), jump_table->size()));
|
WriteSwitch(&node, jump_table);
|
LeaveBasicBlock();
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ResumeGenerator(
|
Register generator, RegisterList registers) {
|
OutputResumeGenerator(generator, registers, registers.register_count());
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkHandler(
|
int handler_id, HandlerTable::CatchPrediction catch_prediction) {
|
BytecodeLabel handler;
|
Bind(&handler);
|
handler_table_builder()->SetHandlerTarget(handler_id, handler.offset());
|
handler_table_builder()->SetPrediction(handler_id, catch_prediction);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryBegin(int handler_id,
|
Register context) {
|
BytecodeLabel try_begin;
|
Bind(&try_begin);
|
handler_table_builder()->SetTryRegionStart(handler_id, try_begin.offset());
|
handler_table_builder()->SetContextRegister(handler_id, context);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::MarkTryEnd(int handler_id) {
|
BytecodeLabel try_end;
|
Bind(&try_end);
|
handler_table_builder()->SetTryRegionEnd(handler_id, try_end.offset());
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallProperty(Register callable,
|
RegisterList args,
|
int feedback_slot) {
|
if (args.register_count() == 1) {
|
OutputCallProperty0(callable, args[0], feedback_slot);
|
} else if (args.register_count() == 2) {
|
OutputCallProperty1(callable, args[0], args[1], feedback_slot);
|
} else if (args.register_count() == 3) {
|
OutputCallProperty2(callable, args[0], args[1], args[2], feedback_slot);
|
} else {
|
OutputCallProperty(callable, args, args.register_count(), feedback_slot);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallUndefinedReceiver(
|
Register callable, RegisterList args, int feedback_slot) {
|
if (args.register_count() == 0) {
|
OutputCallUndefinedReceiver0(callable, feedback_slot);
|
} else if (args.register_count() == 1) {
|
OutputCallUndefinedReceiver1(callable, args[0], feedback_slot);
|
} else if (args.register_count() == 2) {
|
OutputCallUndefinedReceiver2(callable, args[0], args[1], feedback_slot);
|
} else {
|
OutputCallUndefinedReceiver(callable, args, args.register_count(),
|
feedback_slot);
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallAnyReceiver(Register callable,
|
RegisterList args,
|
int feedback_slot) {
|
OutputCallAnyReceiver(callable, args, args.register_count(), feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallWithSpread(Register callable,
|
RegisterList args,
|
int feedback_slot) {
|
OutputCallWithSpread(callable, args, args.register_count(), feedback_slot);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Construct(Register constructor,
|
RegisterList args,
|
int feedback_slot_id) {
|
OutputConstruct(constructor, args, args.register_count(), feedback_slot_id);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::ConstructWithSpread(
|
Register constructor, RegisterList args, int feedback_slot_id) {
|
OutputConstructWithSpread(constructor, args, args.register_count(),
|
feedback_slot_id);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
Runtime::FunctionId function_id, RegisterList args) {
|
DCHECK_EQ(1, Runtime::FunctionForId(function_id)->result_size);
|
DCHECK_LE(Bytecodes::SizeForUnsignedOperand(function_id),
|
OperandSize::kShort);
|
if (IntrinsicsHelper::IsSupported(function_id)) {
|
IntrinsicsHelper::IntrinsicId intrinsic_id =
|
IntrinsicsHelper::FromRuntimeId(function_id);
|
OutputInvokeIntrinsic(static_cast<int>(intrinsic_id), args,
|
args.register_count());
|
} else {
|
OutputCallRuntime(static_cast<int>(function_id), args,
|
args.register_count());
|
}
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
Runtime::FunctionId function_id, Register arg) {
|
return CallRuntime(function_id, RegisterList(arg));
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
|
Runtime::FunctionId function_id) {
|
return CallRuntime(function_id, RegisterList());
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
|
Runtime::FunctionId function_id, RegisterList args,
|
RegisterList return_pair) {
|
DCHECK_EQ(2, Runtime::FunctionForId(function_id)->result_size);
|
DCHECK_LE(Bytecodes::SizeForUnsignedOperand(function_id),
|
OperandSize::kShort);
|
DCHECK_EQ(2, return_pair.register_count());
|
OutputCallRuntimeForPair(static_cast<uint16_t>(function_id), args,
|
args.register_count(), return_pair);
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntimeForPair(
|
Runtime::FunctionId function_id, Register arg, RegisterList return_pair) {
|
return CallRuntimeForPair(function_id, RegisterList(arg), return_pair);
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::CallJSRuntime(int context_index,
|
RegisterList args) {
|
OutputCallJSRuntime(context_index, args, args.register_count());
|
return *this;
|
}
|
|
BytecodeArrayBuilder& BytecodeArrayBuilder::Delete(Register object,
|
LanguageMode language_mode) {
|
if (language_mode == LanguageMode::kSloppy) {
|
OutputDeletePropertySloppy(object);
|
} else {
|
DCHECK_EQ(language_mode, LanguageMode::kStrict);
|
OutputDeletePropertyStrict(object);
|
}
|
return *this;
|
}
|
|
size_t BytecodeArrayBuilder::GetConstantPoolEntry(
|
const AstRawString* raw_string) {
|
return constant_array_builder()->Insert(raw_string);
|
}
|
|
size_t BytecodeArrayBuilder::GetConstantPoolEntry(AstBigInt bigint) {
|
return constant_array_builder()->Insert(bigint);
|
}
|
|
size_t BytecodeArrayBuilder::GetConstantPoolEntry(const Scope* scope) {
|
return constant_array_builder()->Insert(scope);
|
}
|
|
size_t BytecodeArrayBuilder::GetConstantPoolEntry(double number) {
|
return constant_array_builder()->Insert(number);
|
}
|
|
#define ENTRY_GETTER(NAME, ...) \
|
size_t BytecodeArrayBuilder::NAME##ConstantPoolEntry() { \
|
return constant_array_builder()->Insert##NAME(); \
|
}
|
SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_GETTER)
|
#undef ENTRY_GETTER
|
|
BytecodeJumpTable* BytecodeArrayBuilder::AllocateJumpTable(
|
int size, int case_value_base) {
|
DCHECK_GT(size, 0);
|
|
size_t constant_pool_index = constant_array_builder()->InsertJumpTable(size);
|
|
return new (zone())
|
BytecodeJumpTable(constant_pool_index, size, case_value_base, zone());
|
}
|
|
size_t BytecodeArrayBuilder::AllocateDeferredConstantPoolEntry() {
|
return constant_array_builder()->InsertDeferred();
|
}
|
|
void BytecodeArrayBuilder::SetDeferredConstantPoolEntry(size_t entry,
|
Handle<Object> object) {
|
constant_array_builder()->SetDeferredAt(entry, object);
|
}
|
|
bool BytecodeArrayBuilder::RegisterIsValid(Register reg) const {
|
if (!reg.is_valid()) {
|
return false;
|
}
|
|
if (reg.is_current_context() || reg.is_function_closure()) {
|
return true;
|
} else if (reg.is_parameter()) {
|
int parameter_index = reg.ToParameterIndex(parameter_count());
|
return parameter_index >= 0 && parameter_index < parameter_count();
|
} else if (reg.index() < fixed_register_count()) {
|
return true;
|
} else {
|
return register_allocator()->RegisterIsLive(reg);
|
}
|
}
|
|
bool BytecodeArrayBuilder::RegisterListIsValid(RegisterList reg_list) const {
|
if (reg_list.register_count() == 0) {
|
return reg_list.first_register() == Register(0);
|
} else {
|
int first_reg_index = reg_list.first_register().index();
|
for (int i = 0; i < reg_list.register_count(); i++) {
|
if (!RegisterIsValid(Register(first_reg_index + i))) {
|
return false;
|
}
|
}
|
return true;
|
}
|
}
|
|
template <Bytecode bytecode, AccumulatorUse accumulator_use>
|
void BytecodeArrayBuilder::PrepareToOutputBytecode() {
|
if (register_optimizer_)
|
register_optimizer_->PrepareForBytecode<bytecode, accumulator_use>();
|
}
|
|
uint32_t BytecodeArrayBuilder::GetInputRegisterOperand(Register reg) {
|
DCHECK(RegisterIsValid(reg));
|
if (register_optimizer_) reg = register_optimizer_->GetInputRegister(reg);
|
return static_cast<uint32_t>(reg.ToOperand());
|
}
|
|
uint32_t BytecodeArrayBuilder::GetOutputRegisterOperand(Register reg) {
|
DCHECK(RegisterIsValid(reg));
|
if (register_optimizer_) register_optimizer_->PrepareOutputRegister(reg);
|
return static_cast<uint32_t>(reg.ToOperand());
|
}
|
|
uint32_t BytecodeArrayBuilder::GetInputRegisterListOperand(
|
RegisterList reg_list) {
|
DCHECK(RegisterListIsValid(reg_list));
|
if (register_optimizer_)
|
reg_list = register_optimizer_->GetInputRegisterList(reg_list);
|
return static_cast<uint32_t>(reg_list.first_register().ToOperand());
|
}
|
|
uint32_t BytecodeArrayBuilder::GetOutputRegisterListOperand(
|
RegisterList reg_list) {
|
DCHECK(RegisterListIsValid(reg_list));
|
if (register_optimizer_)
|
register_optimizer_->PrepareOutputRegisterList(reg_list);
|
return static_cast<uint32_t>(reg_list.first_register().ToOperand());
|
}
|
|
std::ostream& operator<<(std::ostream& os,
|
const BytecodeArrayBuilder::ToBooleanMode& mode) {
|
switch (mode) {
|
case BytecodeArrayBuilder::ToBooleanMode::kAlreadyBoolean:
|
return os << "AlreadyBoolean";
|
case BytecodeArrayBuilder::ToBooleanMode::kConvertToBoolean:
|
return os << "ConvertToBoolean";
|
}
|
UNREACHABLE();
|
}
|
|
} // namespace interpreter
|
} // namespace internal
|
} // namespace v8
|