#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"
}
+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);
namespace v8 {
namespace internal {
+class CallInterfaceDescriptor;
class Isolate;
class Zone;
// 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);
// Register file pointer relative.
static const int kLastParamFromRegisterPointer =
StandardFrameConstants::kFixedFrameSize + kPointerSize;
+ static const int kFunctionFromRegisterPointer = kPointerSize;
};
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();
}
+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;
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_) ||
}
+// 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) {}
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);
-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);
Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) {
+ set_info(info);
set_scope(info->scope());
// This a temporary guard (oth).
VisitStatements(info->literal()->body());
set_scope(nullptr);
+ set_info(nullptr);
return builder_.ToBytecodeArray();
}
}
-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();
}
}
-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();
}
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) {
}
-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();
}
}
-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();
}
}
-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();
}
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
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_;
};
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)
#include "src/interpreter/interpreter.h"
+#include "src/code-factory.h"
#include "src/compiler.h"
#include "src/compiler/interpreter-assembler.h"
#include "src/factory.h"
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();
shared_info->set_function_data(*bytecodes);
info->SetCode(info->isolate()->builtins()->InterpreterEntryTrampoline());
- info->EnsureFeedbackVector();
return true;
}
}
+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
namespace internal {
class Isolate;
+class Callable;
class CompilationInfo;
namespace compiler {
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_;
public:
const char* kFunctionName = "f";
+ const int kLastParamIndex =
+ -InterpreterFrameConstants::kLastParamFromRegisterPointer / kPointerSize;
+
BytecodeGeneratorHelper() {
i::FLAG_ignition = true;
i::FLAG_ignition_filter = kFunctionName;
}
+ Factory* factory() { return CcTest::i_isolate()->factory(); }
+
+
Handle<BytecodeArray> MakeBytecode(const char* script,
const char* function_name) {
CompileRun(script);
// 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];
};
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);
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);
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);
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);
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);
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);
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));
}
}
}
+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
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();
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() {
*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;
}
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();
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))
.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));
+}
#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"
}
+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) {
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)));
}
}
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);
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)