[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:
parent
c29a4061c9
commit
d8df7468b4
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -180,6 +180,7 @@ class InterpreterFrameConstants : public AllStatic {
|
||||
// Register file pointer relative.
|
||||
static const int kLastParamFromRegisterPointer =
|
||||
StandardFrameConstants::kFixedFrameSize + kPointerSize;
|
||||
static const int kFunctionFromRegisterPointer = kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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) {}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user