#include "test/unittests/compiler/backend/turboshaft-instruction-selector-unittest.h"
#include "src/codegen/code-factory.h"
#include "src/codegen/tick-counter.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/graph.h"
#include "src/compiler/schedule.h"
#include "src/compiler/turboshaft/instruction-selection-phase.h"
#include "src/compiler/turboshaft/phase.h"
#include "src/compiler/turboshaft/representations.h"
#include "src/flags/flags.h"
#include "src/objects/objects-inl.h"
#include "test/unittests/compiler/compiler-test-utils.h"
namespace v8::internal::compiler::turboshaft {
TurboshaftInstructionSelectorTest::TurboshaftInstructionSelectorTest()
: … { … }
TurboshaftInstructionSelectorTest::~TurboshaftInstructionSelectorTest() =
default;
TurboshaftInstructionSelectorTest::Stream
TurboshaftInstructionSelectorTest::StreamBuilder::Build(
InstructionSelector::Features features,
TurboshaftInstructionSelectorTest::StreamBuilderMode mode,
InstructionSelector::SourcePositionMode source_position_mode) { … }
int TurboshaftInstructionSelectorTest::Stream::ToVreg(OpIndex index) const { … }
bool TurboshaftInstructionSelectorTest::Stream::IsFixed(
const InstructionOperand* operand, Register reg) const { … }
bool TurboshaftInstructionSelectorTest::Stream::IsSameAsFirst(
const InstructionOperand* operand) const { … }
bool TurboshaftInstructionSelectorTest::Stream::IsSameAsInput(
const InstructionOperand* operand, int input_index) const { … }
bool TurboshaftInstructionSelectorTest::Stream::IsUsedAtStart(
const InstructionOperand* operand) const { … }
const FrameStateFunctionInfo*
TurboshaftInstructionSelectorTest::StreamBuilder::GetFrameStateFunctionInfo(
uint16_t parameter_count, int local_count) { … }
TARGET_TEST_F(TurboshaftInstructionSelectorTest, ReturnFloat32Constant) { … }
TARGET_TEST_F(TurboshaftInstructionSelectorTest, ReturnParameter) { … }
TARGET_TEST_F(TurboshaftInstructionSelectorTest, ReturnZero) { … }
TARGET_TEST_F(TurboshaftInstructionSelectorTest,
TruncateFloat64ToWord32WithParameter) { … }
TARGET_TEST_F(TurboshaftInstructionSelectorTest, DoubleParameter) { … }
TARGET_TEST_F(TurboshaftInstructionSelectorTest, ReferenceParameter) { … }
TurboshaftInstructionSelectorPhiTest;
TARGET_TEST_P(TurboshaftInstructionSelectorPhiTest, Doubleness) { … }
TARGET_TEST_P(TurboshaftInstructionSelectorPhiTest, Referenceness) { … }
INSTANTIATE_TEST_SUITE_P(…);
#if 0
TARGET_TEST_F(TurboshaftInstructionSelectorTest, CallJSFunctionWithDeopt) {
StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::AnyTagged());
BytecodeOffset bailout_id(42);
Node* function_node = m.Parameter(0);
Node* receiver = m.Parameter(1);
Node* context = m.Parameter(2);
ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
ZoneVector<MachineType> tagged_type(1, MachineType::AnyTagged(), zone());
ZoneVector<MachineType> empty_type(zone());
auto call_descriptor = Linkage::GetJSCallDescriptor(
zone(), false, 1,
CallDescriptor::kNeedsFrameState | CallDescriptor::kCanUseRoots);
Node* parameters = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(1));
Node* locals = m.AddNode(
m.common()->TypedStateValues(&empty_type, SparseInputMask::Dense()));
Node* stack = m.AddNode(
m.common()->TypedStateValues(&tagged_type, SparseInputMask::Dense()),
m.UndefinedConstant());
Node* context_sentinel = m.Int32Constant(0);
Node* state_node = m.AddNode(
m.common()->FrameState(bailout_id, OutputFrameStateCombine::PokeAt(0),
m.GetFrameStateFunctionInfo(1, 0)),
parameters, locals, stack, context_sentinel, function_node,
m.graph()->start());
Node* nodes[] = {function_node, receiver, m.UndefinedConstant(),
m.Int32Constant(1), context, state_node};
Node* call = m.CallNWithFrameState(call_descriptor, arraysize(nodes), nodes);
m.Return(call);
Stream s = m.Build(kAllExceptNopInstructions);
size_t index = 0;
for (; index < s.size() && s[index]->arch_opcode() != kArchCallJSFunction;
index++) {
}
ASSERT_EQ(index + 2, s.size());
EXPECT_EQ(kArchCallJSFunction, s[index++]->arch_opcode());
EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
}
TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeopt) {
StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::AnyTagged());
BytecodeOffset bailout_id_before(42);
Node* function_node = m.Parameter(0);
Node* receiver = m.Parameter(1);
Node* context = m.Int32Constant(1);
ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
ZoneVector<MachineType> float64_type(1, MachineType::Float64(), zone());
ZoneVector<MachineType> tagged_type(1, MachineType::AnyTagged(), zone());
Callable callable = Builtins::CallableFor(isolate(), Builtin::kToObject);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), 1, CallDescriptor::kNeedsFrameState,
Operator::kNoProperties);
Node* parameters = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(43));
Node* locals = m.AddNode(
m.common()->TypedStateValues(&float64_type, SparseInputMask::Dense()),
m.Float64Constant(0.5));
Node* stack = m.AddNode(
m.common()->TypedStateValues(&tagged_type, SparseInputMask::Dense()),
m.UndefinedConstant());
Node* context_sentinel = m.Int32Constant(0);
Node* state_node =
m.AddNode(m.common()->FrameState(bailout_id_before,
OutputFrameStateCombine::PokeAt(0),
m.GetFrameStateFunctionInfo(1, 1)),
parameters, locals, stack, context_sentinel, function_node,
m.graph()->start());
Node* stub_code = m.HeapConstant(callable.code());
Node* nodes[] = {stub_code, function_node, receiver, context, state_node};
Node* call = m.CallNWithFrameState(call_descriptor, arraysize(nodes), nodes);
m.Return(call);
Stream s = m.Build(kAllExceptNopInstructions);
size_t index = 0;
for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject;
index++) {
}
ASSERT_EQ(index + 2, s.size());
const Instruction* call_instr = s[index++];
EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode());
size_t num_operands =
1 +
6 +
1 +
1 +
1;
ASSERT_EQ(num_operands, call_instr->InputCount());
EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate());
int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(1));
FrameStateDescriptor* desc_before =
s.GetFrameStateDescriptor(deopt_id_before);
EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
EXPECT_EQ(1u, desc_before->parameters_count());
EXPECT_EQ(1u, desc_before->locals_count());
EXPECT_EQ(1u, desc_before->stack_count());
EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(3)));
EXPECT_EQ(0, s.ToInt32(call_instr->InputAt(4)));
EXPECT_EQ(0.5, s.ToFloat64(call_instr->InputAt(5)));
EXPECT_TRUE(IsUndefined(*s.ToHeapObject(call_instr->InputAt(6)), isolate()));
EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(7)));
EXPECT_EQ(s.ToVreg(context), s.ToVreg(call_instr->InputAt(8)));
EXPECT_TRUE(call_instr->InputAt(9)->IsImmediate());
EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
EXPECT_EQ(index, s.size());
}
TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeoptRecursiveFrameState) {
StreamBuilder m(this, MachineType::AnyTagged(), MachineType::AnyTagged(),
MachineType::AnyTagged(), MachineType::AnyTagged());
BytecodeOffset bailout_id_before(42);
BytecodeOffset bailout_id_parent(62);
Node* function_node = m.Parameter(0);
Node* receiver = m.Parameter(1);
Node* context = m.Int32Constant(66);
Node* context2 = m.Int32Constant(46);
ZoneVector<MachineType> int32_type(1, MachineType::Int32(), zone());
ZoneVector<MachineType> float64_type(1, MachineType::Float64(), zone());
Callable callable = Builtins::CallableFor(isolate(), Builtin::kToObject);
auto call_descriptor = Linkage::GetStubCallDescriptor(
zone(), callable.descriptor(), 1, CallDescriptor::kNeedsFrameState,
Operator::kNoProperties);
Node* parameters = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(63));
Node* locals = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(64));
Node* stack = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(65));
Node* frame_state_parent = m.AddNode(
m.common()->FrameState(bailout_id_parent,
OutputFrameStateCombine::Ignore(),
m.GetFrameStateFunctionInfo(1, 1)),
parameters, locals, stack, context, function_node, m.graph()->start());
Node* parameters2 = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(43));
Node* locals2 = m.AddNode(
m.common()->TypedStateValues(&float64_type, SparseInputMask::Dense()),
m.Float64Constant(0.25));
Node* stack2 = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(44));
Node* state_node =
m.AddNode(m.common()->FrameState(bailout_id_before,
OutputFrameStateCombine::PokeAt(0),
m.GetFrameStateFunctionInfo(1, 1)),
parameters2, locals2, stack2, context2, function_node,
frame_state_parent);
Node* stub_code = m.HeapConstant(callable.code());
Node* nodes[] = {stub_code, function_node, receiver, context2, state_node};
Node* call = m.CallNWithFrameState(call_descriptor, arraysize(nodes), nodes);
m.Return(call);
Stream s = m.Build(kAllExceptNopInstructions);
size_t index = 0;
for (; index < s.size() && s[index]->arch_opcode() != kArchCallCodeObject;
index++) {
}
EXPECT_EQ(index + 2, s.size());
const Instruction* call_instr = s[index++];
EXPECT_EQ(kArchCallCodeObject, call_instr->arch_opcode());
size_t num_operands =
1 +
1 +
5 +
5 +
1 +
1 +
1;
EXPECT_EQ(num_operands, call_instr->InputCount());
EXPECT_TRUE(call_instr->InputAt(0)->IsImmediate());
int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(1));
FrameStateDescriptor* desc_before =
s.GetFrameStateDescriptor(deopt_id_before);
FrameStateDescriptor* desc_before_outer = desc_before->outer_state();
EXPECT_EQ(bailout_id_before, desc_before->bailout_id());
EXPECT_EQ(1u, desc_before_outer->parameters_count());
EXPECT_EQ(1u, desc_before_outer->locals_count());
EXPECT_EQ(1u, desc_before_outer->stack_count());
EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(3)));
EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(4)));
EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(5)));
EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(6)));
EXPECT_EQ(1u, desc_before->parameters_count());
EXPECT_EQ(1u, desc_before->locals_count());
EXPECT_EQ(1u, desc_before->stack_count());
EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(8)));
EXPECT_EQ(46, s.ToInt32(call_instr->InputAt(9)));
EXPECT_EQ(0.25, s.ToFloat64(call_instr->InputAt(10)));
EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(11)));
EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(12)));
EXPECT_EQ(s.ToVreg(context2), s.ToVreg(call_instr->InputAt(13)));
EXPECT_TRUE(call_instr->InputAt(14)->IsImmediate());
EXPECT_EQ(kArchRet, s[index++]->arch_opcode());
EXPECT_EQ(index, s.size());
}
#endif
}