[Interpreter] Add support for property load operations.

Adds support for property load operations via Load/KeyedLoad ICs. Adds the
following bytecodes:
 - LoadIC
 - KeyedLoadIC
Also adds support to the interpreter assembler for loading the type feedback
vector from the function on the stack, and calling ICs.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1309843007

Cr-Commit-Position: refs/heads/master@{#30543}
This commit is contained in:
rmcilroy 2015-09-02 06:03:06 -07:00 committed by Commit bot
parent c29a4061c9
commit d8df7468b4
15 changed files with 531 additions and 95 deletions

View File

@ -14,6 +14,7 @@
#include "src/compiler/raw-machine-assembler.h"
#include "src/compiler/schedule.h"
#include "src/frames.h"
#include "src/interface-descriptors.h"
#include "src/interpreter/bytecodes.h"
#include "src/macro-assembler.h"
#include "src/zone.h"
@ -223,6 +224,35 @@ Node* InterpreterAssembler::LoadContextSlot(int slot_index) {
}
Node* InterpreterAssembler::LoadTypeFeedbackVector() {
Node* function = raw_assembler_->Load(
kMachAnyTagged, RegisterFileRawPointer(),
IntPtrConstant(InterpreterFrameConstants::kFunctionFromRegisterPointer));
Node* shared_info =
LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset);
Node* vector =
LoadObjectField(shared_info, SharedFunctionInfo::kFeedbackVectorOffset);
return vector;
}
Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
Node* target, Node* arg1, Node* arg2,
Node* arg3, Node* arg4) {
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), descriptor, 0, CallDescriptor::kNoFlags);
Node** args = zone()->NewArray<Node*>(5);
args[0] = arg1;
args[1] = arg2;
args[2] = arg3;
args[3] = arg4;
args[4] = ContextTaggedPointer();
return raw_assembler_->CallN(call_descriptor, target, args);
}
Node* InterpreterAssembler::CallJSBuiltin(int context_index, Node* receiver,
Node** js_args, int js_arg_count) {
Node* global_object = LoadContextSlot(Context::GLOBAL_OBJECT_INDEX);

View File

@ -16,6 +16,7 @@
namespace v8 {
namespace internal {
class CallInterfaceDescriptor;
class Isolate;
class Zone;
@ -76,6 +77,13 @@ class InterpreterAssembler {
// Load |slot_index| from the current context.
Node* LoadContextSlot(int slot_index);
// Load the TypeFeedbackVector for the current function.
Node* LoadTypeFeedbackVector();
// Call an IC code stub.
Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node* arg1,
Node* arg2, Node* arg3, Node* arg4);
// Call JS builtin.
Node* CallJSBuiltin(int context_index, Node* receiver);
Node* CallJSBuiltin(int context_index, Node* receiver, Node* arg1);

View File

@ -180,6 +180,7 @@ class InterpreterFrameConstants : public AllStatic {
// Register file pointer relative.
static const int kLastParamFromRegisterPointer =
StandardFrameConstants::kFixedFrameSize + kPointerSize;
static const int kFunctionFromRegisterPointer = kPointerSize;
};

View File

@ -91,7 +91,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) {
size_t entry = GetConstantPoolEntry(object);
if (entry <= 255) {
if (FitsInByteOperand(entry)) {
Output(Bytecode::kLdaConstant, static_cast<uint8_t>(entry));
} else {
UNIMPLEMENTED();
@ -144,6 +144,38 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
Register object, int feedback_slot, LanguageMode language_mode) {
if (is_strong(language_mode)) {
UNIMPLEMENTED();
}
if (FitsInByteOperand(feedback_slot)) {
Output(Bytecode::kLoadIC, object.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
Register object, int feedback_slot, LanguageMode language_mode) {
if (is_strong(language_mode)) {
UNIMPLEMENTED();
}
if (FitsInByteOperand(feedback_slot)) {
Output(Bytecode::kKeyedLoadIC, object.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
UNIMPLEMENTED();
}
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
Output(Bytecode::kReturn);
return *this;
@ -194,9 +226,8 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index,
case OperandType::kNone:
return false;
case OperandType::kImm8:
return true;
case OperandType::kIdx:
return operand_value < constants_.size();
return true;
case OperandType::kReg: {
int reg_index = Register::FromOperand(operand_value).index();
return (reg_index >= 0 && reg_index < temporary_register_next_) ||
@ -267,6 +298,18 @@ Bytecode BytecodeArrayBuilder::BytecodeForBinaryOperation(Token::Value op) {
}
// static
bool BytecodeArrayBuilder::FitsInByteOperand(int value) {
return 0 <= value && value <= 255;
}
// static
bool BytecodeArrayBuilder::FitsInByteOperand(size_t value) {
return value <= 255;
}
TemporaryRegisterScope::TemporaryRegisterScope(BytecodeArrayBuilder* builder)
: builder_(builder), count_(0), last_register_index_(-1) {}

View File

@ -51,6 +51,12 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg);
// Load properties. The property name should be in the accumulator.
BytecodeArrayBuilder& LoadNamedProperty(Register object, int feedback_slot,
LanguageMode language_mode);
BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot,
LanguageMode language_mode);
// Operators.
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
@ -62,6 +68,8 @@ class BytecodeArrayBuilder {
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
static Bytecode BytecodeForBinaryOperation(Token::Value op);
static bool FitsInByteOperand(int value);
static bool FitsInByteOperand(size_t value);
void Output(Bytecode bytecode, uint8_t r0, uint8_t r1, uint8_t r2);
void Output(Bytecode bytecode, uint8_t r0, uint8_t r1);

View File

@ -25,6 +25,7 @@ BytecodeGenerator::~BytecodeGenerator() {}
Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
set_info(info);
set_scope(info->scope());
// This a temporary guard (oth).
@ -45,6 +46,7 @@ Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
VisitStatements(info->literal()->body());
set_scope(nullptr);
set_info(nullptr);
return builder_.ToBytecodeArray();
}
@ -84,17 +86,17 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
}
void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* node) {
void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitImportDeclaration(ImportDeclaration* node) {
void BytecodeGenerator::VisitImportDeclaration(ImportDeclaration* decl) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitExportDeclaration(ExportDeclaration* node) {
void BytecodeGenerator::VisitExportDeclaration(ExportDeclaration* decl) {
UNIMPLEMENTED();
}
@ -104,36 +106,36 @@ void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
}
void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* node) {
void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitIfStatement(IfStatement* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitContinueStatement(ContinueStatement* node) {
void BytecodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitBreakStatement(BreakStatement* node) {
void BytecodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* node) {
Visit(node->expression());
void BytecodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
Visit(stmt->expression());
builder().Return();
}
void BytecodeGenerator::VisitWithStatement(WithStatement* node) {
void BytecodeGenerator::VisitWithStatement(WithStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
UNIMPLEMENTED();
}
@ -141,63 +143,63 @@ void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
void BytecodeGenerator::VisitCaseClause(CaseClause* clause) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* node) {
void BytecodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitWhileStatement(WhileStatement* node) {
void BytecodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitForStatement(ForStatement* node) {
void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitForInStatement(ForInStatement* node) {
void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitForOfStatement(ForOfStatement* node) {
void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* node) {
void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* node) {
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitClassLiteral(ClassLiteral* node) {
void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitNativeFunctionLiteral(
NativeFunctionLiteral* node) {
NativeFunctionLiteral* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitConditional(Conditional* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitConditional(Conditional* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitLiteral(Literal* expr) {
@ -220,17 +222,17 @@ void BytecodeGenerator::VisitLiteral(Literal* expr) {
}
void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
void BytecodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
void BytecodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
UNIMPLEMENTED();
}
@ -286,30 +288,59 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
}
void BytecodeGenerator::VisitYield(Yield* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitThrow(Throw* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitThrow(Throw* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitProperty(Property* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitProperty(Property* expr) {
LhsKind property_kind = Property::GetAssignType(expr);
FeedbackVectorICSlot slot = expr->PropertyFeedbackSlot();
switch (property_kind) {
case VARIABLE:
UNREACHABLE();
break;
case NAMED_PROPERTY: {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register obj = temporary_register_scope.NewRegister();
Visit(expr->obj());
builder().StoreAccumulatorInRegister(obj);
builder().LoadLiteral(expr->key()->AsLiteral()->AsPropertyName());
builder().LoadNamedProperty(obj, feedback_index(slot), language_mode());
break;
}
case KEYED_PROPERTY: {
TemporaryRegisterScope temporary_register_scope(&builder_);
Register obj = temporary_register_scope.NewRegister();
Visit(expr->obj());
builder().StoreAccumulatorInRegister(obj);
Visit(expr->key());
builder().LoadKeyedProperty(obj, feedback_index(slot), language_mode());
break;
}
case NAMED_SUPER_PROPERTY:
case KEYED_SUPER_PROPERTY:
UNIMPLEMENTED();
}
}
void BytecodeGenerator::VisitCall(Call* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCall(Call* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCallNew(CallNew* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCallNew(CallNew* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCallRuntime(CallRuntime* node) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitCountOperation(CountOperation* node) {
void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
UNIMPLEMENTED();
}
@ -328,31 +359,31 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
}
void BytecodeGenerator::VisitCompareOperation(CompareOperation* node) {
void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitSpread(Spread* node) { UNREACHABLE(); }
void BytecodeGenerator::VisitSpread(Spread* expr) { UNREACHABLE(); }
void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* node) {
void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) {
UNREACHABLE();
}
void BytecodeGenerator::VisitThisFunction(ThisFunction* node) {
void BytecodeGenerator::VisitThisFunction(ThisFunction* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitSuperCallReference(SuperCallReference* node) {
void BytecodeGenerator::VisitSuperCallReference(SuperCallReference* expr) {
UNIMPLEMENTED();
}
void BytecodeGenerator::VisitSuperPropertyReference(
SuperPropertyReference* node) {
SuperPropertyReference* expr) {
UNIMPLEMENTED();
}
@ -371,6 +402,16 @@ void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* binop) {
builder().BinaryOperation(op, temporary);
}
LanguageMode BytecodeGenerator::language_mode() const {
return info()->language_mode();
}
int BytecodeGenerator::feedback_index(FeedbackVectorICSlot slot) const {
return info()->feedback_vector()->GetIndex(slot);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -31,9 +31,15 @@ class BytecodeGenerator : public AstVisitor {
inline BytecodeArrayBuilder& builder() { return builder_; }
inline Scope* scope() const { return scope_; }
inline void set_scope(Scope* s) { scope_ = s; }
inline void set_scope(Scope* scope) { scope_ = scope; }
inline CompilationInfo* info() const { return info_; }
inline void set_info(CompilationInfo* info) { info_ = info; }
LanguageMode language_mode() const;
int feedback_index(FeedbackVectorICSlot slot) const;
BytecodeArrayBuilder builder_;
CompilationInfo* info_;
Scope* scope_;
};

View File

@ -23,30 +23,34 @@ namespace interpreter {
V(Reg)
// The list of bytecodes which are interpreted by the interpreter.
#define BYTECODE_LIST(V) \
\
/* Loading the accumulator */ \
V(LdaZero, OperandType::kNone) \
V(LdaSmi8, OperandType::kImm8) \
V(LdaConstant, OperandType::kIdx) \
V(LdaUndefined, OperandType::kNone) \
V(LdaNull, OperandType::kNone) \
V(LdaTheHole, OperandType::kNone) \
V(LdaTrue, OperandType::kNone) \
V(LdaFalse, OperandType::kNone) \
\
/* Register-accumulator transfers */ \
V(Ldar, OperandType::kReg) \
V(Star, OperandType::kReg) \
\
/* Binary Operators */ \
V(Add, OperandType::kReg) \
V(Sub, OperandType::kReg) \
V(Mul, OperandType::kReg) \
V(Div, OperandType::kReg) \
V(Mod, OperandType::kReg) \
\
/* Control Flow */ \
#define BYTECODE_LIST(V) \
\
/* Loading the accumulator */ \
V(LdaZero, OperandType::kNone) \
V(LdaSmi8, OperandType::kImm8) \
V(LdaConstant, OperandType::kIdx) \
V(LdaUndefined, OperandType::kNone) \
V(LdaNull, OperandType::kNone) \
V(LdaTheHole, OperandType::kNone) \
V(LdaTrue, OperandType::kNone) \
V(LdaFalse, OperandType::kNone) \
\
/* Register-accumulator transfers */ \
V(Ldar, OperandType::kReg) \
V(Star, OperandType::kReg) \
\
/* LoadIC operations */ \
V(LoadIC, OperandType::kReg, OperandType::kIdx) \
V(KeyedLoadIC, OperandType::kReg, OperandType::kIdx) \
\
/* Binary Operators */ \
V(Add, OperandType::kReg) \
V(Sub, OperandType::kReg) \
V(Mul, OperandType::kReg) \
V(Div, OperandType::kReg) \
V(Mod, OperandType::kReg) \
\
/* Control Flow */ \
V(Return, OperandType::kNone)

View File

@ -4,6 +4,7 @@
#include "src/interpreter/interpreter.h"
#include "src/code-factory.h"
#include "src/compiler.h"
#include "src/compiler/interpreter-assembler.h"
#include "src/factory.h"
@ -61,6 +62,7 @@ bool Interpreter::MakeBytecode(CompilationInfo* info) {
Handle<SharedFunctionInfo> shared_info = info->shared_info();
BytecodeGenerator generator(info->isolate(), info->zone());
info->EnsureFeedbackVector();
Handle<BytecodeArray> bytecodes = generator.MakeBytecode(info);
if (FLAG_print_bytecode) {
bytecodes->Print();
@ -73,7 +75,6 @@ bool Interpreter::MakeBytecode(CompilationInfo* info) {
shared_info->set_function_data(*bytecodes);
info->SetCode(info->isolate()->builtins()->InterpreterEntryTrampoline());
info->EnsureFeedbackVector();
return true;
}
@ -190,6 +191,44 @@ void Interpreter::DoStar(compiler::InterpreterAssembler* assembler) {
}
void Interpreter::DoPropertyLoadIC(Callable ic,
compiler::InterpreterAssembler* assembler) {
Node* code_target = __ HeapConstant(ic.code());
Node* reg_index = __ BytecodeOperandReg(0);
Node* object = __ LoadRegister(reg_index);
Node* name = __ GetAccumulator();
Node* raw_slot = __ BytecodeOperandIdx(1);
Node* smi_slot = __ SmiTag(raw_slot);
Node* type_feedback_vector = __ LoadTypeFeedbackVector();
Node* result = __ CallIC(ic.descriptor(), code_target, object, name, smi_slot,
type_feedback_vector);
__ SetAccumulator(result);
__ Dispatch();
}
// LoadIC <object> <slot>
//
// Calls the LoadIC at FeedBackVector slot <slot> for <object> and the name
// in the accumulator.
void Interpreter::DoLoadIC(compiler::InterpreterAssembler* assembler) {
Callable ic = CodeFactory::LoadICInOptimizedCode(isolate_, NOT_INSIDE_TYPEOF,
SLOPPY, UNINITIALIZED);
DoPropertyLoadIC(ic, assembler);
}
// KeyedLoadIC <object> <slot>
//
// Calls the LoadIC at FeedBackVector slot <slot> for <object> and the key
// in the accumulator.
void Interpreter::DoKeyedLoadIC(compiler::InterpreterAssembler* assembler) {
Callable ic =
CodeFactory::KeyedLoadICInOptimizedCode(isolate_, SLOPPY, UNINITIALIZED);
DoPropertyLoadIC(ic, assembler);
}
void Interpreter::DoBinaryOp(int builtin_context_index,
compiler::InterpreterAssembler* assembler) {
// TODO(rmcilroy): Call ICs which back-patch bytecode with type specialized

View File

@ -16,6 +16,7 @@ namespace v8 {
namespace internal {
class Isolate;
class Callable;
class CompilationInfo;
namespace compiler {
@ -51,6 +52,9 @@ class Interpreter {
void DoBinaryOp(int builtin_context_index,
compiler::InterpreterAssembler* assembler);
// Generates code to perform a property load via |ic|.
void DoPropertyLoadIC(Callable ic, compiler::InterpreterAssembler* assembler);
bool IsInterpreterTableInitialized(Handle<FixedArray> handler_table);
Isolate* isolate_;

View File

@ -17,6 +17,9 @@ class BytecodeGeneratorHelper {
public:
const char* kFunctionName = "f";
const int kLastParamIndex =
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
BytecodeGeneratorHelper() {
i::FLAG_ignition = true;
i::FLAG_ignition_filter = kFunctionName;
@ -24,6 +27,9 @@ class BytecodeGeneratorHelper {
}
Factory* factory() { return CcTest::i_isolate()->factory(); }
Handle<BytecodeArray> MakeBytecode(const char* script,
const char* function_name) {
CompileRun(script);
@ -52,11 +58,11 @@ class BytecodeGeneratorHelper {
// Structure for containing expected bytecode snippets.
template<typename T>
struct ExpectedSnippet {
const char* body;
const char* code_snippet;
int frame_size;
int parameter_count;
int bytecode_length;
const uint8_t bytecode[16];
const uint8_t bytecode[24];
int constant_count;
T constants[16];
};
@ -87,7 +93,7 @@ TEST(PrimitiveReturnStatements) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecodeForFunctionBody(snippets[i].body);
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
@ -134,7 +140,7 @@ TEST(PrimitiveExpressions) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecodeForFunctionBody(snippets[i].body);
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
@ -149,24 +155,23 @@ TEST(Parameters) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
int last_param_index =
-InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
ExpectedSnippet<void*> snippets[] = {
{"function f() { return this; }",
0, 1, 3, {B(Ldar), R(last_param_index), B(Return)}, 0},
0, 1, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0},
{"function f(arg1) { return arg1; }",
0, 2, 3, {B(Ldar), R(last_param_index), B(Return)}, 0},
0, 2, 3, {B(Ldar), R(helper.kLastParamIndex), B(Return)}, 0},
{"function f(arg1) { return this; }",
0, 2, 3, {B(Ldar), R(last_param_index - 1), B(Return)}, 0},
0, 2, 3, {B(Ldar), R(helper.kLastParamIndex - 1), B(Return)}, 0},
{"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return arg4; }",
0, 8, 3, {B(Ldar), R(last_param_index - 3), B(Return)}, 0},
0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 3), B(Return)}, 0},
{"function f(arg1, arg2, arg3, arg4, arg5, arg6, arg7) { return this; }",
0, 8, 3, {B(Ldar), R(last_param_index - 7), B(Return)}, 0}
0, 8, 3, {B(Ldar), R(helper.kLastParamIndex - 7), B(Return)}, 0}
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba = helper.MakeBytecodeForFunction(snippets[i].body);
Handle<BytecodeArray> ba =
helper.MakeBytecodeForFunction(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
@ -212,7 +217,7 @@ TEST(Constants) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecodeForFunctionBody(snippets[i].body);
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
@ -261,7 +266,7 @@ TEST(Constants) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecodeForFunctionBody(snippets[i].body);
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
@ -308,7 +313,7 @@ TEST(Constants) {
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecodeForFunctionBody(snippets[i].body);
helper.MakeBytecodeForFunctionBody(snippets[i].code_snippet);
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
@ -316,9 +321,8 @@ TEST(Constants) {
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected =
CcTest::i_isolate()->factory()->NewStringFromAsciiChecked(
snippets[i].constants[j]);
Handle<String> expected = helper.factory()->NewStringFromAsciiChecked(
snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
}
@ -326,6 +330,94 @@ TEST(Constants) {
}
TEST(PropertyLoads) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
Code::Kind ic_kinds[] = { i::Code::LOAD_IC, i::Code::LOAD_IC };
FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
helper.factory()->NewTypeFeedbackVector(&feedback_spec);
ExpectedSnippet<const char*> snippets[] = {
{"function f(a) { return a.name; }\nf({name : \"test\"})",
1 * kPointerSize, 2, 10,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(0),
B(LdaConstant), U8(0),
B(LoadIC), R(0), U8(vector->first_ic_slot_index()),
B(Return)
},
1, { "name" }
},
{"function f(a) { return a[\"key\"]; }\nf({key : \"test\"})",
1 * kPointerSize, 2, 10,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(0),
B(LdaConstant), U8(0),
B(LoadIC), R(0), U8(vector->first_ic_slot_index()),
B(Return)
},
1, { "key" }
},
{"function f(a) { return a[100]; }\nf({100 : \"test\"})",
1 * kPointerSize, 2, 10,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(0),
B(LdaSmi8), U8(100),
B(KeyedLoadIC), R(0), U8(vector->first_ic_slot_index()),
B(Return)
}, 0
},
{"function f(a, b) { return a[b]; }\nf({arg : \"test\"}, \"arg\")",
1 * kPointerSize, 3, 10,
{
B(Ldar), R(helper.kLastParamIndex - 1),
B(Star), R(0),
B(Ldar), R(helper.kLastParamIndex),
B(KeyedLoadIC), R(0), U8(vector->first_ic_slot_index()),
B(Return)
}, 0
},
{"function f(a) { var b = a.name; return a[-124]; }\n"
"f({\"-124\" : \"test\", name : 123 })",
2 * kPointerSize, 2, 21,
{
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(1),
B(LdaConstant), U8(0),
B(LoadIC), R(1), U8(vector->first_ic_slot_index()),
B(Star), R(0),
B(Ldar), R(helper.kLastParamIndex),
B(Star), R(1),
B(LdaSmi8), U8(-124),
B(KeyedLoadIC), R(1), U8(vector->first_ic_slot_index() + 2),
B(Return)
},
1, { "name" }
}
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> ba =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CHECK_EQ(ba->frame_size(), snippets[i].frame_size);
CHECK_EQ(ba->parameter_count(), snippets[i].parameter_count);
CHECK_EQ(ba->length(), snippets[i].bytecode_length);
CHECK(!memcmp(ba->GetFirstBytecodeAddress(), snippets[i].bytecode,
ba->length()));
CHECK_EQ(ba->constant_pool()->length(), snippets[i].constant_count);
for (int j = 0; j < snippets[i].constant_count; j++) {
Handle<String> expected = helper.factory()->NewStringFromAsciiChecked(
snippets[i].constants[j]);
CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected));
}
}
}
} // namespace interpreter
} // namespace internal
} // namespance v8

View File

@ -53,8 +53,12 @@ class InterpreterCallable {
class InterpreterTester {
public:
InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode)
: isolate_(isolate), bytecode_(bytecode) {
InterpreterTester(Isolate* isolate, Handle<BytecodeArray> bytecode,
MaybeHandle<TypeFeedbackVector> feedback_vector =
MaybeHandle<TypeFeedbackVector>())
: isolate_(isolate),
bytecode_(bytecode),
feedback_vector_(feedback_vector) {
i::FLAG_ignition = true;
// Ensure handler table is generated.
isolate->interpreter()->Initialize();
@ -66,9 +70,14 @@ class InterpreterTester {
return InterpreterCallable<A...>(isolate_, GetBytecodeFunction<A...>());
}
Handle<Object> NewObject(const char* script) {
return v8::Utils::OpenHandle(*CompileRun(script));
}
private:
Isolate* isolate_;
Handle<BytecodeArray> bytecode_;
MaybeHandle<TypeFeedbackVector> feedback_vector_;
template <class... A>
Handle<JSFunction> GetBytecodeFunction() {
@ -83,6 +92,10 @@ class InterpreterTester {
*v8::Handle<v8::Function>::Cast(CompileRun(function_text.c_str())));
function->ReplaceCode(*isolate_->builtins()->InterpreterEntryTrampoline());
function->shared()->set_function_data(*bytecode_);
if (!feedback_vector_.is_null()) {
function->shared()->set_feedback_vector(
*feedback_vector_.ToHandleChecked());
}
return function;
}
@ -400,7 +413,7 @@ TEST(InterpreterMod) {
TEST(InterpreterParameter1) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_locals_count(0);
builder.set_parameter_count(1);
builder.LoadAccumulatorWithRegister(builder.Parameter(0)).Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
@ -423,7 +436,7 @@ TEST(InterpreterParameter1) {
TEST(InterpreterParameter8) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_locals_count(0);
builder.set_parameter_count(8);
builder.LoadAccumulatorWithRegister(builder.Parameter(0))
.BinaryOperation(Token::Value::ADD, builder.Parameter(1))
@ -454,3 +467,94 @@ TEST(InterpreterParameter8) {
.ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(36));
}
TEST(InterpreterLoadNamedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Code::Kind ic_kinds[] = { i::Code::LOAD_IC };
i::FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
factory->NewTypeFeedbackVector(&feedback_spec);
Handle<i::String> name = factory->NewStringFromAsciiChecked("val");
name = factory->string_table()->LookupString(isolate, name);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(0);
builder.set_parameter_count(1);
builder.LoadLiteral(name)
.LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(),
i::SLOPPY)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ val : 123 })");
// Test IC miss.
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to monomorphic IC.
return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to polymorphic IC.
Handle<Object> object2 = tester.NewObject("({ val : 456, other : 123 })");
return_val = callable(object2).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(456));
// Test transition to megamorphic IC.
Handle<Object> object3 = tester.NewObject("({ val : 789, val2 : 123 })");
callable(object3).ToHandleChecked();
Handle<Object> object4 = tester.NewObject("({ val : 789, val3 : 123 })");
callable(object4).ToHandleChecked();
Handle<Object> object5 = tester.NewObject("({ val : 789, val4 : 123 })");
return_val = callable(object5).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}
TEST(InterpreterLoadKeyedProperty) {
HandleAndZoneScope handles;
i::Isolate* isolate = handles.main_isolate();
i::Factory* factory = isolate->factory();
i::Code::Kind ic_kinds[] = { i::Code::KEYED_LOAD_IC };
i::FeedbackVectorSpec feedback_spec(0, 1, ic_kinds);
Handle<i::TypeFeedbackVector> vector =
factory->NewTypeFeedbackVector(&feedback_spec);
Handle<i::String> key = factory->NewStringFromAsciiChecked("key");
key = factory->string_table()->LookupString(isolate, key);
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(1);
builder.set_parameter_count(1);
builder.LoadLiteral(key)
.LoadKeyedProperty(builder.Parameter(0), vector->first_ic_slot_index(),
i::SLOPPY)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array, vector);
auto callable = tester.GetCallable<Handle<Object>>();
Handle<Object> object = tester.NewObject("({ key : 123 })");
// Test IC miss.
Handle<Object> return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to monomorphic IC.
return_val = callable(object).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(123));
// Test transition to megamorphic IC.
Handle<Object> object3 = tester.NewObject("({ key : 789, val2 : 123 })");
return_val = callable(object3).ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(789));
}

View File

@ -6,6 +6,7 @@
#include "src/compiler/graph.h"
#include "src/compiler/node.h"
#include "src/interface-descriptors.h"
#include "src/isolate.h"
#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/node-test-utils.h"
@ -75,6 +76,14 @@ Matcher<Node*> InterpreterAssemblerTest::InterpreterAssemblerForTest::IsStore(
}
template <class... A>
Matcher<Node*> InterpreterAssemblerTest::InterpreterAssemblerForTest::IsCall(
const Matcher<const CallDescriptor*>& descriptor_matcher, A... args) {
return ::i::compiler::IsCall(descriptor_matcher, args..., graph()->start(),
graph()->start());
}
Matcher<Node*>
InterpreterAssemblerTest::InterpreterAssemblerForTest::IsBytecodeOperand(
int operand) {
@ -339,15 +348,53 @@ TARGET_TEST_F(InterpreterAssemblerTest, CallJSBuiltin) {
m.IsLoad(kMachAnyTagged, function_matcher,
IsIntPtrConstant(JSFunction::kContextOffset - kHeapObjectTag));
EXPECT_THAT(call_js_builtin_0,
IsCall(_, function_matcher, receiver, context_matcher,
m.graph()->start(), m.graph()->start()));
m.IsCall(_, function_matcher, receiver, context_matcher));
Node* arg1 = m.Int32Constant(0xabcd);
Node* call_js_builtin_1 =
m.CallJSBuiltin(Context::SUB_BUILTIN_INDEX, receiver, arg1);
EXPECT_THAT(call_js_builtin_1,
IsCall(_, function_matcher, receiver, arg1, context_matcher,
m.graph()->start(), m.graph()->start()));
m.IsCall(_, function_matcher, receiver, arg1, context_matcher));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, CallIC) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
LoadWithVectorDescriptor descriptor(isolate());
Node* target = m.Int32Constant(1);
Node* arg1 = m.Int32Constant(2);
Node* arg2 = m.Int32Constant(3);
Node* arg3 = m.Int32Constant(4);
Node* arg4 = m.Int32Constant(5);
Node* call_ic = m.CallIC(descriptor, target, arg1, arg2, arg3, arg4);
EXPECT_THAT(call_ic,
m.IsCall(_, target, arg1, arg2, arg3, arg4,
IsParameter(Linkage::kInterpreterContextParameter)));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, LoadTypeFeedbackVector) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Node* feedback_vector = m.LoadTypeFeedbackVector();
Matcher<Node*> load_function_matcher = m.IsLoad(
kMachAnyTagged, IsParameter(Linkage::kInterpreterRegisterFileParameter),
IsIntPtrConstant(
InterpreterFrameConstants::kFunctionFromRegisterPointer));
Matcher<Node*> load_shared_function_info_matcher =
m.IsLoad(kMachAnyTagged, load_function_matcher,
IsIntPtrConstant(JSFunction::kSharedFunctionInfoOffset -
kHeapObjectTag));
EXPECT_THAT(
feedback_vector,
m.IsLoad(kMachAnyTagged, load_shared_function_info_matcher,
IsIntPtrConstant(SharedFunctionInfo::kFeedbackVectorOffset -
kHeapObjectTag)));
}
}

View File

@ -38,6 +38,11 @@ class InterpreterAssemblerTest : public TestWithIsolateAndZone {
const Matcher<Node*>& base_matcher,
const Matcher<Node*>& index_matcher,
const Matcher<Node*>& value_matcher);
template <class... A>
Matcher<Node*> IsCall(
const Matcher<const CallDescriptor*>& descriptor_matcher,
A... args);
Matcher<Node*> IsBytecodeOperand(int operand);
Matcher<Node*> IsBytecodeOperandSignExtended(int operand);

View File

@ -39,6 +39,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
Register reg(0);
builder.LoadAccumulatorWithRegister(reg).StoreAccumulatorInRegister(reg);
// Emit load property operations.
builder.LoadNamedProperty(reg, 0, LanguageMode::SLOPPY);
builder.LoadKeyedProperty(reg, 0, LanguageMode::STRICT);
// Emit binary operators invocations.
builder.BinaryOperation(Token::Value::ADD, reg)
.BinaryOperation(Token::Value::SUB, reg)