c51041f454
With the new Turbofan variants (NCI and Turboprop), we need a way to distinguish between them both during and after compilation. We initially introduced CompilationTarget to track the variant during compilation, but decided to reuse the code kind as the canonical spot to store this information instead. Why? Because it is an established mechanism, already available in most of the necessary spots (inside the pipeline, on Code objects, in profiling traces). This CL removes CompilationTarget and adds a new NATIVE_CONTEXT_INDEPENDENT kind, plus helper functions to determine various things about a given code kind (e.g.: does this code kind deopt?). As a (very large) drive-by, refactor both Code::Kind and AbstractCode::Kind into a new CodeKind enum class. Bug: v8:8888 Change-Id: Ie858b9a53311b0731630be35cf5cd108dee95b39 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2336793 Commit-Queue: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Dominik Inführ <dinfuehr@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#69244}
552 lines
22 KiB
C++
552 lines
22 KiB
C++
// 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 "test/unittests/interpreter/interpreter-assembler-unittest.h"
|
|
|
|
#include "src/codegen/code-factory.h"
|
|
#include "src/codegen/interface-descriptors.h"
|
|
#include "src/compiler/node-properties.h"
|
|
#include "src/compiler/node.h"
|
|
#include "src/execution/isolate.h"
|
|
#include "src/objects/objects-inl.h"
|
|
#include "test/unittests/compiler/compiler-test-utils.h"
|
|
#include "test/unittests/compiler/node-test-utils.h"
|
|
|
|
using ::testing::_;
|
|
using ::testing::Eq;
|
|
using v8::internal::compiler::Node;
|
|
|
|
namespace c = v8::internal::compiler;
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace interpreter {
|
|
namespace interpreter_assembler_unittest {
|
|
|
|
InterpreterAssemblerTestState::InterpreterAssemblerTestState(
|
|
InterpreterAssemblerTest* test, Bytecode bytecode)
|
|
: compiler::CodeAssemblerState(
|
|
test->isolate(), test->zone(), InterpreterDispatchDescriptor{},
|
|
CodeKind::BYTECODE_HANDLER, Bytecodes::ToString(bytecode),
|
|
PoisoningMitigationLevel::kPoisonCriticalOnly) {}
|
|
|
|
const interpreter::Bytecode kBytecodes[] = {
|
|
#define DEFINE_BYTECODE(Name, ...) interpreter::Bytecode::k##Name,
|
|
BYTECODE_LIST(DEFINE_BYTECODE)
|
|
#undef DEFINE_BYTECODE
|
|
};
|
|
|
|
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::
|
|
~InterpreterAssemblerForTest() {
|
|
// Tests don't necessarily read and write accumulator but
|
|
// InterpreterAssembler checks accumulator uses.
|
|
if (Bytecodes::ReadsAccumulator(bytecode())) {
|
|
GetAccumulator();
|
|
}
|
|
if (Bytecodes::WritesAccumulator(bytecode())) {
|
|
SetAccumulator(NullConstant());
|
|
}
|
|
}
|
|
|
|
Matcher<Node*> InterpreterAssemblerTest::InterpreterAssemblerForTest::IsLoad(
|
|
const Matcher<c::LoadRepresentation>& rep_matcher,
|
|
const Matcher<Node*>& base_matcher, const Matcher<Node*>& index_matcher,
|
|
LoadSensitivity needs_poisoning) {
|
|
CHECK_NE(LoadSensitivity::kUnsafe, needs_poisoning);
|
|
CHECK_NE(PoisoningMitigationLevel::kPoisonAll, poisoning_level());
|
|
if (poisoning_level() == PoisoningMitigationLevel::kPoisonCriticalOnly &&
|
|
needs_poisoning == LoadSensitivity::kCritical) {
|
|
return ::i::compiler::IsPoisonedLoad(rep_matcher, base_matcher,
|
|
index_matcher, _, _);
|
|
}
|
|
return ::i::compiler::IsLoad(rep_matcher, base_matcher, index_matcher, _, _);
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsLoadFromObject(
|
|
const Matcher<c::LoadRepresentation>& rep_matcher,
|
|
const Matcher<Node*>& base_matcher, const Matcher<Node*>& index_matcher) {
|
|
CHECK_NE(PoisoningMitigationLevel::kPoisonAll, poisoning_level());
|
|
return ::i::compiler::IsLoadFromObject(rep_matcher, base_matcher,
|
|
index_matcher, _, _);
|
|
}
|
|
|
|
Matcher<Node*> InterpreterAssemblerTest::InterpreterAssemblerForTest::IsStore(
|
|
const Matcher<c::StoreRepresentation>& rep_matcher,
|
|
const Matcher<Node*>& base_matcher, const Matcher<Node*>& index_matcher,
|
|
const Matcher<Node*>& value_matcher) {
|
|
return ::i::compiler::IsStore(rep_matcher, base_matcher, index_matcher,
|
|
value_matcher, _, _);
|
|
}
|
|
|
|
Matcher<Node*> InterpreterAssemblerTest::InterpreterAssemblerForTest::IsWordNot(
|
|
const Matcher<Node*>& value_matcher) {
|
|
return kSystemPointerSize == 8
|
|
? IsWord64Xor(value_matcher, c::IsInt64Constant(-1))
|
|
: IsWord32Xor(value_matcher, c::IsInt32Constant(-1));
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsUnsignedByteOperand(
|
|
int offset, LoadSensitivity needs_poisoning) {
|
|
return IsLoad(
|
|
MachineType::Uint8(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset)),
|
|
needs_poisoning);
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsSignedByteOperand(
|
|
int offset, LoadSensitivity needs_poisoning) {
|
|
return IsLoad(
|
|
MachineType::Int8(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset)),
|
|
needs_poisoning);
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsUnsignedShortOperand(
|
|
int offset, LoadSensitivity needs_poisoning) {
|
|
if (TargetSupportsUnalignedAccess()) {
|
|
return IsLoad(
|
|
MachineType::Uint16(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset)),
|
|
needs_poisoning);
|
|
} else {
|
|
#if V8_TARGET_LITTLE_ENDIAN
|
|
const int kStep = -1;
|
|
const int kMsbOffset = 1;
|
|
#elif V8_TARGET_BIG_ENDIAN
|
|
const int kStep = 1;
|
|
const int kMsbOffset = 0;
|
|
#else
|
|
#error "Unknown Architecture"
|
|
#endif
|
|
Matcher<Node*> bytes[2];
|
|
for (int i = 0; i < static_cast<int>(arraysize(bytes)); i++) {
|
|
bytes[i] = IsLoad(
|
|
MachineType::Uint8(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset + kMsbOffset + kStep * i)),
|
|
needs_poisoning);
|
|
}
|
|
return c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[0], c::IsInt32Constant(kBitsPerByte)), bytes[1]);
|
|
}
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsSignedShortOperand(
|
|
int offset, LoadSensitivity needs_poisoning) {
|
|
if (TargetSupportsUnalignedAccess()) {
|
|
return IsLoad(
|
|
MachineType::Int16(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset)),
|
|
needs_poisoning);
|
|
} else {
|
|
#if V8_TARGET_LITTLE_ENDIAN
|
|
const int kStep = -1;
|
|
const int kMsbOffset = 1;
|
|
#elif V8_TARGET_BIG_ENDIAN
|
|
const int kStep = 1;
|
|
const int kMsbOffset = 0;
|
|
#else
|
|
#error "Unknown Architecture"
|
|
#endif
|
|
Matcher<Node*> bytes[2];
|
|
for (int i = 0; i < static_cast<int>(arraysize(bytes)); i++) {
|
|
bytes[i] = IsLoad(
|
|
(i == 0) ? MachineType::Int8() : MachineType::Uint8(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset + kMsbOffset + kStep * i)),
|
|
needs_poisoning);
|
|
}
|
|
return c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[0], c::IsInt32Constant(kBitsPerByte)), bytes[1]);
|
|
}
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsUnsignedQuadOperand(
|
|
int offset, LoadSensitivity needs_poisoning) {
|
|
if (TargetSupportsUnalignedAccess()) {
|
|
return IsLoad(
|
|
MachineType::Uint32(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset)),
|
|
needs_poisoning);
|
|
} else {
|
|
#if V8_TARGET_LITTLE_ENDIAN
|
|
const int kStep = -1;
|
|
const int kMsbOffset = 3;
|
|
#elif V8_TARGET_BIG_ENDIAN
|
|
const int kStep = 1;
|
|
const int kMsbOffset = 0;
|
|
#else
|
|
#error "Unknown Architecture"
|
|
#endif
|
|
Matcher<Node*> bytes[4];
|
|
for (int i = 0; i < static_cast<int>(arraysize(bytes)); i++) {
|
|
bytes[i] = IsLoad(
|
|
MachineType::Uint8(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset + kMsbOffset + kStep * i)),
|
|
needs_poisoning);
|
|
}
|
|
return c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[0], c::IsInt32Constant(3 * kBitsPerByte)),
|
|
c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[1], c::IsInt32Constant(2 * kBitsPerByte)),
|
|
c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[2], c::IsInt32Constant(1 * kBitsPerByte)),
|
|
bytes[3])));
|
|
}
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsSignedQuadOperand(
|
|
int offset, LoadSensitivity needs_poisoning) {
|
|
if (TargetSupportsUnalignedAccess()) {
|
|
return IsLoad(
|
|
MachineType::Int32(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset)),
|
|
needs_poisoning);
|
|
} else {
|
|
#if V8_TARGET_LITTLE_ENDIAN
|
|
const int kStep = -1;
|
|
int kMsbOffset = 3;
|
|
#elif V8_TARGET_BIG_ENDIAN
|
|
const int kStep = 1;
|
|
int kMsbOffset = 0;
|
|
#else
|
|
#error "Unknown Architecture"
|
|
#endif
|
|
Matcher<Node*> bytes[4];
|
|
for (int i = 0; i < static_cast<int>(arraysize(bytes)); i++) {
|
|
bytes[i] = IsLoad(
|
|
(i == 0) ? MachineType::Int8() : MachineType::Uint8(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrAdd(
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeOffset),
|
|
c::IsIntPtrConstant(offset + kMsbOffset + kStep * i)),
|
|
needs_poisoning);
|
|
}
|
|
return c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[0], c::IsInt32Constant(3 * kBitsPerByte)),
|
|
c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[1], c::IsInt32Constant(2 * kBitsPerByte)),
|
|
c::IsWord32Or(
|
|
c::IsWord32Shl(bytes[2], c::IsInt32Constant(1 * kBitsPerByte)),
|
|
bytes[3])));
|
|
}
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsSignedOperand(
|
|
int offset, OperandSize operand_size, LoadSensitivity needs_poisoning) {
|
|
switch (operand_size) {
|
|
case OperandSize::kByte:
|
|
return IsSignedByteOperand(offset, needs_poisoning);
|
|
case OperandSize::kShort:
|
|
return IsSignedShortOperand(offset, needs_poisoning);
|
|
case OperandSize::kQuad:
|
|
return IsSignedQuadOperand(offset, needs_poisoning);
|
|
case OperandSize::kNone:
|
|
UNREACHABLE();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Matcher<Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsUnsignedOperand(
|
|
int offset, OperandSize operand_size, LoadSensitivity needs_poisoning) {
|
|
switch (operand_size) {
|
|
case OperandSize::kByte:
|
|
return IsUnsignedByteOperand(offset, needs_poisoning);
|
|
case OperandSize::kShort:
|
|
return IsUnsignedShortOperand(offset, needs_poisoning);
|
|
case OperandSize::kQuad:
|
|
return IsUnsignedQuadOperand(offset, needs_poisoning);
|
|
case OperandSize::kNone:
|
|
UNREACHABLE();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Matcher<compiler::Node*>
|
|
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsLoadRegisterOperand(
|
|
int offset, OperandSize operand_size) {
|
|
Matcher<compiler::Node*> reg_operand = IsChangeInt32ToIntPtr(
|
|
IsSignedOperand(offset, operand_size, LoadSensitivity::kSafe));
|
|
return IsBitcastWordToTagged(IsLoad(
|
|
MachineType::Pointer(), c::IsLoadParentFramePointer(),
|
|
c::IsWordShl(reg_operand, c::IsIntPtrConstant(kSystemPointerSizeLog2)),
|
|
LoadSensitivity::kCritical));
|
|
}
|
|
|
|
TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) {
|
|
static const OperandScale kOperandScales[] = {
|
|
OperandScale::kSingle, OperandScale::kDouble, OperandScale::kQuadruple};
|
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
|
TRACED_FOREACH(interpreter::OperandScale, operand_scale, kOperandScales) {
|
|
InterpreterAssemblerTestState state(this, bytecode);
|
|
InterpreterAssemblerForTest m(&state, bytecode, operand_scale);
|
|
int number_of_operands =
|
|
interpreter::Bytecodes::NumberOfOperands(bytecode);
|
|
for (int i = 0; i < number_of_operands; i++) {
|
|
int offset = interpreter::Bytecodes::GetOperandOffset(bytecode, i,
|
|
operand_scale);
|
|
OperandType operand_type =
|
|
interpreter::Bytecodes::GetOperandType(bytecode, i);
|
|
OperandSize operand_size =
|
|
Bytecodes::SizeOfOperand(operand_type, operand_scale);
|
|
switch (interpreter::Bytecodes::GetOperandType(bytecode, i)) {
|
|
case interpreter::OperandType::kRegCount:
|
|
EXPECT_THAT(m.BytecodeOperandCount(i),
|
|
m.IsUnsignedOperand(offset, operand_size,
|
|
LoadSensitivity::kCritical));
|
|
break;
|
|
case interpreter::OperandType::kFlag8:
|
|
EXPECT_THAT(m.BytecodeOperandFlag(i),
|
|
m.IsUnsignedOperand(offset, operand_size,
|
|
LoadSensitivity::kCritical));
|
|
break;
|
|
case interpreter::OperandType::kIdx:
|
|
EXPECT_THAT(m.BytecodeOperandIdx(i),
|
|
c::IsChangeUint32ToWord(m.IsUnsignedOperand(
|
|
offset, operand_size, LoadSensitivity::kCritical)));
|
|
break;
|
|
case interpreter::OperandType::kNativeContextIndex:
|
|
EXPECT_THAT(m.BytecodeOperandNativeContextIndex(i),
|
|
c::IsChangeUint32ToWord(m.IsUnsignedOperand(
|
|
offset, operand_size, LoadSensitivity::kCritical)));
|
|
break;
|
|
case interpreter::OperandType::kUImm:
|
|
EXPECT_THAT(m.BytecodeOperandUImm(i),
|
|
m.IsUnsignedOperand(offset, operand_size,
|
|
LoadSensitivity::kCritical));
|
|
break;
|
|
case interpreter::OperandType::kImm: {
|
|
EXPECT_THAT(m.BytecodeOperandImm(i),
|
|
m.IsSignedOperand(offset, operand_size,
|
|
LoadSensitivity::kCritical));
|
|
break;
|
|
}
|
|
case interpreter::OperandType::kRuntimeId:
|
|
EXPECT_THAT(m.BytecodeOperandRuntimeId(i),
|
|
m.IsUnsignedOperand(offset, operand_size,
|
|
LoadSensitivity::kCritical));
|
|
break;
|
|
case interpreter::OperandType::kIntrinsicId:
|
|
EXPECT_THAT(m.BytecodeOperandIntrinsicId(i),
|
|
m.IsUnsignedOperand(offset, operand_size,
|
|
LoadSensitivity::kCritical));
|
|
break;
|
|
case interpreter::OperandType::kRegList:
|
|
case interpreter::OperandType::kReg:
|
|
case interpreter::OperandType::kRegPair:
|
|
case interpreter::OperandType::kRegOut:
|
|
case interpreter::OperandType::kRegOutList:
|
|
case interpreter::OperandType::kRegOutPair:
|
|
case interpreter::OperandType::kRegOutTriple:
|
|
EXPECT_THAT(m.LoadRegisterAtOperandIndex(i),
|
|
m.IsLoadRegisterOperand(offset, operand_size));
|
|
break;
|
|
case interpreter::OperandType::kNone:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TARGET_TEST_F(InterpreterAssemblerTest, GetContext) {
|
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
|
InterpreterAssemblerTestState state(this, bytecode);
|
|
InterpreterAssemblerForTest m(&state, bytecode);
|
|
EXPECT_THAT(
|
|
m.GetContext(),
|
|
IsBitcastWordToTagged(m.IsLoad(
|
|
MachineType::Pointer(), c::IsLoadParentFramePointer(),
|
|
c::IsIntPtrConstant(Register::current_context().ToOperand() *
|
|
kSystemPointerSize))));
|
|
}
|
|
}
|
|
|
|
TARGET_TEST_F(InterpreterAssemblerTest, LoadConstantPoolEntry) {
|
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
|
InterpreterAssemblerTestState state(this, bytecode);
|
|
InterpreterAssemblerForTest m(&state, bytecode);
|
|
{
|
|
TNode<IntPtrT> index = m.IntPtrConstant(2);
|
|
TNode<Object> load_constant = m.LoadConstantPoolEntry(index);
|
|
Matcher<Node*> constant_pool_matcher = m.IsLoadFromObject(
|
|
MachineType::AnyTagged(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrConstant(BytecodeArray::kConstantPoolOffset -
|
|
kHeapObjectTag));
|
|
EXPECT_THAT(
|
|
load_constant,
|
|
m.IsLoad(MachineType::AnyTagged(), constant_pool_matcher,
|
|
c::IsIntPtrConstant(FixedArray::OffsetOfElementAt(2) -
|
|
kHeapObjectTag),
|
|
LoadSensitivity::kCritical));
|
|
}
|
|
{
|
|
Node* index = m.Parameter(2);
|
|
TNode<Object> load_constant =
|
|
m.LoadConstantPoolEntry(m.ReinterpretCast<IntPtrT>(index));
|
|
Matcher<Node*> constant_pool_matcher = m.IsLoadFromObject(
|
|
MachineType::AnyTagged(),
|
|
c::IsParameter(InterpreterDispatchDescriptor::kBytecodeArray),
|
|
c::IsIntPtrConstant(BytecodeArray::kConstantPoolOffset -
|
|
kHeapObjectTag));
|
|
EXPECT_THAT(
|
|
load_constant,
|
|
m.IsLoad(
|
|
MachineType::AnyTagged(), constant_pool_matcher,
|
|
c::IsIntPtrAdd(
|
|
c::IsIntPtrConstant(FixedArray::kHeaderSize -
|
|
kHeapObjectTag),
|
|
c::IsWordShl(index, c::IsIntPtrConstant(kTaggedSizeLog2))),
|
|
LoadSensitivity::kCritical));
|
|
}
|
|
}
|
|
}
|
|
|
|
TARGET_TEST_F(InterpreterAssemblerTest, LoadObjectField) {
|
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
|
InterpreterAssemblerTestState state(this, bytecode);
|
|
InterpreterAssemblerForTest m(&state, bytecode);
|
|
TNode<HeapObject> object =
|
|
m.ReinterpretCast<HeapObject>(m.IntPtrConstant(0xDEADBEEF));
|
|
int offset = 16;
|
|
TNode<Object> load_field = m.LoadObjectField(object, offset);
|
|
EXPECT_THAT(
|
|
load_field,
|
|
m.IsLoadFromObject(MachineType::AnyTagged(), Eq(object),
|
|
c::IsIntPtrConstant(offset - kHeapObjectTag)));
|
|
}
|
|
}
|
|
|
|
TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime2) {
|
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
|
InterpreterAssemblerTestState state(this, bytecode);
|
|
InterpreterAssemblerForTest m(&state, bytecode);
|
|
TNode<Object> arg1 = m.ReinterpretCast<Object>(m.Int32Constant(2));
|
|
TNode<Object> arg2 = m.ReinterpretCast<Object>(m.Int32Constant(3));
|
|
TNode<Object> context = m.ReinterpretCast<Object>(m.Int32Constant(4));
|
|
TNode<Object> call_runtime =
|
|
m.CallRuntime(Runtime::kAdd, context, arg1, arg2);
|
|
EXPECT_THAT(call_runtime,
|
|
c::IsCall(_, _, Eq(arg1), Eq(arg2), _, c::IsInt32Constant(2),
|
|
Eq(context), _, _));
|
|
}
|
|
}
|
|
|
|
TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime) {
|
|
const int kResultSizes[] = {1, 2};
|
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
|
TRACED_FOREACH(int, result_size, kResultSizes) {
|
|
if (Bytecodes::IsCallRuntime(bytecode)) {
|
|
InterpreterAssemblerTestState state(this, bytecode);
|
|
InterpreterAssemblerForTest m(&state, bytecode);
|
|
Callable builtin =
|
|
CodeFactory::InterpreterCEntry(isolate(), result_size);
|
|
|
|
TNode<Uint32T> function_id = m.Uint32Constant(0);
|
|
InterpreterAssembler::RegListNodePair registers(m.IntPtrConstant(1),
|
|
m.Int32Constant(2));
|
|
TNode<Context> context = m.ReinterpretCast<Context>(m.Int32Constant(4));
|
|
|
|
Matcher<Node*> function_table = c::IsExternalConstant(
|
|
ExternalReference::runtime_function_table_address_for_unittests(
|
|
isolate()));
|
|
Matcher<Node*> function =
|
|
c::IsIntPtrAdd(function_table,
|
|
c::IsChangeUint32ToWord(c::IsInt32Mul(
|
|
Eq(function_id),
|
|
c::IsInt32Constant(sizeof(Runtime::Function)))));
|
|
Matcher<Node*> function_entry =
|
|
m.IsLoad(MachineType::Pointer(), function,
|
|
c::IsIntPtrConstant(offsetof(Runtime::Function, entry)));
|
|
|
|
Node* call_runtime =
|
|
m.CallRuntimeN(function_id, context, registers, result_size);
|
|
EXPECT_THAT(call_runtime,
|
|
c::IsCall(_, c::IsHeapConstant(builtin.code()),
|
|
Eq(registers.reg_count()),
|
|
Eq(registers.base_reg_location()), function_entry,
|
|
Eq(context), _, _));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TARGET_TEST_F(InterpreterAssemblerTest, LoadFeedbackVector) {
|
|
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
|
|
InterpreterAssemblerTestState state(this, bytecode);
|
|
InterpreterAssemblerForTest m(&state, bytecode);
|
|
TNode<HeapObject> feedback_vector = m.LoadFeedbackVector();
|
|
|
|
// Feedback vector is a phi node with two inputs. One of them is loading the
|
|
// feedback vector and the other is undefined constant (when feedback
|
|
// vectors aren't allocated). Find the input that loads feedback vector.
|
|
CHECK_EQ(static_cast<Node*>(feedback_vector)->opcode(),
|
|
i::compiler::IrOpcode::kPhi);
|
|
Node* value0 =
|
|
i::compiler::NodeProperties::GetValueInput(feedback_vector, 0);
|
|
Node* value1 =
|
|
i::compiler::NodeProperties::GetValueInput(feedback_vector, 1);
|
|
Node* load_feedback_vector = value0;
|
|
if (value0->opcode() == i::compiler::IrOpcode::kHeapConstant) {
|
|
load_feedback_vector = value1;
|
|
}
|
|
|
|
Matcher<Node*> load_function_matcher = IsBitcastWordToTagged(
|
|
m.IsLoad(MachineType::Pointer(), c::IsLoadParentFramePointer(),
|
|
c::IsIntPtrConstant(Register::function_closure().ToOperand() *
|
|
kSystemPointerSize)));
|
|
Matcher<Node*> load_vector_cell_matcher = m.IsLoadFromObject(
|
|
MachineType::TaggedPointer(), load_function_matcher,
|
|
c::IsIntPtrConstant(JSFunction::kFeedbackCellOffset -
|
|
kHeapObjectTag));
|
|
EXPECT_THAT(
|
|
load_feedback_vector,
|
|
m.IsLoadFromObject(
|
|
MachineType::TaggedPointer(), load_vector_cell_matcher,
|
|
c::IsIntPtrConstant(Cell::kValueOffset - kHeapObjectTag)));
|
|
}
|
|
}
|
|
|
|
} // namespace interpreter_assembler_unittest
|
|
} // namespace interpreter
|
|
} // namespace internal
|
|
} // namespace v8
|