blob: 30e2baf4467582d655b7b2d7440de421ffb6c255 [file] [log] [blame]
// Copyright 2021 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.
// TODO(v8:11421): Remove #if once baseline compiler is ported to other
// architectures.
#include "src/flags/flags.h"
#if ENABLE_SPARKPLUG
#include <algorithm>
#include <type_traits>
#include "src/base/bits.h"
#include "src/baseline/baseline-assembler-inl.h"
#include "src/baseline/baseline-assembler.h"
#include "src/baseline/baseline-compiler.h"
#include "src/builtins/builtins-constructor.h"
#include "src/builtins/builtins-descriptors.h"
#include "src/builtins/builtins.h"
#include "src/codegen/assembler.h"
#include "src/codegen/compiler.h"
#include "src/codegen/interface-descriptors-inl.h"
#include "src/codegen/machine-type.h"
#include "src/codegen/macro-assembler-inl.h"
#include "src/common/globals.h"
#include "src/execution/frame-constants.h"
#include "src/heap/local-factory-inl.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-flags.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/code.h"
#include "src/objects/heap-object.h"
#include "src/objects/instance-type.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/roots/roots.h"
#if V8_TARGET_ARCH_X64
#include "src/baseline/x64/baseline-compiler-x64-inl.h"
#elif V8_TARGET_ARCH_ARM64
#include "src/baseline/arm64/baseline-compiler-arm64-inl.h"
#elif V8_TARGET_ARCH_IA32
#include "src/baseline/ia32/baseline-compiler-ia32-inl.h"
#elif V8_TARGET_ARCH_ARM
#include "src/baseline/arm/baseline-compiler-arm-inl.h"
#elif V8_TARGET_ARCH_PPC64
#include "src/baseline/ppc/baseline-compiler-ppc-inl.h"
#elif V8_TARGET_ARCH_S390X
#include "src/baseline/s390/baseline-compiler-s390-inl.h"
#elif V8_TARGET_ARCH_RISCV64
#include "src/baseline/riscv/baseline-compiler-riscv-inl.h"
#elif V8_TARGET_ARCH_RISCV32
#include "src/baseline/riscv/baseline-compiler-riscv-inl.h"
#elif V8_TARGET_ARCH_MIPS64
#include "src/baseline/mips64/baseline-compiler-mips64-inl.h"
#elif V8_TARGET_ARCH_LOONG64
#include "src/baseline/loong64/baseline-compiler-loong64-inl.h"
#else
#error Unsupported target architecture.
#endif
namespace v8 {
namespace internal {
namespace baseline {
template <typename IsolateT>
Handle<ByteArray> BytecodeOffsetTableBuilder::ToBytecodeOffsetTable(
IsolateT* isolate) {
if (bytes_.empty()) return isolate->factory()->empty_byte_array();
Handle<ByteArray> table = isolate->factory()->NewByteArray(
static_cast<int>(bytes_.size()), AllocationType::kOld);
MemCopy(table->GetDataStartAddress(), bytes_.data(), bytes_.size());
return table;
}
namespace detail {
#ifdef DEBUG
bool Clobbers(Register target, Register reg) { return target == reg; }
bool Clobbers(Register target, Handle<Object> handle) { return false; }
bool Clobbers(Register target, Smi smi) { return false; }
bool Clobbers(Register target, TaggedIndex index) { return false; }
bool Clobbers(Register target, int32_t imm) { return false; }
bool Clobbers(Register target, RootIndex index) { return false; }
bool Clobbers(Register target, interpreter::Register reg) { return false; }
bool Clobbers(Register target, interpreter::RegisterList list) { return false; }
// We don't know what's inside machine registers or operands, so assume they
// match.
bool MachineTypeMatches(MachineType type, Register reg) { return true; }
bool MachineTypeMatches(MachineType type, MemOperand reg) { return true; }
bool MachineTypeMatches(MachineType type, Handle<HeapObject> handle) {
return type.IsTagged() && !type.IsTaggedSigned();
}
bool MachineTypeMatches(MachineType type, Smi handle) {
return type.IsTagged() && !type.IsTaggedPointer();
}
bool MachineTypeMatches(MachineType type, TaggedIndex handle) {
// TaggedIndex doesn't have a separate type, so check for the same type as for
// Smis.
return type.IsTagged() && !type.IsTaggedPointer();
}
bool MachineTypeMatches(MachineType type, int32_t imm) {
// 32-bit immediates can be used for 64-bit params -- they'll be
// zero-extended.
return type.representation() == MachineRepresentation::kWord32 ||
type.representation() == MachineRepresentation::kWord64;
}
bool MachineTypeMatches(MachineType type, RootIndex index) {
return type.IsTagged() && !type.IsTaggedSigned();
}
bool MachineTypeMatches(MachineType type, interpreter::Register reg) {
return type.IsTagged();
}
template <typename Descriptor, typename... Args>
struct CheckArgsHelper;
template <typename Descriptor>
struct CheckArgsHelper<Descriptor> {
static void Check(BaselineAssembler* masm, int i) {
if (Descriptor::AllowVarArgs()) {
CHECK_GE(i, Descriptor::GetParameterCount());
} else {
CHECK_EQ(i, Descriptor::GetParameterCount());
}
}
};
template <typename Descriptor, typename Arg, typename... Args>
struct CheckArgsHelper<Descriptor, Arg, Args...> {
static void Check(BaselineAssembler* masm, int i, Arg arg, Args... args) {
if (i >= Descriptor::GetParameterCount()) {
CHECK(Descriptor::AllowVarArgs());
return;
}
CHECK(MachineTypeMatches(Descriptor().GetParameterType(i), arg));
CheckArgsHelper<Descriptor, Args...>::Check(masm, i + 1, args...);
}
};
template <typename Descriptor, typename... Args>
struct CheckArgsHelper<Descriptor, interpreter::RegisterList, Args...> {
static void Check(BaselineAssembler* masm, int i,
interpreter::RegisterList list, Args... args) {
for (int reg_index = 0; reg_index < list.register_count();
++reg_index, ++i) {
if (i >= Descriptor::GetParameterCount()) {
CHECK(Descriptor::AllowVarArgs());
return;
}
CHECK(MachineTypeMatches(Descriptor().GetParameterType(i),
list[reg_index]));
}
CheckArgsHelper<Descriptor, Args...>::Check(masm, i, args...);
}
};
template <typename Descriptor, typename... Args>
void CheckArgs(BaselineAssembler* masm, Args... args) {
CheckArgsHelper<Descriptor, Args...>::Check(masm, 0, args...);
}
void CheckSettingDoesntClobber(Register target) {}
template <typename Arg, typename... Args>
void CheckSettingDoesntClobber(Register target, Arg arg, Args... args) {
DCHECK(!Clobbers(target, arg));
CheckSettingDoesntClobber(target, args...);
}
#else // DEBUG
template <typename Descriptor, typename... Args>
void CheckArgs(Args... args) {}
template <typename... Args>
void CheckSettingDoesntClobber(Register target, Args... args) {}
#endif // DEBUG
template <typename Descriptor, int ArgIndex, bool kIsRegister, typename... Args>
struct ArgumentSettingHelper;
template <typename Descriptor, int ArgIndex, bool kIsRegister>
struct ArgumentSettingHelper<Descriptor, ArgIndex, kIsRegister> {
static void Set(BaselineAssembler* masm) {
// Should only ever be called for the end of register arguments.
static_assert(ArgIndex == Descriptor::GetRegisterParameterCount());
}
};
template <typename Descriptor, int ArgIndex, typename Arg, typename... Args>
struct ArgumentSettingHelper<Descriptor, ArgIndex, true, Arg, Args...> {
static void Set(BaselineAssembler* masm, Arg arg, Args... args) {
static_assert(ArgIndex < Descriptor::GetRegisterParameterCount());
Register target = Descriptor::GetRegisterParameter(ArgIndex);
CheckSettingDoesntClobber(target, args...);
masm->Move(target, arg);
ArgumentSettingHelper<Descriptor, ArgIndex + 1,
(ArgIndex + 1 <
Descriptor::GetRegisterParameterCount()),
Args...>::Set(masm, args...);
}
};
template <typename Descriptor, int ArgIndex>
struct ArgumentSettingHelper<Descriptor, ArgIndex, true,
interpreter::RegisterList> {
static void Set(BaselineAssembler* masm, interpreter::RegisterList list) {
static_assert(ArgIndex < Descriptor::GetRegisterParameterCount());
DCHECK_EQ(ArgIndex + list.register_count(),
Descriptor::GetRegisterParameterCount());
for (int i = 0; ArgIndex + i < Descriptor::GetRegisterParameterCount();
++i) {
Register target = Descriptor::GetRegisterParameter(ArgIndex + i);
masm->Move(target, masm->RegisterFrameOperand(list[i]));
}
}
};
template <typename Descriptor, int ArgIndex, typename Arg, typename... Args>
struct ArgumentSettingHelper<Descriptor, ArgIndex, false, Arg, Args...> {
static void Set(BaselineAssembler* masm, Arg arg, Args... args) {
if (Descriptor::kStackArgumentOrder == StackArgumentOrder::kDefault) {
masm->Push(arg, args...);
} else {
masm->PushReverse(arg, args...);
}
}
};
template <Builtin kBuiltin, typename... Args>
void MoveArgumentsForBuiltin(BaselineAssembler* masm, Args... args) {
using Descriptor = typename CallInterfaceDescriptorFor<kBuiltin>::type;
CheckArgs<Descriptor>(masm, args...);
ArgumentSettingHelper<Descriptor, 0,
(0 < Descriptor::GetRegisterParameterCount()),
Args...>::Set(masm, args...);
if (Descriptor::HasContextParameter()) {
masm->LoadContext(Descriptor::ContextRegister());
}
}
} // namespace detail
namespace {
AssemblerOptions BaselineAssemblerOptions(Isolate* isolate) {
AssemblerOptions options = AssemblerOptions::Default(isolate);
options.builtin_call_jump_mode =
isolate->is_short_builtin_calls_enabled()
? BuiltinCallJumpMode::kPCRelative
: kFallbackBuiltinCallJumpModeForBaseline;
return options;
}
// Rough upper-bound estimate. Copying the data is most likely more expensive
// than pre-allocating a large enough buffer.
#ifdef V8_TARGET_ARCH_IA32
const int kAverageBytecodeToInstructionRatio = 5;
#else
const int kAverageBytecodeToInstructionRatio = 7;
#endif
std::unique_ptr<AssemblerBuffer> AllocateBuffer(
Handle<BytecodeArray> bytecodes) {
int estimated_size;
{
DisallowHeapAllocation no_gc;
estimated_size = BaselineCompiler::EstimateInstructionSize(*bytecodes);
}
return NewAssemblerBuffer(RoundUp(estimated_size, 4 * KB));
}
} // namespace
BaselineCompiler::BaselineCompiler(
LocalIsolate* local_isolate,
Handle<SharedFunctionInfo> shared_function_info,
Handle<BytecodeArray> bytecode)
: local_isolate_(local_isolate),
stats_(local_isolate->runtime_call_stats()),
shared_function_info_(shared_function_info),
bytecode_(bytecode),
masm_(
local_isolate->GetMainThreadIsolateUnsafe(),
BaselineAssemblerOptions(local_isolate->GetMainThreadIsolateUnsafe()),
CodeObjectRequired::kNo, AllocateBuffer(bytecode)),
basm_(&masm_),
iterator_(bytecode_),
zone_(local_isolate->allocator(), ZONE_NAME),
labels_(zone_.NewArray<BaselineLabelPointer>(bytecode_->length())) {
MemsetPointer(reinterpret_cast<Address*>(labels_), Address{0},
bytecode_->length());
// Empirically determined expected size of the offset table at the 95th %ile,
// based on the size of the bytecode, to be:
//
// 16 + (bytecode size) / 4
bytecode_offset_table_builder_.Reserve(
base::bits::RoundUpToPowerOfTwo(16 + bytecode_->Size() / 4));
}
#define __ basm_.
#define RCS_BASELINE_SCOPE(rcs) \
RCS_SCOPE(stats_, \
local_isolate_->is_main_thread() \
? RuntimeCallCounterId::kCompileBaseline##rcs \
: RuntimeCallCounterId::kCompileBackgroundBaseline##rcs)
void BaselineCompiler::GenerateCode() {
{
RCS_BASELINE_SCOPE(PreVisit);
// Mark exception handlers as valid indirect jump targets. This is required
// when CFI is enabled, to allow indirect jumps into baseline code.
HandlerTable table(*bytecode_);
for (int i = 0; i < table.NumberOfRangeEntries(); ++i) {
labels_[table.GetRangeHandler(i)].MarkAsIndirectJumpTarget();
}
for (; !iterator_.done(); iterator_.Advance()) {
PreVisitSingleBytecode();
}
iterator_.Reset();
}
// No code generated yet.
DCHECK_EQ(__ pc_offset(), 0);
__ CodeEntry();
{
RCS_BASELINE_SCOPE(Visit);
Prologue();
AddPosition();
for (; !iterator_.done(); iterator_.Advance()) {
VisitSingleBytecode();
AddPosition();
}
}
}
MaybeHandle<Code> BaselineCompiler::Build(LocalIsolate* local_isolate) {
CodeDesc desc;
__ GetCode(local_isolate->GetMainThreadIsolateUnsafe(), &desc);
// Allocate the bytecode offset table.
Handle<ByteArray> bytecode_offset_table =
bytecode_offset_table_builder_.ToBytecodeOffsetTable(local_isolate);
Factory::CodeBuilder code_builder(local_isolate, desc, CodeKind::BASELINE);
code_builder.set_bytecode_offset_table(bytecode_offset_table);
if (shared_function_info_->HasInterpreterData()) {
code_builder.set_interpreter_data(
handle(shared_function_info_->interpreter_data(), local_isolate));
} else {
code_builder.set_interpreter_data(bytecode_);
}
return code_builder.TryBuild();
}
int BaselineCompiler::EstimateInstructionSize(BytecodeArray bytecode) {
return bytecode.length() * kAverageBytecodeToInstructionRatio;
}
interpreter::Register BaselineCompiler::RegisterOperand(int operand_index) {
return iterator().GetRegisterOperand(operand_index);
}
void BaselineCompiler::LoadRegister(Register output, int operand_index) {
__ LoadRegister(output, RegisterOperand(operand_index));
}
void BaselineCompiler::StoreRegister(int operand_index, Register value) {
__ Move(RegisterOperand(operand_index), value);
}
void BaselineCompiler::StoreRegisterPair(int operand_index, Register val0,
Register val1) {
interpreter::Register reg0, reg1;
std::tie(reg0, reg1) = iterator().GetRegisterPairOperand(operand_index);
__ StoreRegister(reg0, val0);
__ StoreRegister(reg1, val1);
}
template <typename Type>
Handle<Type> BaselineCompiler::Constant(int operand_index) {
return Handle<Type>::cast(
iterator().GetConstantForIndexOperand(operand_index, local_isolate_));
}
Smi BaselineCompiler::ConstantSmi(int operand_index) {
return iterator().GetConstantAtIndexAsSmi(operand_index);
}
template <typename Type>
void BaselineCompiler::LoadConstant(Register output, int operand_index) {
__ Move(output, Constant<Type>(operand_index));
}
uint32_t BaselineCompiler::Uint(int operand_index) {
return iterator().GetUnsignedImmediateOperand(operand_index);
}
int32_t BaselineCompiler::Int(int operand_index) {
return iterator().GetImmediateOperand(operand_index);
}
uint32_t BaselineCompiler::Index(int operand_index) {
return iterator().GetIndexOperand(operand_index);
}
uint32_t BaselineCompiler::Flag8(int operand_index) {
return iterator().GetFlag8Operand(operand_index);
}
uint32_t BaselineCompiler::Flag16(int operand_index) {
return iterator().GetFlag16Operand(operand_index);
}
uint32_t BaselineCompiler::RegisterCount(int operand_index) {
return iterator().GetRegisterCountOperand(operand_index);
}
TaggedIndex BaselineCompiler::IndexAsTagged(int operand_index) {
return TaggedIndex::FromIntptr(Index(operand_index));
}
TaggedIndex BaselineCompiler::UintAsTagged(int operand_index) {
return TaggedIndex::FromIntptr(Uint(operand_index));
}
Smi BaselineCompiler::IndexAsSmi(int operand_index) {
return Smi::FromInt(Index(operand_index));
}
Smi BaselineCompiler::IntAsSmi(int operand_index) {
return Smi::FromInt(Int(operand_index));
}
Smi BaselineCompiler::Flag8AsSmi(int operand_index) {
return Smi::FromInt(Flag8(operand_index));
}
Smi BaselineCompiler::Flag16AsSmi(int operand_index) {
return Smi::FromInt(Flag16(operand_index));
}
MemOperand BaselineCompiler::FeedbackVector() {
return __ FeedbackVectorOperand();
}
void BaselineCompiler::LoadFeedbackVector(Register output) {
ASM_CODE_COMMENT(&masm_);
__ Move(output, __ FeedbackVectorOperand());
}
void BaselineCompiler::LoadClosureFeedbackArray(Register output) {
LoadFeedbackVector(output);
__ LoadTaggedField(output, output,
FeedbackVector::kClosureFeedbackCellArrayOffset);
}
void BaselineCompiler::SelectBooleanConstant(
Register output, std::function<void(Label*, Label::Distance)> jump_func) {
Label done, set_true;
jump_func(&set_true, Label::kNear);
__ LoadRoot(output, RootIndex::kFalseValue);
__ Jump(&done, Label::kNear);
__ Bind(&set_true);
__ LoadRoot(output, RootIndex::kTrueValue);
__ Bind(&done);
}
void BaselineCompiler::AddPosition() {
bytecode_offset_table_builder_.AddPosition(__ pc_offset());
}
void BaselineCompiler::PreVisitSingleBytecode() {
switch (iterator().current_bytecode()) {
case interpreter::Bytecode::kJumpLoop:
EnsureLabel(iterator().GetJumpTargetOffset(),
MarkAsIndirectJumpTarget::kYes);
break;
// TODO(leszeks): Update the max_call_args as part of the main bytecode
// visit loop, by patching the value passed to the prologue.
case interpreter::Bytecode::kCallProperty:
case interpreter::Bytecode::kCallAnyReceiver:
case interpreter::Bytecode::kCallWithSpread:
case interpreter::Bytecode::kConstruct:
case interpreter::Bytecode::kConstructWithSpread:
return UpdateMaxCallArgs(
iterator().GetRegisterListOperand(1).register_count());
case interpreter::Bytecode::kCallUndefinedReceiver:
return UpdateMaxCallArgs(
iterator().GetRegisterListOperand(1).register_count() + 1);
case interpreter::Bytecode::kCallProperty0:
case interpreter::Bytecode::kCallUndefinedReceiver0:
return UpdateMaxCallArgs(1);
case interpreter::Bytecode::kCallProperty1:
case interpreter::Bytecode::kCallUndefinedReceiver1:
return UpdateMaxCallArgs(2);
case interpreter::Bytecode::kCallProperty2:
case interpreter::Bytecode::kCallUndefinedReceiver2:
return UpdateMaxCallArgs(3);
default:
break;
}
}
void BaselineCompiler::VisitSingleBytecode() {
int offset = iterator().current_offset();
BaselineLabelPointer label = labels_[offset];
if (label.GetPointer()) __ Bind(label.GetPointer());
// Mark position as valid jump target unconditionnaly when the deoptimizer can
// jump to baseline code. This is required when CFI is enabled.
if (v8_flags.deopt_to_baseline || label.IsIndirectJumpTarget()) {
__ JumpTarget();
}
#ifdef V8_CODE_COMMENTS
std::ostringstream str;
if (v8_flags.code_comments) {
iterator().PrintTo(str);
}
ASM_CODE_COMMENT_STRING(&masm_, str.str());
#endif
VerifyFrame();
#ifdef V8_TRACE_UNOPTIMIZED
TraceBytecode(Runtime::kTraceUnoptimizedBytecodeEntry);
#endif
{
interpreter::Bytecode bytecode = iterator().current_bytecode();
#ifdef DEBUG
base::Optional<EnsureAccumulatorPreservedScope> accumulator_preserved_scope;
// We should make sure to preserve the accumulator whenever the bytecode
// isn't registered as writing to it. We can't do this for jumps or switches
// though, since the control flow would not match the control flow of this
// scope.
if (v8_flags.debug_code &&
!interpreter::Bytecodes::WritesOrClobbersAccumulator(bytecode) &&
!interpreter::Bytecodes::IsJump(bytecode) &&
!interpreter::Bytecodes::IsSwitch(bytecode)) {
accumulator_preserved_scope.emplace(&basm_);
}
#endif // DEBUG
switch (bytecode) {
#define BYTECODE_CASE(name, ...) \
case interpreter::Bytecode::k##name: \
Visit##name(); \
break;
BYTECODE_LIST(BYTECODE_CASE)
#undef BYTECODE_CASE
}
}
#ifdef V8_TRACE_UNOPTIMIZED
TraceBytecode(Runtime::kTraceUnoptimizedBytecodeExit);
#endif
}
void BaselineCompiler::VerifyFrame() {
if (v8_flags.debug_code) {
ASM_CODE_COMMENT(&masm_);
__ RecordComment(" -- Verify frame size");
VerifyFrameSize();
__ RecordComment(" -- Verify feedback vector");
{
BaselineAssembler::ScratchRegisterScope temps(&basm_);
Register scratch = temps.AcquireScratch();
__ Move(scratch, __ FeedbackVectorOperand());
Label is_smi, is_ok;
__ JumpIfSmi(scratch, &is_smi);
__ JumpIfObjectTypeFast(kEqual, scratch, FEEDBACK_VECTOR_TYPE, &is_ok);
__ Bind(&is_smi);
__ masm()->Abort(AbortReason::kExpectedFeedbackVector);
__ Bind(&is_ok);
}
// TODO(leszeks): More verification.
}
}
#ifdef V8_TRACE_UNOPTIMIZED
void BaselineCompiler::TraceBytecode(Runtime::FunctionId function_id) {
if (!v8_flags.trace_baseline_exec) return;
ASM_CODE_COMMENT_STRING(&masm_,
function_id == Runtime::kTraceUnoptimizedBytecodeEntry
? "Trace bytecode entry"
: "Trace bytecode exit");
SaveAccumulatorScope accumulator_scope(&basm_);
CallRuntime(function_id, bytecode_,
Smi::FromInt(BytecodeArray::kHeaderSize - kHeapObjectTag +
iterator().current_offset()),
kInterpreterAccumulatorRegister);
}
#endif
#define DECLARE_VISITOR(name, ...) void Visit##name();
BYTECODE_LIST(DECLARE_VISITOR)
#undef DECLARE_VISITOR
#define DECLARE_VISITOR(name, ...) \
void VisitIntrinsic##name(interpreter::RegisterList args);
INTRINSICS_LIST(DECLARE_VISITOR)
#undef DECLARE_VISITOR
void BaselineCompiler::UpdateInterruptBudgetAndJumpToLabel(
int weight, Label* label, Label* skip_interrupt_label) {
if (weight != 0) {
ASM_CODE_COMMENT(&masm_);
__ AddToInterruptBudgetAndJumpIfNotExceeded(weight, skip_interrupt_label);
DCHECK_LT(weight, 0);
SaveAccumulatorScope accumulator_scope(&basm_);
CallRuntime(Runtime::kBytecodeBudgetInterruptWithStackCheck_Sparkplug,
__ FunctionOperand());
}
if (label) __ Jump(label);
}
void BaselineCompiler::JumpIfRoot(RootIndex root) {
Label dont_jump;
__ JumpIfNotRoot(kInterpreterAccumulatorRegister, root, &dont_jump,
Label::kNear);
__ Jump(BuildForwardJumpLabel());
__ Bind(&dont_jump);
}
void BaselineCompiler::JumpIfNotRoot(RootIndex root) {
Label dont_jump;
__ JumpIfRoot(kInterpreterAccumulatorRegister, root, &dont_jump,
Label::kNear);
__ Jump(BuildForwardJumpLabel());
__ Bind(&dont_jump);
}
Label* BaselineCompiler::BuildForwardJumpLabel() {
int target_offset = iterator().GetJumpTargetOffset();
return EnsureLabel(target_offset);
}
template <Builtin kBuiltin, typename... Args>
void BaselineCompiler::CallBuiltin(Args... args) {
ASM_CODE_COMMENT(&masm_);
detail::MoveArgumentsForBuiltin<kBuiltin>(&basm_, args...);
__ CallBuiltin(kBuiltin);
}
template <Builtin kBuiltin, typename... Args>
void BaselineCompiler::TailCallBuiltin(Args... args) {
detail::MoveArgumentsForBuiltin<kBuiltin>(&basm_, args...);
__ TailCallBuiltin(kBuiltin);
}
template <typename... Args>
void BaselineCompiler::CallRuntime(Runtime::FunctionId function, Args... args) {
__ LoadContext(kContextRegister);
int nargs = __ Push(args...);
__ CallRuntime(function, nargs);
}
// Returns into kInterpreterAccumulatorRegister
void BaselineCompiler::JumpIfToBoolean(bool do_jump_if_true, Label* label,
Label::Distance distance) {
CallBuiltin<Builtin::kToBooleanForBaselineJump>(
kInterpreterAccumulatorRegister);
// ToBooleanForBaselineJump returns the ToBoolean value into return reg 1, and
// the original value into kInterpreterAccumulatorRegister, so we don't have
// to worry about it getting clobbered.
static_assert(kReturnRegister0 == kInterpreterAccumulatorRegister);
__ JumpIfSmi(do_jump_if_true ? kNotEqual : kEqual, kReturnRegister1,
Smi::FromInt(0), label, distance);
}
void BaselineCompiler::VisitLdaZero() {
__ Move(kInterpreterAccumulatorRegister, Smi::FromInt(0));
}
void BaselineCompiler::VisitLdaSmi() {
Smi constant = Smi::FromInt(iterator().GetImmediateOperand(0));
__ Move(kInterpreterAccumulatorRegister, constant);
}
void BaselineCompiler::VisitLdaUndefined() {
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue);
}
void BaselineCompiler::VisitLdaNull() {
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kNullValue);
}
void BaselineCompiler::VisitLdaTheHole() {
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTheHoleValue);
}
void BaselineCompiler::VisitLdaTrue() {
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
}
void BaselineCompiler::VisitLdaFalse() {
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
}
void BaselineCompiler::VisitLdaConstant() {
LoadConstant<HeapObject>(kInterpreterAccumulatorRegister, 0);
}
void BaselineCompiler::VisitLdaGlobal() {
CallBuiltin<Builtin::kLoadGlobalICBaseline>(Constant<Name>(0), // name
IndexAsTagged(1)); // slot
}
void BaselineCompiler::VisitLdaGlobalInsideTypeof() {
CallBuiltin<Builtin::kLoadGlobalICInsideTypeofBaseline>(
Constant<Name>(0), // name
IndexAsTagged(1)); // slot
}
void BaselineCompiler::VisitStaGlobal() {
CallBuiltin<Builtin::kStoreGlobalICBaseline>(
Constant<Name>(0), // name
kInterpreterAccumulatorRegister, // value
IndexAsTagged(1)); // slot
}
void BaselineCompiler::VisitPushContext() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register context = scratch_scope.AcquireScratch();
__ LoadContext(context);
__ StoreContext(kInterpreterAccumulatorRegister);
StoreRegister(0, context);
}
void BaselineCompiler::VisitPopContext() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register context = scratch_scope.AcquireScratch();
LoadRegister(context, 0);
__ StoreContext(context);
}
void BaselineCompiler::VisitLdaContextSlot() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register context = scratch_scope.AcquireScratch();
LoadRegister(context, 0);
uint32_t index = Index(1);
uint32_t depth = Uint(2);
__ LdaContextSlot(context, index, depth);
}
void BaselineCompiler::VisitLdaImmutableContextSlot() { VisitLdaContextSlot(); }
void BaselineCompiler::VisitLdaCurrentContextSlot() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register context = scratch_scope.AcquireScratch();
__ LoadContext(context);
__ LoadTaggedField(kInterpreterAccumulatorRegister, context,
Context::OffsetOfElementAt(Index(0)));
}
void BaselineCompiler::VisitLdaImmutableCurrentContextSlot() {
VisitLdaCurrentContextSlot();
}
void BaselineCompiler::VisitStaContextSlot() {
Register value = WriteBarrierDescriptor::ValueRegister();
Register context = WriteBarrierDescriptor::ObjectRegister();
DCHECK(!AreAliased(value, context, kInterpreterAccumulatorRegister));
__ Move(value, kInterpreterAccumulatorRegister);
LoadRegister(context, 0);
uint32_t index = Index(1);
uint32_t depth = Uint(2);
__ StaContextSlot(context, value, index, depth);
}
void BaselineCompiler::VisitStaCurrentContextSlot() {
Register value = WriteBarrierDescriptor::ValueRegister();
Register context = WriteBarrierDescriptor::ObjectRegister();
DCHECK(!AreAliased(value, context, kInterpreterAccumulatorRegister));
__ Move(value, kInterpreterAccumulatorRegister);
__ LoadContext(context);
__ StoreTaggedFieldWithWriteBarrier(
context, Context::OffsetOfElementAt(Index(0)), value);
}
void BaselineCompiler::VisitLdaLookupSlot() {
CallRuntime(Runtime::kLoadLookupSlot, Constant<Name>(0));
}
void BaselineCompiler::VisitLdaLookupContextSlot() {
CallBuiltin<Builtin::kLookupContextBaseline>(
Constant<Name>(0), UintAsTagged(2), IndexAsTagged(1));
}
void BaselineCompiler::VisitLdaLookupGlobalSlot() {
CallBuiltin<Builtin::kLookupGlobalICBaseline>(
Constant<Name>(0), UintAsTagged(2), IndexAsTagged(1));
}
void BaselineCompiler::VisitLdaLookupSlotInsideTypeof() {
CallRuntime(Runtime::kLoadLookupSlotInsideTypeof, Constant<Name>(0));
}
void BaselineCompiler::VisitLdaLookupContextSlotInsideTypeof() {
CallBuiltin<Builtin::kLookupContextInsideTypeofBaseline>(
Constant<Name>(0), UintAsTagged(2), IndexAsTagged(1));
}
void BaselineCompiler::VisitLdaLookupGlobalSlotInsideTypeof() {
CallBuiltin<Builtin::kLookupGlobalICInsideTypeofBaseline>(
Constant<Name>(0), UintAsTagged(2), IndexAsTagged(1));
}
void BaselineCompiler::VisitStaLookupSlot() {
uint32_t flags = Flag8(1);
Runtime::FunctionId function_id;
if (flags & interpreter::StoreLookupSlotFlags::LanguageModeBit::kMask) {
function_id = Runtime::kStoreLookupSlot_Strict;
} else if (flags &
interpreter::StoreLookupSlotFlags::LookupHoistingModeBit::kMask) {
function_id = Runtime::kStoreLookupSlot_SloppyHoisting;
} else {
function_id = Runtime::kStoreLookupSlot_Sloppy;
}
CallRuntime(function_id, Constant<Name>(0), // name
kInterpreterAccumulatorRegister); // value
}
void BaselineCompiler::VisitLdar() {
LoadRegister(kInterpreterAccumulatorRegister, 0);
}
void BaselineCompiler::VisitStar() {
StoreRegister(0, kInterpreterAccumulatorRegister);
}
#define SHORT_STAR_VISITOR(Name, ...) \
void BaselineCompiler::Visit##Name() { \
__ StoreRegister( \
interpreter::Register::FromShortStar(interpreter::Bytecode::k##Name), \
kInterpreterAccumulatorRegister); \
}
SHORT_STAR_BYTECODE_LIST(SHORT_STAR_VISITOR)
#undef SHORT_STAR_VISITOR
void BaselineCompiler::VisitMov() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register scratch = scratch_scope.AcquireScratch();
LoadRegister(scratch, 0);
StoreRegister(1, scratch);
}
void BaselineCompiler::VisitGetNamedProperty() {
CallBuiltin<Builtin::kLoadICBaseline>(RegisterOperand(0), // object
Constant<Name>(1), // name
IndexAsTagged(2)); // slot
}
void BaselineCompiler::VisitGetNamedPropertyFromSuper() {
__ LoadPrototype(
LoadWithReceiverAndVectorDescriptor::LookupStartObjectRegister(),
kInterpreterAccumulatorRegister);
CallBuiltin<Builtin::kLoadSuperICBaseline>(
RegisterOperand(0), // object
LoadWithReceiverAndVectorDescriptor::
LookupStartObjectRegister(), // lookup start
Constant<Name>(1), // name
IndexAsTagged(2)); // slot
}
void BaselineCompiler::VisitGetKeyedProperty() {
CallBuiltin<Builtin::kKeyedLoadICBaseline>(
RegisterOperand(0), // object
kInterpreterAccumulatorRegister, // key
IndexAsTagged(1)); // slot
}
void BaselineCompiler::VisitLdaModuleVariable() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register scratch = scratch_scope.AcquireScratch();
__ LoadContext(scratch);
int cell_index = Int(0);
int depth = Uint(1);
__ LdaModuleVariable(scratch, cell_index, depth);
}
void BaselineCompiler::VisitStaModuleVariable() {
int cell_index = Int(0);
if (V8_UNLIKELY(cell_index < 0)) {
// Not supported (probably never).
CallRuntime(Runtime::kAbort,
Smi::FromInt(static_cast<int>(
AbortReason::kUnsupportedModuleOperation)));
__ Trap();
}
Register value = WriteBarrierDescriptor::ValueRegister();
Register scratch = WriteBarrierDescriptor::ObjectRegister();
DCHECK(!AreAliased(value, scratch, kInterpreterAccumulatorRegister));
__ Move(value, kInterpreterAccumulatorRegister);
__ LoadContext(scratch);
int depth = Uint(1);
__ StaModuleVariable(scratch, value, cell_index, depth);
}
void BaselineCompiler::VisitSetNamedProperty() {
// StoreIC is currently a base class for multiple property store operations
// and contains mixed logic for named and keyed, set and define operations,
// the paths are controlled by feedback.
// TODO(v8:12548): refactor SetNamedIC as a subclass of StoreIC, which can be
// called here.
CallBuiltin<Builtin::kStoreICBaseline>(
RegisterOperand(0), // object
Constant<Name>(1), // name
kInterpreterAccumulatorRegister, // value
IndexAsTagged(2)); // slot
}
void BaselineCompiler::VisitDefineNamedOwnProperty() {
CallBuiltin<Builtin::kDefineNamedOwnICBaseline>(
RegisterOperand(0), // object
Constant<Name>(1), // name
kInterpreterAccumulatorRegister, // value
IndexAsTagged(2)); // slot
}
void BaselineCompiler::VisitSetKeyedProperty() {
// KeyedStoreIC is currently a base class for multiple keyed property store
// operations and contains mixed logic for set and define operations,
// the paths are controlled by feedback.
// TODO(v8:12548): refactor SetKeyedIC as a subclass of KeyedStoreIC, which
// can be called here.
CallBuiltin<Builtin::kKeyedStoreICBaseline>(
RegisterOperand(0), // object
RegisterOperand(1), // key
kInterpreterAccumulatorRegister, // value
IndexAsTagged(2)); // slot
}
void BaselineCompiler::VisitDefineKeyedOwnProperty() {
CallBuiltin<Builtin::kDefineKeyedOwnICBaseline>(
RegisterOperand(0), // object
RegisterOperand(1), // key
kInterpreterAccumulatorRegister, // value
Flag8AsSmi(2), // flags
IndexAsTagged(3)); // slot
}
void BaselineCompiler::VisitStaInArrayLiteral() {
CallBuiltin<Builtin::kStoreInArrayLiteralICBaseline>(
RegisterOperand(0), // object
RegisterOperand(1), // name
kInterpreterAccumulatorRegister, // value
IndexAsTagged(2)); // slot
}
void BaselineCompiler::VisitDefineKeyedOwnPropertyInLiteral() {
// Here we should save the accumulator, since
// DefineKeyedOwnPropertyInLiteral doesn't write the accumulator, but
// Runtime::kDefineKeyedOwnPropertyInLiteral returns the value that we got
// from the accumulator so this still works.
CallRuntime(Runtime::kDefineKeyedOwnPropertyInLiteral,
RegisterOperand(0), // object
RegisterOperand(1), // name
kInterpreterAccumulatorRegister, // value
Flag8AsSmi(2), // flags
FeedbackVector(), // feedback vector
IndexAsTagged(3)); // slot
}
void BaselineCompiler::VisitAdd() {
CallBuiltin<Builtin::kAdd_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitSub() {
CallBuiltin<Builtin::kSubtract_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitMul() {
CallBuiltin<Builtin::kMultiply_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitDiv() {
CallBuiltin<Builtin::kDivide_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitMod() {
CallBuiltin<Builtin::kModulus_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitExp() {
CallBuiltin<Builtin::kExponentiate_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitBitwiseOr() {
CallBuiltin<Builtin::kBitwiseOr_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitBitwiseXor() {
CallBuiltin<Builtin::kBitwiseXor_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitBitwiseAnd() {
CallBuiltin<Builtin::kBitwiseAnd_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitShiftLeft() {
CallBuiltin<Builtin::kShiftLeft_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitShiftRight() {
CallBuiltin<Builtin::kShiftRight_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitShiftRightLogical() {
CallBuiltin<Builtin::kShiftRightLogical_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitAddSmi() {
CallBuiltin<Builtin::kAddSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitSubSmi() {
CallBuiltin<Builtin::kSubtractSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitMulSmi() {
CallBuiltin<Builtin::kMultiplySmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitDivSmi() {
CallBuiltin<Builtin::kDivideSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitModSmi() {
CallBuiltin<Builtin::kModulusSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitExpSmi() {
CallBuiltin<Builtin::kExponentiateSmi_Baseline>(
kInterpreterAccumulatorRegister, IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitBitwiseOrSmi() {
CallBuiltin<Builtin::kBitwiseOrSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitBitwiseXorSmi() {
CallBuiltin<Builtin::kBitwiseXorSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitBitwiseAndSmi() {
CallBuiltin<Builtin::kBitwiseAndSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitShiftLeftSmi() {
CallBuiltin<Builtin::kShiftLeftSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitShiftRightSmi() {
CallBuiltin<Builtin::kShiftRightSmi_Baseline>(kInterpreterAccumulatorRegister,
IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitShiftRightLogicalSmi() {
CallBuiltin<Builtin::kShiftRightLogicalSmi_Baseline>(
kInterpreterAccumulatorRegister, IntAsSmi(0), Index(1));
}
void BaselineCompiler::VisitInc() {
CallBuiltin<Builtin::kIncrement_Baseline>(kInterpreterAccumulatorRegister,
Index(0));
}
void BaselineCompiler::VisitDec() {
CallBuiltin<Builtin::kDecrement_Baseline>(kInterpreterAccumulatorRegister,
Index(0));
}
void BaselineCompiler::VisitNegate() {
CallBuiltin<Builtin::kNegate_Baseline>(kInterpreterAccumulatorRegister,
Index(0));
}
void BaselineCompiler::VisitBitwiseNot() {
CallBuiltin<Builtin::kBitwiseNot_Baseline>(kInterpreterAccumulatorRegister,
Index(0));
}
void BaselineCompiler::VisitToBooleanLogicalNot() {
SelectBooleanConstant(kInterpreterAccumulatorRegister,
[&](Label* if_true, Label::Distance distance) {
JumpIfToBoolean(false, if_true, distance);
});
}
void BaselineCompiler::VisitLogicalNot() {
SelectBooleanConstant(kInterpreterAccumulatorRegister,
[&](Label* if_true, Label::Distance distance) {
__ JumpIfRoot(kInterpreterAccumulatorRegister,
RootIndex::kFalseValue, if_true,
distance);
});
}
void BaselineCompiler::VisitTypeOf() {
CallBuiltin<Builtin::kTypeof>(kInterpreterAccumulatorRegister);
}
void BaselineCompiler::VisitDeletePropertyStrict() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register scratch = scratch_scope.AcquireScratch();
__ Move(scratch, kInterpreterAccumulatorRegister);
CallBuiltin<Builtin::kDeleteProperty>(RegisterOperand(0), scratch,
Smi::FromEnum(LanguageMode::kStrict));
}
void BaselineCompiler::VisitDeletePropertySloppy() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register scratch = scratch_scope.AcquireScratch();
__ Move(scratch, kInterpreterAccumulatorRegister);
CallBuiltin<Builtin::kDeleteProperty>(RegisterOperand(0), scratch,
Smi::FromEnum(LanguageMode::kSloppy));
}
void BaselineCompiler::VisitGetSuperConstructor() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register prototype = scratch_scope.AcquireScratch();
__ LoadPrototype(prototype, kInterpreterAccumulatorRegister);
StoreRegister(0, prototype);
}
void BaselineCompiler::VisitFindNonDefaultConstructorOrConstruct() {
SaveAccumulatorScope accumulator_scope(&basm_);
CallBuiltin<Builtin::kFindNonDefaultConstructorOrConstruct>(
RegisterOperand(0), RegisterOperand(1));
StoreRegisterPair(2, kReturnRegister0, kReturnRegister1);
}
namespace {
constexpr Builtin ConvertReceiverModeToCompactBuiltin(
ConvertReceiverMode mode) {
switch (mode) {
case ConvertReceiverMode::kAny:
return Builtin::kCall_ReceiverIsAny_Baseline_Compact;
case ConvertReceiverMode::kNullOrUndefined:
return Builtin::kCall_ReceiverIsNullOrUndefined_Baseline_Compact;
case ConvertReceiverMode::kNotNullOrUndefined:
return Builtin::kCall_ReceiverIsNotNullOrUndefined_Baseline_Compact;
}
}
constexpr Builtin ConvertReceiverModeToBuiltin(ConvertReceiverMode mode) {
switch (mode) {
case ConvertReceiverMode::kAny:
return Builtin::kCall_ReceiverIsAny_Baseline;
case ConvertReceiverMode::kNullOrUndefined:
return Builtin::kCall_ReceiverIsNullOrUndefined_Baseline;
case ConvertReceiverMode::kNotNullOrUndefined:
return Builtin::kCall_ReceiverIsNotNullOrUndefined_Baseline;
}
}
} // namespace
template <ConvertReceiverMode kMode, typename... Args>
void BaselineCompiler::BuildCall(uint32_t slot, uint32_t arg_count,
Args... args) {
uint32_t bitfield;
if (CallTrampoline_Baseline_CompactDescriptor::EncodeBitField(arg_count, slot,
&bitfield)) {
CallBuiltin<ConvertReceiverModeToCompactBuiltin(kMode)>(
RegisterOperand(0), // kFunction
bitfield, // kActualArgumentsCount | kSlot
args...); // Arguments
} else {
CallBuiltin<ConvertReceiverModeToBuiltin(kMode)>(
RegisterOperand(0), // kFunction
arg_count, // kActualArgumentsCount
slot, // kSlot
args...); // Arguments
}
}
void BaselineCompiler::VisitCallAnyReceiver() {
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
uint32_t arg_count = args.register_count();
BuildCall<ConvertReceiverMode::kAny>(Index(3), arg_count, args);
}
void BaselineCompiler::VisitCallProperty() {
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
uint32_t arg_count = args.register_count();
BuildCall<ConvertReceiverMode::kNotNullOrUndefined>(Index(3), arg_count,
args);
}
void BaselineCompiler::VisitCallProperty0() {
BuildCall<ConvertReceiverMode::kNotNullOrUndefined>(
Index(2), JSParameterCount(0), RegisterOperand(1));
}
void BaselineCompiler::VisitCallProperty1() {
BuildCall<ConvertReceiverMode::kNotNullOrUndefined>(
Index(3), JSParameterCount(1), RegisterOperand(1), RegisterOperand(2));
}
void BaselineCompiler::VisitCallProperty2() {
BuildCall<ConvertReceiverMode::kNotNullOrUndefined>(
Index(4), JSParameterCount(2), RegisterOperand(1), RegisterOperand(2),
RegisterOperand(3));
}
void BaselineCompiler::VisitCallUndefinedReceiver() {
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
uint32_t arg_count = JSParameterCount(args.register_count());
BuildCall<ConvertReceiverMode::kNullOrUndefined>(
Index(3), arg_count, RootIndex::kUndefinedValue, args);
}
void BaselineCompiler::VisitCallUndefinedReceiver0() {
BuildCall<ConvertReceiverMode::kNullOrUndefined>(
Index(1), JSParameterCount(0), RootIndex::kUndefinedValue);
}
void BaselineCompiler::VisitCallUndefinedReceiver1() {
BuildCall<ConvertReceiverMode::kNullOrUndefined>(
Index(2), JSParameterCount(1), RootIndex::kUndefinedValue,
RegisterOperand(1));
}
void BaselineCompiler::VisitCallUndefinedReceiver2() {
BuildCall<ConvertReceiverMode::kNullOrUndefined>(
Index(3), JSParameterCount(2), RootIndex::kUndefinedValue,
RegisterOperand(1), RegisterOperand(2));
}
void BaselineCompiler::VisitCallWithSpread() {
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
// Do not push the spread argument
interpreter::Register spread_register = args.last_register();
args = args.Truncate(args.register_count() - 1);
uint32_t arg_count = args.register_count();
CallBuiltin<Builtin::kCallWithSpread_Baseline>(
RegisterOperand(0), // kFunction
arg_count, // kActualArgumentsCount
spread_register, // kSpread
Index(3), // kSlot
args);
}
void BaselineCompiler::VisitCallRuntime() {
CallRuntime(iterator().GetRuntimeIdOperand(0),
iterator().GetRegisterListOperand(1));
}
void BaselineCompiler::VisitCallRuntimeForPair() {
SaveAccumulatorScope accumulator_scope(&basm_);
CallRuntime(iterator().GetRuntimeIdOperand(0),
iterator().GetRegisterListOperand(1));
StoreRegisterPair(3, kReturnRegister0, kReturnRegister1);
}
void BaselineCompiler::VisitCallJSRuntime() {
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
uint32_t arg_count = JSParameterCount(args.register_count());
// Load context for LoadNativeContextSlot.
__ LoadContext(kContextRegister);
__ LoadNativeContextSlot(kJavaScriptCallTargetRegister,
iterator().GetNativeContextIndexOperand(0));
CallBuiltin<Builtin::kCall_ReceiverIsNullOrUndefined>(
kJavaScriptCallTargetRegister, // kFunction
arg_count, // kActualArgumentsCount
RootIndex::kUndefinedValue, // kReceiver
args);
}
void BaselineCompiler::VisitInvokeIntrinsic() {
Runtime::FunctionId intrinsic_id = iterator().GetIntrinsicIdOperand(0);
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
switch (intrinsic_id) {
#define CASE(Name, ...) \
case Runtime::kInline##Name: \
VisitIntrinsic##Name(args); \
break;
INTRINSICS_LIST(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
void BaselineCompiler::VisitIntrinsicCopyDataProperties(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kCopyDataProperties>(args);
}
void BaselineCompiler::
VisitIntrinsicCopyDataPropertiesWithExcludedPropertiesOnStack(
interpreter::RegisterList args) {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register rscratch = scratch_scope.AcquireScratch();
// Use an offset from args[0] instead of args[1] to pass a valid "end of"
// pointer in the case where args.register_count() == 1.
basm_.RegisterFrameAddress(interpreter::Register(args[0].index() + 1),
rscratch);
CallBuiltin<Builtin::kCopyDataPropertiesWithExcludedPropertiesOnStack>(
args[0], args.register_count() - 1, rscratch);
}
void BaselineCompiler::VisitIntrinsicCreateIterResultObject(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kCreateIterResultObject>(args);
}
void BaselineCompiler::VisitIntrinsicCreateAsyncFromSyncIterator(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kCreateAsyncFromSyncIteratorBaseline>(args[0]);
}
void BaselineCompiler::VisitIntrinsicCreateJSGeneratorObject(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kCreateGeneratorObject>(args);
}
void BaselineCompiler::VisitIntrinsicGeneratorGetResumeMode(
interpreter::RegisterList args) {
__ LoadRegister(kInterpreterAccumulatorRegister, args[0]);
__ LoadTaggedField(kInterpreterAccumulatorRegister,
kInterpreterAccumulatorRegister,
JSGeneratorObject::kResumeModeOffset);
}
void BaselineCompiler::VisitIntrinsicGeneratorClose(
interpreter::RegisterList args) {
__ LoadRegister(kInterpreterAccumulatorRegister, args[0]);
__ StoreTaggedSignedField(kInterpreterAccumulatorRegister,
JSGeneratorObject::kContinuationOffset,
Smi::FromInt(JSGeneratorObject::kGeneratorClosed));
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue);
}
void BaselineCompiler::VisitIntrinsicGetImportMetaObject(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kGetImportMetaObjectBaseline>();
}
void BaselineCompiler::VisitIntrinsicAsyncFunctionAwaitCaught(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncFunctionAwaitCaught>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncFunctionAwaitUncaught(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncFunctionAwaitUncaught>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncFunctionEnter(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncFunctionEnter>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncFunctionReject(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncFunctionReject>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncFunctionResolve(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncFunctionResolve>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncGeneratorAwaitCaught(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncGeneratorAwaitCaught>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncGeneratorAwaitUncaught(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncGeneratorAwaitUncaught>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncGeneratorReject(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncGeneratorReject>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncGeneratorResolve(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncGeneratorResolve>(args);
}
void BaselineCompiler::VisitIntrinsicAsyncGeneratorYieldWithAwait(
interpreter::RegisterList args) {
CallBuiltin<Builtin::kAsyncGeneratorYieldWithAwait>(args);
}
void BaselineCompiler::VisitConstruct() {
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
uint32_t arg_count = JSParameterCount(args.register_count());
CallBuiltin<Builtin::kConstruct_Baseline>(
RegisterOperand(0), // kFunction
kInterpreterAccumulatorRegister, // kNewTarget
arg_count, // kActualArgumentsCount
Index(3), // kSlot
RootIndex::kUndefinedValue, // kReceiver
args);
}
void BaselineCompiler::VisitConstructWithSpread() {
interpreter::RegisterList args = iterator().GetRegisterListOperand(1);
// Do not push the spread argument
interpreter::Register spread_register = args.last_register();
args = args.Truncate(args.register_count() - 1);
uint32_t arg_count = JSParameterCount(args.register_count());
using Descriptor =
CallInterfaceDescriptorFor<Builtin::kConstructWithSpread_Baseline>::type;
Register new_target =
Descriptor::GetRegisterParameter(Descriptor::kNewTarget);
__ Move(new_target, kInterpreterAccumulatorRegister);
CallBuiltin<Builtin::kConstructWithSpread_Baseline>(
RegisterOperand(0), // kFunction
new_target, // kNewTarget
arg_count, // kActualArgumentsCount
Index(3), // kSlot
spread_register, // kSpread
RootIndex::kUndefinedValue, // kReceiver
args);
}
void BaselineCompiler::VisitTestEqual() {
CallBuiltin<Builtin::kEqual_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitTestEqualStrict() {
CallBuiltin<Builtin::kStrictEqual_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitTestLessThan() {
CallBuiltin<Builtin::kLessThan_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitTestGreaterThan() {
CallBuiltin<Builtin::kGreaterThan_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitTestLessThanOrEqual() {
CallBuiltin<Builtin::kLessThanOrEqual_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitTestGreaterThanOrEqual() {
CallBuiltin<Builtin::kGreaterThanOrEqual_Baseline>(
RegisterOperand(0), kInterpreterAccumulatorRegister, Index(1));
}
void BaselineCompiler::VisitTestReferenceEqual() {
SelectBooleanConstant(
kInterpreterAccumulatorRegister,
[&](Label* is_true, Label::Distance distance) {
__ JumpIfTagged(kEqual, __ RegisterFrameOperand(RegisterOperand(0)),
kInterpreterAccumulatorRegister, is_true, distance);
});
}
void BaselineCompiler::VisitTestInstanceOf() {
using Descriptor =
CallInterfaceDescriptorFor<Builtin::kInstanceOf_Baseline>::type;
Register callable = Descriptor::GetRegisterParameter(Descriptor::kRight);
__ Move(callable, kInterpreterAccumulatorRegister);
CallBuiltin<Builtin::kInstanceOf_Baseline>(RegisterOperand(0), // object
callable, // callable
Index(1)); // slot
}
void BaselineCompiler::VisitTestIn() {
CallBuiltin<Builtin::kKeyedHasICBaseline>(
kInterpreterAccumulatorRegister, // object
RegisterOperand(0), // name
IndexAsTagged(1)); // slot
}
void BaselineCompiler::VisitTestUndetectable() {
Label done, is_smi, not_undetectable;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
Register map_bit_field = kInterpreterAccumulatorRegister;
__ LoadMap(map_bit_field, kInterpreterAccumulatorRegister);
__ LoadWord8Field(map_bit_field, map_bit_field, Map::kBitFieldOffset);
__ TestAndBranch(map_bit_field, Map::Bits1::IsUndetectableBit::kMask, kZero,
&not_undetectable, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&not_undetectable);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
__ Bind(&done);
}
void BaselineCompiler::VisitTestNull() {
SelectBooleanConstant(kInterpreterAccumulatorRegister,
[&](Label* is_true, Label::Distance distance) {
__ JumpIfRoot(kInterpreterAccumulatorRegister,
RootIndex::kNullValue, is_true,
distance);
});
}
void BaselineCompiler::VisitTestUndefined() {
SelectBooleanConstant(kInterpreterAccumulatorRegister,
[&](Label* is_true, Label::Distance distance) {
__ JumpIfRoot(kInterpreterAccumulatorRegister,
RootIndex::kUndefinedValue, is_true,
distance);
});
}
void BaselineCompiler::VisitTestTypeOf() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
auto literal_flag =
static_cast<interpreter::TestTypeOfFlags::LiteralFlag>(Flag8(0));
Label done;
switch (literal_flag) {
case interpreter::TestTypeOfFlags::LiteralFlag::kNumber: {
Label is_smi, is_heap_number;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
__ JumpIfObjectTypeFast(kEqual, kInterpreterAccumulatorRegister,
HEAP_NUMBER_TYPE, &is_heap_number, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&is_heap_number);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kString: {
Label is_smi, bad_instance_type;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
static_assert(INTERNALIZED_STRING_TYPE == FIRST_TYPE);
__ JumpIfObjectType(kGreaterThanEqual, kInterpreterAccumulatorRegister,
FIRST_NONSTRING_TYPE, scratch_scope.AcquireScratch(),
&bad_instance_type, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&bad_instance_type);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kSymbol: {
Label is_smi, bad_instance_type;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
__ JumpIfObjectTypeFast(kNotEqual, kInterpreterAccumulatorRegister,
SYMBOL_TYPE, &bad_instance_type, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&bad_instance_type);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kBoolean: {
Label is_true, is_false;
__ JumpIfRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue,
&is_true, Label::kNear);
__ JumpIfRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue,
&is_false, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_true);
__ Bind(&is_false);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kBigInt: {
Label is_smi, bad_instance_type;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
__ JumpIfObjectTypeFast(kNotEqual, kInterpreterAccumulatorRegister,
BIGINT_TYPE, &bad_instance_type, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&bad_instance_type);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kUndefined: {
Label is_smi, is_null, not_undetectable;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
// null is undetectable, so test it explicitly, and return false.
__ JumpIfRoot(kInterpreterAccumulatorRegister, RootIndex::kNullValue,
&is_null, Label::kNear);
// All other undetectable maps are typeof undefined.
Register map_bit_field = kInterpreterAccumulatorRegister;
__ LoadMap(map_bit_field, kInterpreterAccumulatorRegister);
__ LoadWord8Field(map_bit_field, map_bit_field, Map::kBitFieldOffset);
__ TestAndBranch(map_bit_field, Map::Bits1::IsUndetectableBit::kMask,
kZero, &not_undetectable, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&is_null);
__ Bind(&not_undetectable);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kFunction: {
Label is_smi, not_callable, undetectable;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
// Check if the map is callable but not undetectable.
Register map_bit_field = kInterpreterAccumulatorRegister;
__ LoadMap(map_bit_field, kInterpreterAccumulatorRegister);
__ LoadWord8Field(map_bit_field, map_bit_field, Map::kBitFieldOffset);
__ TestAndBranch(map_bit_field, Map::Bits1::IsCallableBit::kMask, kZero,
&not_callable, Label::kNear);
__ TestAndBranch(map_bit_field, Map::Bits1::IsUndetectableBit::kMask,
kNotZero, &undetectable, Label::kNear);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&not_callable);
__ Bind(&undetectable);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kObject: {
Label is_smi, is_null, bad_instance_type, undetectable_or_callable;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
// If the object is null, return true.
__ JumpIfRoot(kInterpreterAccumulatorRegister, RootIndex::kNullValue,
&is_null, Label::kNear);
// If the object's instance type isn't within the range, return false.
static_assert(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
Register map = scratch_scope.AcquireScratch();
__ JumpIfObjectType(kLessThan, kInterpreterAccumulatorRegister,
FIRST_JS_RECEIVER_TYPE, map, &bad_instance_type,
Label::kNear);
// If the map is undetectable or callable, return false.
Register map_bit_field = kInterpreterAccumulatorRegister;
__ LoadWord8Field(map_bit_field, map, Map::kBitFieldOffset);
__ TestAndBranch(map_bit_field,
Map::Bits1::IsUndetectableBit::kMask |
Map::Bits1::IsCallableBit::kMask,
kNotZero, &undetectable_or_callable, Label::kNear);
__ Bind(&is_null);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kTrueValue);
__ Jump(&done, Label::kNear);
__ Bind(&is_smi);
__ Bind(&bad_instance_type);
__ Bind(&undetectable_or_callable);
__ LoadRoot(kInterpreterAccumulatorRegister, RootIndex::kFalseValue);
break;
}
case interpreter::TestTypeOfFlags::LiteralFlag::kOther:
default:
UNREACHABLE();
}
__ Bind(&done);
}
void BaselineCompiler::VisitToName() {
SaveAccumulatorScope save_accumulator(&basm_);
CallBuiltin<Builtin::kToName>(kInterpreterAccumulatorRegister);
StoreRegister(0, kInterpreterAccumulatorRegister);
}
void BaselineCompiler::VisitToNumber() {
CallBuiltin<Builtin::kToNumber_Baseline>(kInterpreterAccumulatorRegister,
Index(0));
}
void BaselineCompiler::VisitToNumeric() {
CallBuiltin<Builtin::kToNumeric_Baseline>(kInterpreterAccumulatorRegister,
Index(0));
}
void BaselineCompiler::VisitToObject() {
SaveAccumulatorScope save_accumulator(&basm_);
CallBuiltin<Builtin::kToObject>(kInterpreterAccumulatorRegister);
StoreRegister(0, kInterpreterAccumulatorRegister);
}
void BaselineCompiler::VisitToString() {
CallBuiltin<Builtin::kToString>(kInterpreterAccumulatorRegister);
}
void BaselineCompiler::VisitToBoolean() {
CallBuiltin<Builtin::kToBoolean>(kInterpreterAccumulatorRegister);
}
void BaselineCompiler::VisitCreateRegExpLiteral() {
CallBuiltin<Builtin::kCreateRegExpLiteral>(
FeedbackVector(), // feedback vector
IndexAsTagged(1), // slot
Constant<HeapObject>(0), // pattern
Flag16AsSmi(2)); // flags
}
void BaselineCompiler::VisitCreateArrayLiteral() {
uint32_t flags = Flag8(2);
int32_t flags_raw = static_cast<int32_t>(
interpreter::CreateArrayLiteralFlags::FlagsBits::decode(flags));
if (flags &
interpreter::CreateArrayLiteralFlags::FastCloneSupportedBit::kMask) {
CallBuiltin<Builtin::kCreateShallowArrayLiteral>(
FeedbackVector(), // feedback vector
IndexAsTagged(1), // slot
Constant<HeapObject>(0), // constant elements
Smi::FromInt(flags_raw)); // flags
} else {
CallRuntime(Runtime::kCreateArrayLiteral,
FeedbackVector(), // feedback vector
IndexAsTagged(1), // slot
Constant<HeapObject>(0), // constant elements
Smi::FromInt(flags_raw)); // flags
}
}
void BaselineCompiler::VisitCreateArrayFromIterable() {
CallBuiltin<Builtin::kIterableToListWithSymbolLookup>(
kInterpreterAccumulatorRegister); // iterable
}
void BaselineCompiler::VisitCreateEmptyArrayLiteral() {
CallBuiltin<Builtin::kCreateEmptyArrayLiteral>(FeedbackVector(),
IndexAsTagged(0));
}
void BaselineCompiler::VisitCreateObjectLiteral() {
uint32_t flags = Flag8(2);
int32_t flags_raw = static_cast<int32_t>(
interpreter::CreateObjectLiteralFlags::FlagsBits::decode(flags));
if (flags &
interpreter::CreateObjectLiteralFlags::FastCloneSupportedBit::kMask) {
CallBuiltin<Builtin::kCreateShallowObjectLiteral>(
FeedbackVector(), // feedback vector
IndexAsTagged(1), // slot
Constant<ObjectBoilerplateDescription>(0), // boilerplate
Smi::FromInt(flags_raw)); // flags
} else {
CallRuntime(Runtime::kCreateObjectLiteral,
FeedbackVector(), // feedback vector
IndexAsTagged(1), // slot
Constant<ObjectBoilerplateDescription>(0), // boilerplate
Smi::FromInt(flags_raw)); // flags
}
}
void BaselineCompiler::VisitCreateEmptyObjectLiteral() {
CallBuiltin<Builtin::kCreateEmptyLiteralObject>();
}
void BaselineCompiler::VisitCloneObject() {
uint32_t flags = Flag8(1);
int32_t raw_flags =
interpreter::CreateObjectLiteralFlags::FlagsBits::decode(flags);
CallBuiltin<Builtin::kCloneObjectICBaseline>(
RegisterOperand(0), // source
Smi::FromInt(raw_flags), // flags
IndexAsTagged(2)); // slot
}
void BaselineCompiler::VisitGetTemplateObject() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
CallBuiltin<Builtin::kGetTemplateObject>(
shared_function_info_, // shared function info
Constant<HeapObject>(0), // description
Index(1), // slot
FeedbackVector()); // feedback_vector
}
void BaselineCompiler::VisitCreateClosure() {
Register feedback_cell =
FastNewClosureBaselineDescriptor::GetRegisterParameter(
FastNewClosureBaselineDescriptor::kFeedbackCell);
LoadClosureFeedbackArray(feedback_cell);
__ LoadFixedArrayElement(feedback_cell, feedback_cell, Index(1));
uint32_t flags = Flag8(2);
if (interpreter::CreateClosureFlags::FastNewClosureBit::decode(flags)) {
CallBuiltin<Builtin::kFastNewClosureBaseline>(
Constant<SharedFunctionInfo>(0), feedback_cell);
} else {
Runtime::FunctionId function_id =
interpreter::CreateClosureFlags::PretenuredBit::decode(flags)
? Runtime::kNewClosure_Tenured
: Runtime::kNewClosure;
CallRuntime(function_id, Constant<SharedFunctionInfo>(0), feedback_cell);
}
}
void BaselineCompiler::VisitCreateBlockContext() {
CallRuntime(Runtime::kPushBlockContext, Constant<ScopeInfo>(0));
}
void BaselineCompiler::VisitCreateCatchContext() {
CallRuntime(Runtime::kPushCatchContext,
RegisterOperand(0), // exception
Constant<ScopeInfo>(1));
}
void BaselineCompiler::VisitCreateFunctionContext() {
Handle<ScopeInfo> info = Constant<ScopeInfo>(0);
uint32_t slot_count = Uint(1);
DCHECK_LE(slot_count, ConstructorBuiltins::MaximumFunctionContextSlots());
DCHECK_EQ(info->scope_type(), ScopeType::FUNCTION_SCOPE);
CallBuiltin<Builtin::kFastNewFunctionContextFunction>(info, slot_count);
}
void BaselineCompiler::VisitCreateEvalContext() {
Handle<ScopeInfo> info = Constant<ScopeInfo>(0);
uint32_t slot_count = Uint(1);
if (slot_count < static_cast<uint32_t>(
ConstructorBuiltins::MaximumFunctionContextSlots())) {
DCHECK_EQ(info->scope_type(), ScopeType::EVAL_SCOPE);
CallBuiltin<Builtin::kFastNewFunctionContextEval>(info, slot_count);
} else {
CallRuntime(Runtime::kNewFunctionContext, Constant<ScopeInfo>(0));
}
}
void BaselineCompiler::VisitCreateWithContext() {
CallRuntime(Runtime::kPushWithContext,
RegisterOperand(0), // object
Constant<ScopeInfo>(1));
}
void BaselineCompiler::VisitCreateMappedArguments() {
if (shared_function_info_->has_duplicate_parameters()) {
CallRuntime(Runtime::kNewSloppyArguments, __ FunctionOperand());
} else {
CallBuiltin<Builtin::kFastNewSloppyArguments>(__ FunctionOperand());
}
}
void BaselineCompiler::VisitCreateUnmappedArguments() {
CallBuiltin<Builtin::kFastNewStrictArguments>(__ FunctionOperand());
}
void BaselineCompiler::VisitCreateRestParameter() {
CallBuiltin<Builtin::kFastNewRestArguments>(__ FunctionOperand());
}
void BaselineCompiler::VisitJumpLoop() {
#ifndef V8_JITLESS
Label osr_armed, osr_not_armed;
using D = OnStackReplacementDescriptor;
Register feedback_vector = Register::no_reg();
Register osr_state = Register::no_reg();
const int loop_depth = iterator().GetImmediateOperand(1);
{
ASM_CODE_COMMENT_STRING(&masm_, "OSR Check Armed");
BaselineAssembler::ScratchRegisterScope temps(&basm_);
feedback_vector = temps.AcquireScratch();
osr_state = temps.AcquireScratch();
LoadFeedbackVector(feedback_vector);
__ LoadWord8Field(osr_state, feedback_vector,
FeedbackVector::kOsrStateOffset);
static_assert(FeedbackVector::MaybeHasOptimizedOsrCodeBit::encode(true) >
FeedbackVector::kMaxOsrUrgency);
__ JumpIfByte(kUnsignedGreaterThan, osr_state, loop_depth, &osr_armed,
Label::kNear);
}
__ Bind(&osr_not_armed);
#endif // !V8_JITLESS
Label* label = labels_[iterator().GetJumpTargetOffset()].GetPointer();
int weight = iterator().GetRelativeJumpTargetOffset() -
iterator().current_bytecode_size_without_prefix();
// We can pass in the same label twice since it's a back edge and thus already
// bound.
DCHECK(label->is_bound());
UpdateInterruptBudgetAndJumpToLabel(weight, label, label);
#ifndef V8_JITLESS
{
ASM_CODE_COMMENT_STRING(&masm_, "OSR Handle Armed");
__ Bind(&osr_armed);
Register maybe_target_code = D::MaybeTargetCodeRegister();
Label osr;
{
BaselineAssembler::ScratchRegisterScope temps(&basm_);
Register scratch0 = temps.AcquireScratch();
Register scratch1 = temps.AcquireScratch();
DCHECK_EQ(scratch0, feedback_vector);
DCHECK_EQ(scratch1, osr_state);
DCHECK(!AreAliased(maybe_target_code, scratch0, scratch1));
__ TryLoadOptimizedOsrCode(maybe_target_code, scratch0,
iterator().GetSlotOperand(2), &osr,
Label::kNear);
__ DecodeField<FeedbackVector::OsrUrgencyBits>(scratch1);
__ JumpIfByte(kUnsignedLessThanEqual, scratch1, loop_depth,
&osr_not_armed, Label::kNear);
}
__ Bind(&osr);
Label do_osr;
int weight = bytecode_->length() * v8_flags.osr_to_tierup;
UpdateInterruptBudgetAndJumpToLabel(-weight, nullptr, &do_osr);
__ Bind(&do_osr);
CallBuiltin<Builtin::kBaselineOnStackReplacement>(maybe_target_code);
__ AddToInterruptBudgetAndJumpIfNotExceeded(weight, nullptr);
__ Jump(&osr_not_armed, Label::kNear);
}
#endif // !V8_JITLESS
}
void BaselineCompiler::VisitJump() { __ Jump(BuildForwardJumpLabel()); }
void BaselineCompiler::VisitJumpConstant() { VisitJump(); }
void BaselineCompiler::VisitJumpIfNullConstant() { VisitJumpIfNull(); }
void BaselineCompiler::VisitJumpIfNotNullConstant() { VisitJumpIfNotNull(); }
void BaselineCompiler::VisitJumpIfUndefinedConstant() {
VisitJumpIfUndefined();
}
void BaselineCompiler::VisitJumpIfNotUndefinedConstant() {
VisitJumpIfNotUndefined();
}
void BaselineCompiler::VisitJumpIfUndefinedOrNullConstant() {
VisitJumpIfUndefinedOrNull();
}
void BaselineCompiler::VisitJumpIfTrueConstant() { VisitJumpIfTrue(); }
void BaselineCompiler::VisitJumpIfFalseConstant() { VisitJumpIfFalse(); }
void BaselineCompiler::VisitJumpIfJSReceiverConstant() {
VisitJumpIfJSReceiver();
}
void BaselineCompiler::VisitJumpIfToBooleanTrueConstant() {
VisitJumpIfToBooleanTrue();
}
void BaselineCompiler::VisitJumpIfToBooleanFalseConstant() {
VisitJumpIfToBooleanFalse();
}
void BaselineCompiler::VisitJumpIfToBooleanTrue() {
Label dont_jump;
JumpIfToBoolean(false, &dont_jump, Label::kNear);
__ Jump(BuildForwardJumpLabel());
__ Bind(&dont_jump);
}
void BaselineCompiler::VisitJumpIfToBooleanFalse() {
Label dont_jump;
JumpIfToBoolean(true, &dont_jump, Label::kNear);
__ Jump(BuildForwardJumpLabel());
__ Bind(&dont_jump);
}
void BaselineCompiler::VisitJumpIfTrue() { JumpIfRoot(RootIndex::kTrueValue); }
void BaselineCompiler::VisitJumpIfFalse() {
JumpIfRoot(RootIndex::kFalseValue);
}
void BaselineCompiler::VisitJumpIfNull() { JumpIfRoot(RootIndex::kNullValue); }
void BaselineCompiler::VisitJumpIfNotNull() {
JumpIfNotRoot(RootIndex::kNullValue);
}
void BaselineCompiler::VisitJumpIfUndefined() {
JumpIfRoot(RootIndex::kUndefinedValue);
}
void BaselineCompiler::VisitJumpIfNotUndefined() {
JumpIfNotRoot(RootIndex::kUndefinedValue);
}
void BaselineCompiler::VisitJumpIfUndefinedOrNull() {
Label do_jump, dont_jump;
__ JumpIfRoot(kInterpreterAccumulatorRegister, RootIndex::kUndefinedValue,
&do_jump);
__ JumpIfNotRoot(kInterpreterAccumulatorRegister, RootIndex::kNullValue,
&dont_jump, Label::kNear);
__ Bind(&do_jump);
__ Jump(BuildForwardJumpLabel());
__ Bind(&dont_jump);
}
void BaselineCompiler::VisitJumpIfJSReceiver() {
Label is_smi, dont_jump;
__ JumpIfSmi(kInterpreterAccumulatorRegister, &is_smi, Label::kNear);
#if V8_STATIC_ROOTS_BOOL
__ JumpIfJSAnyIsPrimitive(kInterpreterAccumulatorRegister, &dont_jump,
Label::Distance::kNear);
#else
__ JumpIfObjectTypeFast(kLessThan, kInterpreterAccumulatorRegister,
FIRST_JS_RECEIVER_TYPE, &dont_jump);
#endif
__ Jump(BuildForwardJumpLabel());
__ Bind(&is_smi);
__ Bind(&dont_jump);
}
void BaselineCompiler::VisitSwitchOnSmiNoFeedback() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
interpreter::JumpTableTargetOffsets offsets =
iterator().GetJumpTableTargetOffsets();
if (offsets.size() == 0) return;
int case_value_base = (*offsets.begin()).case_value;
std::unique_ptr<Label*[]> labels = std::make_unique<Label*[]>(offsets.size());
for (interpreter::JumpTableTargetOffset offset : offsets) {
labels[offset.case_value - case_value_base] =
EnsureLabel(offset.target_offset);
}
Register case_value = scratch_scope.AcquireScratch();
__ SmiUntag(case_value, kInterpreterAccumulatorRegister);
__ Switch(case_value, case_value_base, labels.get(), offsets.size());
}
void BaselineCompiler::VisitForInEnumerate() {
CallBuiltin<Builtin::kForInEnumerate>(RegisterOperand(0));
}
void BaselineCompiler::VisitForInPrepare() {
StoreRegister(0, kInterpreterAccumulatorRegister);
CallBuiltin<Builtin::kForInPrepare>(kInterpreterAccumulatorRegister,
IndexAsTagged(1), FeedbackVector());
interpreter::Register first = iterator().GetRegisterOperand(0);
interpreter::Register second(first.index() + 1);
interpreter::Register third(first.index() + 2);
__ StoreRegister(second, kReturnRegister0);
__ StoreRegister(third, kReturnRegister1);
}
void BaselineCompiler::VisitForInContinue() {
SelectBooleanConstant(kInterpreterAccumulatorRegister,
[&](Label* is_true, Label::Distance distance) {
LoadRegister(kInterpreterAccumulatorRegister, 0);
__ JumpIfTagged(
kNotEqual, kInterpreterAccumulatorRegister,
__ RegisterFrameOperand(RegisterOperand(1)),
is_true, distance);
});
}
void BaselineCompiler::VisitForInNext() {
interpreter::Register cache_type, cache_array;
std::tie(cache_type, cache_array) = iterator().GetRegisterPairOperand(2);
CallBuiltin<Builtin::kForInNext>(Index(3), // vector slot
RegisterOperand(0), // object
cache_array, // cache array
cache_type, // cache type
RegisterOperand(1), // index
FeedbackVector()); // feedback vector
}
void BaselineCompiler::VisitForInStep() {
LoadRegister(kInterpreterAccumulatorRegister, 0);
__ AddSmi(kInterpreterAccumulatorRegister, Smi::FromInt(1));
}
void BaselineCompiler::VisitSetPendingMessage() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register pending_message = scratch_scope.AcquireScratch();
__ Move(pending_message,
ExternalReference::address_of_pending_message(local_isolate_));
Register tmp = scratch_scope.AcquireScratch();
__ Move(tmp, kInterpreterAccumulatorRegister);
__ Move(kInterpreterAccumulatorRegister, MemOperand(pending_message, 0));
__ Move(MemOperand(pending_message, 0), tmp);
}
void BaselineCompiler::VisitThrow() {
CallRuntime(Runtime::kThrow, kInterpreterAccumulatorRegister);
__ Trap();
}
void BaselineCompiler::VisitReThrow() {
CallRuntime(Runtime::kReThrow, kInterpreterAccumulatorRegister);
__ Trap();
}
void BaselineCompiler::VisitReturn() {
ASM_CODE_COMMENT_STRING(&masm_, "Return");
int profiling_weight = iterator().current_offset() +
iterator().current_bytecode_size_without_prefix();
int parameter_count = bytecode_->parameter_count();
TailCallBuiltin<Builtin::kBaselineLeaveFrame>(parameter_count,
-profiling_weight);
}
void BaselineCompiler::VisitThrowReferenceErrorIfHole() {
Label done;
__ JumpIfNotRoot(kInterpreterAccumulatorRegister, RootIndex::kTheHoleValue,
&done);
CallRuntime(Runtime::kThrowAccessedUninitializedVariable, Constant<Name>(0));
// Unreachable.
__ Trap();
__ Bind(&done);
}
void BaselineCompiler::VisitThrowSuperNotCalledIfHole() {
Label done;
__ JumpIfNotRoot(kInterpreterAccumulatorRegister, RootIndex::kTheHoleValue,
&done);
CallRuntime(Runtime::kThrowSuperNotCalled);
// Unreachable.
__ Trap();
__ Bind(&done);
}
void BaselineCompiler::VisitThrowSuperAlreadyCalledIfNotHole() {
Label done;
__ JumpIfRoot(kInterpreterAccumulatorRegister, RootIndex::kTheHoleValue,
&done);
CallRuntime(Runtime::kThrowSuperAlreadyCalledError);
// Unreachable.
__ Trap();
__ Bind(&done);
}
void BaselineCompiler::VisitThrowIfNotSuperConstructor() {
Label done;
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register reg = scratch_scope.AcquireScratch();
LoadRegister(reg, 0);
Register map_bit_field = scratch_scope.AcquireScratch();
__ LoadMap(map_bit_field, reg);
__ LoadWord8Field(map_bit_field, map_bit_field, Map::kBitFieldOffset);
__ TestAndBranch(map_bit_field, Map::Bits1::IsConstructorBit::kMask, kNotZero,
&done, Label::kNear);
CallRuntime(Runtime::kThrowNotSuperConstructor, reg, __ FunctionOperand());
__ Bind(&done);
}
void BaselineCompiler::VisitSwitchOnGeneratorState() {
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Label fallthrough;
Register generator_object = scratch_scope.AcquireScratch();
LoadRegister(generator_object, 0);
__ JumpIfRoot(generator_object, RootIndex::kUndefinedValue, &fallthrough);
Register continuation = scratch_scope.AcquireScratch();
__ LoadTaggedSignedFieldAndUntag(continuation, generator_object,
JSGeneratorObject::kContinuationOffset);
__ StoreTaggedSignedField(
generator_object, JSGeneratorObject::kContinuationOffset,
Smi::FromInt(JSGeneratorObject::kGeneratorExecuting));
Register context = scratch_scope.AcquireScratch();
__ LoadTaggedField(context, generator_object,
JSGeneratorObject::kContextOffset);
__ StoreContext(context);
interpreter::JumpTableTargetOffsets offsets =
iterator().GetJumpTableTargetOffsets();
if (0 < offsets.size()) {
DCHECK_EQ(0, (*offsets.begin()).case_value);
std::unique_ptr<Label*[]> labels =
std::make_unique<Label*[]>(offsets.size());
for (interpreter::JumpTableTargetOffset offset : offsets) {
labels[offset.case_value] = EnsureLabel(offset.target_offset);
}
__ Switch(continuation, 0, labels.get(), offsets.size());
// We should never fall through this switch.
// TODO(v8:11429,leszeks): Maybe remove the fallthrough check in the Switch?
__ Trap();
}
__ Bind(&fallthrough);
}
void BaselineCompiler::VisitSuspendGenerator() {
DCHECK_EQ(iterator().GetRegisterOperand(1), interpreter::Register(0));
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register generator_object = scratch_scope.AcquireScratch();
LoadRegister(generator_object, 0);
{
SaveAccumulatorScope accumulator_scope(&basm_);
int bytecode_offset =
BytecodeArray::kHeaderSize + iterator().current_offset();
CallBuiltin<Builtin::kSuspendGeneratorBaseline>(
generator_object,
static_cast<int>(Uint(3)), // suspend_id
bytecode_offset,
static_cast<int>(RegisterCount(2))); // register_count
}
int parameter_count = bytecode_->parameter_count();
TailCallBuiltin<Builtin::kBaselineLeaveFrame>(parameter_count, 0);
}
void BaselineCompiler::VisitResumeGenerator() {
DCHECK_EQ(iterator().GetRegisterOperand(1), interpreter::Register(0));
BaselineAssembler::ScratchRegisterScope scratch_scope(&basm_);
Register generator_object = scratch_scope.AcquireScratch();
LoadRegister(generator_object, 0);
CallBuiltin<Builtin::kResumeGeneratorBaseline>(
generator_object,
static_cast<int>(RegisterCount(2))); // register_count
}
void BaselineCompiler::VisitGetIterator() {
CallBuiltin<Builtin::kGetIteratorBaseline>(RegisterOperand(0), // receiver
IndexAsTagged(1), // load_slot
IndexAsTagged(2)); // call_slot
}
void BaselineCompiler::VisitDebugger() {
SaveAccumulatorScope accumulator_scope(&basm_);
CallRuntime(Runtime::kHandleDebuggerStatement);
}
void BaselineCompiler::VisitIncBlockCounter() {
SaveAccumulatorScope accumulator_scope(&basm_);
CallBuiltin<Builtin::kIncBlockCounter>(__ FunctionOperand(),
IndexAsSmi(0)); // coverage array slot
}
void BaselineCompiler::VisitAbort() {
CallRuntime(Runtime::kAbort, Smi::FromInt(Index(0)));
__ Trap();
}
void BaselineCompiler::VisitWide() {
// Consumed by the BytecodeArrayIterator.
UNREACHABLE();
}
void BaselineCompiler::VisitExtraWide() {
// Consumed by the BytecodeArrayIterator.
UNREACHABLE();
}
void BaselineCompiler::VisitIllegal() {
// Not emitted in valid bytecode.
UNREACHABLE();
}
#define DEBUG_BREAK(Name, ...) \
void BaselineCompiler::Visit##Name() { UNREACHABLE(); }
DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK)
#undef DEBUG_BREAK
} // namespace baseline
} // namespace internal
} // namespace v8
#endif // ENABLE_SPARKPLUG