From e7fb233946b990ecbbbd76cc6529f62bd5da64e3 Mon Sep 17 00:00:00 2001 From: rmcilroy Date: Mon, 14 Sep 2015 03:05:18 -0700 Subject: [PATCH] [Interpreter] Add support for JS calls. Adds support for JS calls to the interpreter. In order to support calls from the interpreter, the PushArgsAndCall builtin is added which pushes a sequence of arguments onto the stack and calls builtin::Call. Adds the Call bytecode. MIPS port contributed by akos.palfi@imgtec.com in https://codereview.chromium.org/1334873002/ BUG=v8:4280 LOG=N Review URL: https://codereview.chromium.org/1323463005 Cr-Commit-Position: refs/heads/master@{#30710} --- src/arm/builtins-arm.cc | 29 +++++ src/arm/interface-descriptors-arm.cc | 12 ++ src/arm64/assembler-arm64.cc | 4 +- src/arm64/builtins-arm64.cc | 32 +++++ src/arm64/interface-descriptors-arm64.cc | 12 ++ src/builtins.h | 4 + src/code-factory.cc | 7 + src/code-factory.h | 2 + src/compiler/bytecode-graph-builder.cc | 6 + src/compiler/interpreter-assembler.cc | 62 +++++++-- src/compiler/interpreter-assembler.h | 18 ++- src/ia32/builtins-ia32.cc | 35 +++++ src/ia32/interface-descriptors-ia32.cc | 12 ++ src/interface-descriptors.h | 7 + src/interpreter/bytecode-array-builder.cc | 14 ++ src/interpreter/bytecode-array-builder.h | 7 + src/interpreter/bytecode-generator.cc | 66 ++++++++-- src/interpreter/bytecode-generator.h | 1 + src/interpreter/bytecodes.cc | 3 + src/interpreter/bytecodes.h | 4 + src/interpreter/interpreter.cc | 16 +++ src/mips/builtins-mips.cc | 29 +++++ src/mips/interface-descriptors-mips.cc | 12 ++ src/mips64/builtins-mips64.cc | 29 +++++ src/mips64/interface-descriptors-mips64.cc | 12 ++ src/x64/builtins-x64.cc | 35 +++++ src/x64/interface-descriptors-x64.cc | 12 ++ test/cctest/interpreter/test-bytecode-generator.cc | 89 ++++++++++++- test/cctest/interpreter/test-interpreter.cc | 145 ++++++++++++++++++++- .../compiler/interpreter-assembler-unittest.cc | 66 ++++++++++ .../interpreter/bytecode-array-builder-unittest.cc | 3 + 31 files changed, 758 insertions(+), 27 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 39f4371..7581172 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -1610,6 +1610,35 @@ void Builtins::Generate_Call(MacroAssembler* masm) { } +// static +void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- r0 : the number of arguments (not including the receiver) + // -- r2 : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- r1 : the target to call (can be any Object). + + // Find the address of the last argument. + __ add(r3, r0, Operand(1)); // Add one for receiver. + __ mov(r3, Operand(r3, LSL, kPointerSizeLog2)); + __ sub(r3, r2, r3); + + // Push the arguments. + Label loop_header, loop_check; + __ b(al, &loop_check); + __ bind(&loop_header); + __ ldr(r4, MemOperand(r2, -kPointerSize, PostIndex)); + __ push(r4); + __ bind(&loop_check); + __ cmp(r2, r3); + __ b(gt, &loop_header); + + // Call the target. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : actual number of arguments diff --git a/src/arm/interface-descriptors-arm.cc b/src/arm/interface-descriptors-arm.cc index 1054a25..09fdd5b 100644 --- a/src/arm/interface-descriptors-arm.cc +++ b/src/arm/interface-descriptors-arm.cc @@ -406,6 +406,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor:: }; data->InitializePlatformSpecific(arraysize(registers), registers); } + + +void PushArgsAndCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + r0, // argument count (including receiver) + r2, // address of first argument + r1 // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + } // namespace internal } // namespace v8 diff --git a/src/arm64/assembler-arm64.cc b/src/arm64/assembler-arm64.cc index 37a2f5a..9d9dd1b 100644 --- a/src/arm64/assembler-arm64.cc +++ b/src/arm64/assembler-arm64.cc @@ -2246,7 +2246,7 @@ void Assembler::debug(const char* message, uint32_t code, Instr params) { #ifdef USE_SIMULATOR // Don't generate simulator specific code if we are building a snapshot, which // might be run on real hardware. - if (!serializer_enabled()) { + // if (!serializer_enabled()) { // The arguments to the debug marker need to be contiguous in memory, so // make sure we don't try to emit pools. BlockPoolsScope scope(this); @@ -2266,7 +2266,7 @@ void Assembler::debug(const char* message, uint32_t code, Instr params) { hlt(kImmExceptionIsUnreachable); return; - } + // } // Fall through if Serializer is enabled. #endif diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 38106c4..4f22a91 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -1655,6 +1655,38 @@ void Builtins::Generate_Call(MacroAssembler* masm) { } +// static +void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- x0 : the number of arguments (not including the receiver) + // -- x2 : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- x1 : the target to call (can be any Object). + + // Find the address of the last argument. + __ add(x3, x0, Operand(1)); // Add one for receiver. + __ lsl(x3, x3, kPointerSizeLog2); + __ sub(x4, x2, x3); + + // Push the arguments. + Label loop_header, loop_check; + __ Mov(x5, jssp); + __ Claim(x3, 1); + __ B(&loop_check); + __ Bind(&loop_header); + // TODO(rmcilroy): Push two at a time once we ensure we keep stack aligned. + __ Ldr(x3, MemOperand(x2, -kPointerSize, PostIndex)); + __ Str(x3, MemOperand(x5, -kPointerSize, PreIndex)); + __ Bind(&loop_check); + __ Cmp(x2, x4); + __ B(gt, &loop_header); + + // Call the target. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { ASM_LOCATION("Builtins::Generate_ArgumentsAdaptorTrampoline"); // ----------- S t a t e ------------- diff --git a/src/arm64/interface-descriptors-arm64.cc b/src/arm64/interface-descriptors-arm64.cc index d013362..76bcca3 100644 --- a/src/arm64/interface-descriptors-arm64.cc +++ b/src/arm64/interface-descriptors-arm64.cc @@ -435,6 +435,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor:: }; data->InitializePlatformSpecific(arraysize(registers), registers); } + + +void PushArgsAndCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + x0, // argument count (including receiver) + x2, // address of first argument + x1 // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + } // namespace internal } // namespace v8 diff --git a/src/builtins.h b/src/builtins.h index 58b69d2..8a55eaa 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -74,6 +74,8 @@ enum BuiltinExtraArguments { V(CallFunction, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(Call, BUILTIN, UNINITIALIZED, kNoExtraICState) \ \ + V(PushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \ + \ V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \ V(JSConstructStubForDerived, BUILTIN, UNINITIALIZED, kNoExtraICState) \ @@ -270,6 +272,8 @@ class Builtins { // ES6 section 7.3.12 Call(F, V, [argumentsList]) static void Generate_Call(MacroAssembler* masm); + static void Generate_PushArgsAndCall(MacroAssembler* masm); + static void Generate_FunctionCall(MacroAssembler* masm); static void Generate_FunctionApply(MacroAssembler* masm); static void Generate_ReflectApply(MacroAssembler* masm); diff --git a/src/code-factory.cc b/src/code-factory.cc index c3da105..63dba9d 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -242,5 +242,12 @@ Callable CodeFactory::CallFunction(Isolate* isolate, int argc, return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor()); } + +// static +Callable CodeFactory::PushArgsAndCall(Isolate* isolate) { + return Callable(isolate->builtins()->PushArgsAndCall(), + PushArgsAndCallDescriptor(isolate)); +} + } // namespace internal } // namespace v8 diff --git a/src/code-factory.h b/src/code-factory.h index 22f31a8..89507d1 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -91,6 +91,8 @@ class CodeFactory final { static Callable CallFunction(Isolate* isolate, int argc, CallFunctionFlags flags); + + static Callable PushArgsAndCall(Isolate* isolate); }; } // namespace internal diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc index 29530f9..3dbc0b2 100644 --- a/src/compiler/bytecode-graph-builder.cc +++ b/src/compiler/bytecode-graph-builder.cc @@ -273,6 +273,12 @@ void BytecodeGraphBuilder::VisitKeyedStoreIC( } +void BytecodeGraphBuilder::VisitCall( + const interpreter::BytecodeArrayIterator& iterator) { + UNIMPLEMENTED(); +} + + void BytecodeGraphBuilder::BuildBinaryOp( const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) { Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0)); diff --git a/src/compiler/interpreter-assembler.cc b/src/compiler/interpreter-assembler.cc index 052bcf7..11f75ed 100644 --- a/src/compiler/interpreter-assembler.cc +++ b/src/compiler/interpreter-assembler.cc @@ -6,6 +6,7 @@ #include +#include "src/code-factory.h" #include "src/compiler/graph.h" #include "src/compiler/instruction-selector.h" #include "src/compiler/linkage.h" @@ -102,7 +103,12 @@ Node* InterpreterAssembler::DispatchTableRawPointer() { Node* InterpreterAssembler::RegisterFrameOffset(Node* index) { - return raw_assembler_->WordShl(index, Int32Constant(kPointerSizeLog2)); + return WordShl(index, kPointerSizeLog2); +} + + +Node* InterpreterAssembler::RegisterLocation(Node* reg_index) { + return IntPtrAdd(RegisterFileRawPointer(), RegisterFrameOffset(reg_index)); } @@ -122,8 +128,7 @@ Node* InterpreterAssembler::BytecodeOperand(int operand_index) { DCHECK_LT(operand_index, interpreter::Bytecodes::NumberOfOperands(bytecode_)); return raw_assembler_->Load( kMachUint8, BytecodeArrayTaggedPointer(), - raw_assembler_->IntPtrAdd(BytecodeOffset(), - Int32Constant(1 + operand_index))); + IntPtrAdd(BytecodeOffset(), Int32Constant(1 + operand_index))); } @@ -131,8 +136,7 @@ Node* InterpreterAssembler::BytecodeOperandSignExtended(int operand_index) { DCHECK_LT(operand_index, interpreter::Bytecodes::NumberOfOperands(bytecode_)); Node* load = raw_assembler_->Load( kMachInt8, BytecodeArrayTaggedPointer(), - raw_assembler_->IntPtrAdd(BytecodeOffset(), - Int32Constant(1 + operand_index))); + IntPtrAdd(BytecodeOffset(), Int32Constant(1 + operand_index))); // Ensure that we sign extend to full pointer size if (kPointerSize == 8) { load = raw_assembler_->ChangeInt32ToInt64(load); @@ -141,6 +145,13 @@ Node* InterpreterAssembler::BytecodeOperandSignExtended(int operand_index) { } +Node* InterpreterAssembler::BytecodeOperandCount(int operand_index) { + DCHECK_EQ(interpreter::OperandType::kCount, + interpreter::Bytecodes::GetOperandType(bytecode_, operand_index)); + return BytecodeOperand(operand_index); +} + + Node* InterpreterAssembler::BytecodeOperandImm8(int operand_index) { DCHECK_EQ(interpreter::OperandType::kImm8, interpreter::Bytecodes::GetOperandType(bytecode_, operand_index)); @@ -197,12 +208,27 @@ Node* InterpreterAssembler::SmiUntag(Node* value) { } +Node* InterpreterAssembler::IntPtrAdd(Node* a, Node* b) { + return raw_assembler_->IntPtrAdd(a, b); +} + + +Node* InterpreterAssembler::IntPtrSub(Node* a, Node* b) { + return raw_assembler_->IntPtrSub(a, b); +} + + +Node* InterpreterAssembler::WordShl(Node* value, int shift) { + return raw_assembler_->WordShl(value, Int32Constant(shift)); +} + + Node* InterpreterAssembler::LoadConstantPoolEntry(Node* index) { Node* constant_pool = LoadObjectField(BytecodeArrayTaggedPointer(), BytecodeArray::kConstantPoolOffset); - Node* entry_offset = raw_assembler_->IntPtrAdd( - IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag), - raw_assembler_->WordShl(index, Int32Constant(kPointerSizeLog2))); + Node* entry_offset = + IntPtrAdd(IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag), + WordShl(index, kPointerSizeLog2)); return raw_assembler_->Load(kMachAnyTagged, constant_pool, entry_offset); } @@ -236,6 +262,24 @@ Node* InterpreterAssembler::LoadTypeFeedbackVector() { } +Node* InterpreterAssembler::CallJS(Node* function, Node* first_arg, + Node* arg_count) { + Callable builtin = CodeFactory::PushArgsAndCall(isolate()); + CallDescriptor* descriptor = Linkage::GetStubCallDescriptor( + isolate(), zone(), builtin.descriptor(), 0, CallDescriptor::kNoFlags); + + Node* code_target = HeapConstant(builtin.code()); + + Node** args = zone()->NewArray(4); + args[0] = arg_count; + args[1] = first_arg; + args[2] = function; + args[3] = ContextTaggedPointer(); + + return raw_assembler_->CallN(descriptor, code_target, args); +} + + Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor, Node* target, Node** args) { CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor( @@ -302,7 +346,7 @@ void InterpreterAssembler::Return() { Node* InterpreterAssembler::Advance(int delta) { - return raw_assembler_->IntPtrAdd(BytecodeOffset(), Int32Constant(delta)); + return IntPtrAdd(BytecodeOffset(), Int32Constant(delta)); } diff --git a/src/compiler/interpreter-assembler.h b/src/compiler/interpreter-assembler.h index 0949fbb..adb7ed8 100644 --- a/src/compiler/interpreter-assembler.h +++ b/src/compiler/interpreter-assembler.h @@ -38,7 +38,10 @@ class InterpreterAssembler { Handle GenerateCode(); - // Returns the Idx immediate for bytecode operand |operand_index| in the + // Returns the count immediate for bytecode operand |operand_index| in the + // current bytecode. + Node* BytecodeOperandCount(int operand_index); + // Returns the index immediate for bytecode operand |operand_index| in the // current bytecode. Node* BytecodeOperandIdx(int operand_index); // Returns the Imm8 immediate for bytecode operand |operand_index| in the @@ -56,6 +59,10 @@ class InterpreterAssembler { Node* LoadRegister(Node* reg_index); Node* StoreRegister(Node* value, Node* reg_index); + // Returns the location in memory of the register |reg_index| in the + // interpreter register file. + Node* RegisterLocation(Node* reg_index); + // Constants. Node* Int32Constant(int value); Node* IntPtrConstant(intptr_t value); @@ -66,6 +73,11 @@ class InterpreterAssembler { Node* SmiTag(Node* value); Node* SmiUntag(Node* value); + // Basic arithmetic operations. + Node* IntPtrAdd(Node* a, Node* b); + Node* IntPtrSub(Node* a, Node* b); + Node* WordShl(Node* value, int shift); + // Load constant at |index| in the constant pool. Node* LoadConstantPoolEntry(Node* index); @@ -81,6 +93,10 @@ class InterpreterAssembler { // Load the TypeFeedbackVector for the current function. Node* LoadTypeFeedbackVector(); + // Call JSFunction or Callable |function| with |arg_count| (not including + // receiver) and the first argument located at |first_arg|. + Node* CallJS(Node* function, Node* first_arg, Node* arg_count); + // Call an IC code stub. Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node* arg1, Node* arg2, Node* arg3, Node* arg4); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index f7215d2..5771d97 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -1534,6 +1534,41 @@ void Builtins::Generate_Call(MacroAssembler* masm) { } +// static +void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : the number of arguments (not including the receiver) + // -- ebx : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- edi : the target to call (can be any Object). + + // Pop return address to allow tail-call after pushing arguments. + __ Pop(edx); + + // Find the address of the last argument. + __ mov(ecx, eax); + __ add(ecx, Immediate(1)); // Add one for receiver. + __ shl(ecx, kPointerSizeLog2); + __ neg(ecx); + __ add(ecx, ebx); + + // Push the arguments. + Label loop_header, loop_check; + __ jmp(&loop_check); + __ bind(&loop_header); + __ Push(Operand(ebx, 0)); + __ sub(ebx, Immediate(kPointerSize)); + __ bind(&loop_check); + __ cmp(ebx, ecx); + __ j(greater, &loop_header, Label::kNear); + + // Call the target. + __ Push(edx); // Re-push return address. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : actual number of arguments diff --git a/src/ia32/interface-descriptors-ia32.cc b/src/ia32/interface-descriptors-ia32.cc index 821e57d..0fea67b 100644 --- a/src/ia32/interface-descriptors-ia32.cc +++ b/src/ia32/interface-descriptors-ia32.cc @@ -389,6 +389,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor:: }; data->InitializePlatformSpecific(arraysize(registers), registers); } + + +void PushArgsAndCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + eax, // argument count (including receiver) + ebx, // address of first argument + edi // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + } // namespace internal } // namespace v8 diff --git a/src/interface-descriptors.h b/src/interface-descriptors.h index 495a621..06e586e 100644 --- a/src/interface-descriptors.h +++ b/src/interface-descriptors.h @@ -38,6 +38,7 @@ class PlatformInterfaceDescriptor; V(CallFunctionWithFeedbackAndVector) \ V(CallConstruct) \ V(CallTrampoline) \ + V(PushArgsAndCall) \ V(RegExpConstructResult) \ V(TransitionElementsKind) \ V(AllocateHeapNumber) \ @@ -681,6 +682,12 @@ class GrowArrayElementsDescriptor : public CallInterfaceDescriptor { static const Register KeyRegister(); }; + +class PushArgsAndCallDescriptor : public CallInterfaceDescriptor { + public: + DECLARE_DESCRIPTOR(PushArgsAndCallDescriptor, CallInterfaceDescriptor) +}; + #undef DECLARE_DESCRIPTOR diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc index 613df53..f3a1b1e 100644 --- a/src/interpreter/bytecode-array-builder.cc +++ b/src/interpreter/bytecode-array-builder.cc @@ -224,6 +224,19 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { } +BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable, + Register receiver, + size_t arg_count) { + if (FitsInByteOperand(arg_count)) { + Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(), + static_cast(arg_count)); + } else { + UNIMPLEMENTED(); + } + return *this; +} + + size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle object) { // These constants shouldn't be added to the constant pool, the should use // specialzed bytecodes instead. @@ -267,6 +280,7 @@ bool BytecodeArrayBuilder::OperandIsValid(Bytecode bytecode, int operand_index, switch (operand_type) { case OperandType::kNone: return false; + case OperandType::kCount: case OperandType::kImm8: case OperandType::kIdx: return true; diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h index ebcb180..f7a8ddd 100644 --- a/src/interpreter/bytecode-array-builder.h +++ b/src/interpreter/bytecode-array-builder.h @@ -67,6 +67,13 @@ class BytecodeArrayBuilder { int feedback_slot, LanguageMode language_mode); + // Call a JS function. The JSFunction or Callable to be called should be in + // |callable|, the receiver should be in |receiver| and all subsequent + // arguments should be in registers to + // . + BytecodeArrayBuilder& Call(Register callable, Register receiver, + size_t arg_count); + // Operators. BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg); diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc index 520dbd4..2a330ca8 100644 --- a/src/interpreter/bytecode-generator.cc +++ b/src/interpreter/bytecode-generator.cc @@ -342,27 +342,18 @@ void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); } void BytecodeGenerator::VisitThrow(Throw* expr) { UNIMPLEMENTED(); } -void BytecodeGenerator::VisitProperty(Property* expr) { +void BytecodeGenerator::VisitPropertyLoad(Register obj, 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; @@ -374,7 +365,60 @@ void BytecodeGenerator::VisitProperty(Property* expr) { } -void BytecodeGenerator::VisitCall(Call* expr) { UNIMPLEMENTED(); } +void BytecodeGenerator::VisitProperty(Property* expr) { + TemporaryRegisterScope temporary_register_scope(&builder_); + Register obj = temporary_register_scope.NewRegister(); + Visit(expr->obj()); + builder().StoreAccumulatorInRegister(obj); + VisitPropertyLoad(obj, expr); +} + + +void BytecodeGenerator::VisitCall(Call* expr) { + Expression* callee_expr = expr->expression(); + Call::CallType call_type = expr->GetCallType(isolate()); + + // Prepare the callee and the receiver to the function call. This depends on + // the semantics of the underlying call type. + TemporaryRegisterScope temporary_register_scope(&builder_); + Register callee = temporary_register_scope.NewRegister(); + Register receiver = temporary_register_scope.NewRegister(); + + switch (call_type) { + case Call::PROPERTY_CALL: { + Property* property = callee_expr->AsProperty(); + if (property->IsSuperAccess()) { + UNIMPLEMENTED(); + } + Visit(property->obj()); + builder().StoreAccumulatorInRegister(receiver); + // Perform a property load of the callee. + VisitPropertyLoad(receiver, property); + builder().StoreAccumulatorInRegister(callee); + break; + } + case Call::GLOBAL_CALL: + case Call::LOOKUP_SLOT_CALL: + case Call::SUPER_CALL: + case Call::POSSIBLY_EVAL_CALL: + case Call::OTHER_CALL: + UNIMPLEMENTED(); + } + + // Evaluate all arguments to the function call and store in sequential + // registers. + ZoneList* args = expr->arguments(); + for (int i = 0; i < args->length(); ++i) { + Visit(args->at(i)); + Register arg = temporary_register_scope.NewRegister(); + DCHECK(arg.index() - i == receiver.index() + 1); + builder().StoreAccumulatorInRegister(arg); + } + + // TODO(rmcilroy): Deal with possible direct eval here? + // TODO(rmcilroy): Use CallIC to allow call type feedback. + builder().Call(callee, receiver, args->length()); +} void BytecodeGenerator::VisitCallNew(CallNew* expr) { UNIMPLEMENTED(); } diff --git a/src/interpreter/bytecode-generator.h b/src/interpreter/bytecode-generator.h index 080f644..a192bc6 100644 --- a/src/interpreter/bytecode-generator.h +++ b/src/interpreter/bytecode-generator.h @@ -28,6 +28,7 @@ class BytecodeGenerator : public AstVisitor { DEFINE_AST_VISITOR_SUBCLASS_MEMBERS(); void VisitArithmeticExpression(BinaryOperation* binop); + void VisitPropertyLoad(Register obj, Property* expr); inline BytecodeArrayBuilder& builder() { return builder_; } inline Scope* scope() const { return scope_; } diff --git a/src/interpreter/bytecodes.cc b/src/interpreter/bytecodes.cc index 3fae0e9..42a3578 100644 --- a/src/interpreter/bytecodes.cc +++ b/src/interpreter/bytecodes.cc @@ -126,6 +126,9 @@ std::ostream& Bytecodes::Decode(std::ostream& os, const uint8_t* bytecode_start, OperandType op_type = GetOperandType(bytecode, i); uint8_t operand = operands_start[i]; switch (op_type) { + case interpreter::OperandType::kCount: + os << "#" << static_cast(operand); + break; case interpreter::OperandType::kIdx: os << "[" << static_cast(operand) << "]"; break; diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index 9ab9c6f..f475651 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -18,6 +18,7 @@ namespace interpreter { // The list of operand types used by bytecodes. #define OPERAND_TYPE_LIST(V) \ V(None) \ + V(Count) \ V(Imm8) \ V(Idx) \ V(Reg) @@ -54,6 +55,9 @@ namespace interpreter { V(Div, OperandType::kReg) \ V(Mod, OperandType::kReg) \ \ + /* Call operations. */ \ + V(Call, OperandType::kReg, OperandType::kReg, OperandType::kCount) \ + \ /* Control Flow */ \ V(Return, OperandType::kNone) diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 2029512..61cd7a4 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -322,6 +322,22 @@ void Interpreter::DoMod(compiler::InterpreterAssembler* assembler) { } +// Call +// +// Call a JS function with receiver and |arg_count| arguments in subsequent +// registers. The JSfunction or Callable to call is in the accumulator. +void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) { + Node* function_reg = __ BytecodeOperandReg(0); + Node* function = __ LoadRegister(function_reg); + Node* receiver_reg = __ BytecodeOperandReg(1); + Node* first_arg = __ RegisterLocation(receiver_reg); + Node* args_count = __ BytecodeOperandCount(2); + Node* result = __ CallJS(function, first_arg, args_count); + __ SetAccumulator(result); + __ Dispatch(); +} + + // Return // // Return the value in register 0. diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index b908668..c5ca8b6 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -1619,6 +1619,35 @@ void Builtins::Generate_Call(MacroAssembler* masm) { } +// static +void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : the number of arguments (not including the receiver) + // -- a2 : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- a1 : the target to call (can be any Object). + + // Find the address of the last argument. + __ Addu(a3, a0, Operand(1)); // Add one for receiver. + __ sll(a3, a3, kPointerSizeLog2); + __ Subu(a3, a2, Operand(a3)); + + // Push the arguments. + Label loop_header, loop_check; + __ Branch(&loop_check); + __ bind(&loop_header); + __ lw(t0, MemOperand(a2)); + __ Addu(a2, a2, Operand(-kPointerSize)); + __ push(t0); + __ bind(&loop_check); + __ Branch(&loop_header, gt, a2, Operand(a3)); + + // Call the target. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // State setup as expected by MacroAssembler::InvokePrologue. // ----------- S t a t e ------------- diff --git a/src/mips/interface-descriptors-mips.cc b/src/mips/interface-descriptors-mips.cc index b02cdad..3e56629 100644 --- a/src/mips/interface-descriptors-mips.cc +++ b/src/mips/interface-descriptors-mips.cc @@ -381,6 +381,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor:: }; data->InitializePlatformSpecific(arraysize(registers), registers); } + + +void PushArgsAndCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a0, // argument count (including receiver) + a2, // address of first argument + a1 // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + } // namespace internal } // namespace v8 diff --git a/src/mips64/builtins-mips64.cc b/src/mips64/builtins-mips64.cc index bc43967..df499c1 100644 --- a/src/mips64/builtins-mips64.cc +++ b/src/mips64/builtins-mips64.cc @@ -1616,6 +1616,35 @@ void Builtins::Generate_Call(MacroAssembler* masm) { } +// static +void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- a0 : the number of arguments (not including the receiver) + // -- a2 : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- a1 : the target to call (can be any Object). + + // Find the address of the last argument. + __ Daddu(a3, a0, Operand(1)); // Add one for receiver. + __ dsll(a3, a3, kPointerSizeLog2); + __ Dsubu(a3, a2, Operand(a3)); + + // Push the arguments. + Label loop_header, loop_check; + __ Branch(&loop_check); + __ bind(&loop_header); + __ ld(a4, MemOperand(a2)); + __ Daddu(a2, a2, Operand(-kPointerSize)); + __ push(a4); + __ bind(&loop_check); + __ Branch(&loop_header, gt, a2, Operand(a3)); + + // Call the target. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { // State setup as expected by MacroAssembler::InvokePrologue. // ----------- S t a t e ------------- diff --git a/src/mips64/interface-descriptors-mips64.cc b/src/mips64/interface-descriptors-mips64.cc index f861513..42513e0 100644 --- a/src/mips64/interface-descriptors-mips64.cc +++ b/src/mips64/interface-descriptors-mips64.cc @@ -381,6 +381,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor:: }; data->InitializePlatformSpecific(arraysize(registers), registers); } + + +void PushArgsAndCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + a0, // argument count (including receiver) + a2, // address of first argument + a1 // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + } // namespace internal } // namespace v8 diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 832d064..0ddedc2 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -1722,6 +1722,41 @@ void Builtins::Generate_Call(MacroAssembler* masm) { } +// static +void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rax : the number of arguments (not including the receiver) + // -- rbx : the address of the first argument to be pushed. Subsequent + // arguments should be consecutive above this, in the same order as + // they are to be pushed onto the stack. + // -- rdi : the target to call (can be any Object). + + // Pop return address to allow tail-call after pushing arguments. + __ Pop(rdx); + + // Find the address of the last argument. + __ movp(rcx, rax); + __ addp(rcx, Immediate(1)); // Add one for receiver. + __ shlp(rcx, Immediate(kPointerSizeLog2)); + __ negp(rcx); + __ addp(rcx, rbx); + + // Push the arguments. + Label loop_header, loop_check; + __ j(always, &loop_check); + __ bind(&loop_header); + __ Push(Operand(rbx, 0)); + __ subp(rbx, Immediate(kPointerSize)); + __ bind(&loop_check); + __ cmpp(rbx, rcx); + __ j(greater, &loop_header, Label::kNear); + + // Call the target. + __ Push(rdx); // Re-push return address. + __ Jump(masm->isolate()->builtins()->Call(), RelocInfo::CODE_TARGET); +} + + void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { // Lookup the function in the JavaScript frame. __ movp(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); diff --git a/src/x64/interface-descriptors-x64.cc b/src/x64/interface-descriptors-x64.cc index d9f6d05..146d9a8 100644 --- a/src/x64/interface-descriptors-x64.cc +++ b/src/x64/interface-descriptors-x64.cc @@ -381,6 +381,18 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor:: }; data->InitializePlatformSpecific(arraysize(registers), registers); } + + +void PushArgsAndCallDescriptor::InitializePlatformSpecific( + CallInterfaceDescriptorData* data) { + Register registers[] = { + rax, // argument count (including receiver) + rbx, // address of first argument + rdi // the target callable to be call + }; + data->InitializePlatformSpecific(arraysize(registers), registers); +} + } // namespace internal } // namespace v8 diff --git a/test/cctest/interpreter/test-bytecode-generator.cc b/test/cctest/interpreter/test-bytecode-generator.cc index 42125f0..bff48bc 100644 --- a/test/cctest/interpreter/test-bytecode-generator.cc +++ b/test/cctest/interpreter/test-bytecode-generator.cc @@ -64,7 +64,7 @@ struct ExpectedSnippet { int frame_size; int parameter_count; int bytecode_length; - const uint8_t bytecode[24]; + const uint8_t bytecode[32]; int constant_count; T constants[16]; }; @@ -338,7 +338,7 @@ TEST(PropertyLoads) { BytecodeGeneratorHelper helper; Code::Kind ic_kinds[] = { i::Code::LOAD_IC, i::Code::LOAD_IC }; - FeedbackVectorSpec feedback_spec(0, 1, ic_kinds); + FeedbackVectorSpec feedback_spec(0, 2, ic_kinds); Handle vector = helper.factory()->NewTypeFeedbackVector(&feedback_spec); @@ -427,7 +427,7 @@ TEST(PropertyStores) { BytecodeGeneratorHelper helper; Code::Kind ic_kinds[] = { i::Code::STORE_IC, i::Code::STORE_IC }; - FeedbackVectorSpec feedback_spec(0, 1, ic_kinds); + FeedbackVectorSpec feedback_spec(0, 2, ic_kinds); Handle vector = helper.factory()->NewTypeFeedbackVector(&feedback_spec); @@ -525,6 +525,89 @@ TEST(PropertyStores) { } } + +#define FUNC_ARG "new (function Obj() { this.func = function() { return; }})()" + + +TEST(PropertyCall) { + InitializedHandleScope handle_scope; + BytecodeGeneratorHelper helper; + + Code::Kind ic_kinds[] = { i::Code::LOAD_IC, i::Code::LOAD_IC }; + FeedbackVectorSpec feedback_spec(0, 2, ic_kinds); + Handle vector = + helper.factory()->NewTypeFeedbackVector(&feedback_spec); + + ExpectedSnippet snippets[] = { + {"function f(a) { return a.func(); }\nf(" FUNC_ARG ")", + 2 * kPointerSize, 2, 16, + { + B(Ldar), R(helper.kLastParamIndex), + B(Star), R(1), + B(LdaConstant), U8(0), + B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), + B(Star), R(0), + B(Call), R(0), R(1), U8(0), + B(Return) + }, + 1, { "func" } + }, + {"function f(a, b, c) { return a.func(b, c); }\nf(" FUNC_ARG ", 1, 2)", + 4 * kPointerSize, 4, 24, + { + B(Ldar), R(helper.kLastParamIndex - 2), + B(Star), R(1), + B(LdaConstant), U8(0), + B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), + B(Star), R(0), + B(Ldar), R(helper.kLastParamIndex - 1), + B(Star), R(2), + B(Ldar), R(helper.kLastParamIndex), + B(Star), R(3), + B(Call), R(0), R(1), U8(2), + B(Return) + }, + 1, { "func" } + }, + {"function f(a, b) { return a.func(b + b, b); }\nf(" FUNC_ARG ", 1)", + 4 * kPointerSize, 3, 30, + { + B(Ldar), R(helper.kLastParamIndex - 1), + B(Star), R(1), + B(LdaConstant), U8(0), + B(LoadIC), R(1), U8(vector->first_ic_slot_index() + 2), + B(Star), R(0), + B(Ldar), R(helper.kLastParamIndex), + B(Star), R(2), + B(Ldar), R(helper.kLastParamIndex), + B(Add), R(2), + B(Star), R(2), + B(Ldar), R(helper.kLastParamIndex), + B(Star), R(3), + B(Call), R(0), R(1), U8(2), + B(Return) + }, + 1, { "func" } + } + }; + size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]); + for (size_t i = 0; i < num_snippets; i++) { + Handle 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 expected = + helper.factory()->NewStringFromAsciiChecked(snippets[i].constants[j]); + CHECK(String::cast(ba->constant_pool()->get(j))->Equals(*expected)); + } + } +} + } // namespace interpreter } // namespace internal } // namespance v8 diff --git a/test/cctest/interpreter/test-interpreter.cc b/test/cctest/interpreter/test-interpreter.cc index 7099747..ca4ddb4 100644 --- a/test/cctest/interpreter/test-interpreter.cc +++ b/test/cctest/interpreter/test-interpreter.cc @@ -476,7 +476,7 @@ TEST(InterpreterLoadNamedProperty) { i::Isolate* isolate = handles.main_isolate(); i::Factory* factory = isolate->factory(); - i::Code::Kind ic_kinds[] = { i::Code::LOAD_IC }; + i::Code::Kind ic_kinds[] = {i::Code::LOAD_IC}; i::FeedbackVectorSpec feedback_spec(0, 1, ic_kinds); Handle vector = factory->NewTypeFeedbackVector(&feedback_spec); @@ -662,3 +662,146 @@ TEST(InterpreterStoreKeyedProperty) { CHECK(Runtime::GetObjectProperty(isolate, object2, name).ToHandle(&result)); CHECK_EQ(Smi::cast(*result), Smi::FromInt(999)); } + + +TEST(InterpreterCall) { + 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 vector = + factory->NewTypeFeedbackVector(&feedback_spec); + + Handle name = factory->NewStringFromAsciiChecked("func"); + name = factory->string_table()->LookupString(isolate, name); + + // Check with no args. + { + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(1); + builder.set_parameter_count(1); + builder.LoadLiteral(name) + .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), + i::SLOPPY) + .StoreAccumulatorInRegister(Register(0)) + .Call(Register(0), builder.Parameter(0), 0) + .Return(); + Handle bytecode_array = builder.ToBytecodeArray(); + + InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); + auto callable = tester.GetCallable>(); + + Handle object = tester.NewObject( + "new (function Obj() { this.func = function() { return 0x265; }})()"); + Handle return_val = callable(object).ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(0x265)); + } + + // Check that receiver is passed properly. + { + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(1); + builder.set_parameter_count(1); + builder.LoadLiteral(name) + .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), + i::SLOPPY) + .StoreAccumulatorInRegister(Register(0)) + .Call(Register(0), builder.Parameter(0), 0) + .Return(); + Handle bytecode_array = builder.ToBytecodeArray(); + + InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); + auto callable = tester.GetCallable>(); + + Handle object = tester.NewObject( + "new (function Obj() {" + " this.val = 1234;" + " this.func = function() { return this.val; };" + "})()"); + Handle return_val = callable(object).ToHandleChecked(); + CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(1234)); + } + + // Check with two parameters (+ receiver). + { + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(4); + builder.set_parameter_count(1); + builder.LoadLiteral(name) + .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), + i::SLOPPY) + .StoreAccumulatorInRegister(Register(0)) + .LoadAccumulatorWithRegister(builder.Parameter(0)) + .StoreAccumulatorInRegister(Register(1)) + .LoadLiteral(Smi::FromInt(51)) + .StoreAccumulatorInRegister(Register(2)) + .LoadLiteral(Smi::FromInt(11)) + .StoreAccumulatorInRegister(Register(3)) + .Call(Register(0), Register(1), 2) + .Return(); + Handle bytecode_array = builder.ToBytecodeArray(); + + InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); + auto callable = tester.GetCallable>(); + + Handle object = tester.NewObject( + "new (function Obj() { " + " this.func = function(a, b) { return a - b; }" + "})()"); + Handle return_val = callable(object).ToHandleChecked(); + CHECK(return_val->SameValue(Smi::FromInt(40))); + } + + // Check with 10 parameters (+ receiver). + { + BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone()); + builder.set_locals_count(12); + builder.set_parameter_count(1); + builder.LoadLiteral(name) + .LoadNamedProperty(builder.Parameter(0), vector->first_ic_slot_index(), + i::SLOPPY) + .StoreAccumulatorInRegister(Register(0)) + .LoadAccumulatorWithRegister(builder.Parameter(0)) + .StoreAccumulatorInRegister(Register(1)) + .LoadLiteral(factory->NewStringFromAsciiChecked("a")) + .StoreAccumulatorInRegister(Register(2)) + .LoadLiteral(factory->NewStringFromAsciiChecked("b")) + .StoreAccumulatorInRegister(Register(3)) + .LoadLiteral(factory->NewStringFromAsciiChecked("c")) + .StoreAccumulatorInRegister(Register(4)) + .LoadLiteral(factory->NewStringFromAsciiChecked("d")) + .StoreAccumulatorInRegister(Register(5)) + .LoadLiteral(factory->NewStringFromAsciiChecked("e")) + .StoreAccumulatorInRegister(Register(6)) + .LoadLiteral(factory->NewStringFromAsciiChecked("f")) + .StoreAccumulatorInRegister(Register(7)) + .LoadLiteral(factory->NewStringFromAsciiChecked("g")) + .StoreAccumulatorInRegister(Register(8)) + .LoadLiteral(factory->NewStringFromAsciiChecked("h")) + .StoreAccumulatorInRegister(Register(9)) + .LoadLiteral(factory->NewStringFromAsciiChecked("i")) + .StoreAccumulatorInRegister(Register(10)) + .LoadLiteral(factory->NewStringFromAsciiChecked("j")) + .StoreAccumulatorInRegister(Register(11)) + .Call(Register(0), Register(1), 10) + .Return(); + Handle bytecode_array = builder.ToBytecodeArray(); + + InterpreterTester tester(handles.main_isolate(), bytecode_array, vector); + auto callable = tester.GetCallable>(); + + Handle object = tester.NewObject( + "new (function Obj() { " + " this.prefix = \"prefix_\";" + " this.func = function(a, b, c, d, e, f, g, h, i, j) {" + " return this.prefix + a + b + c + d + e + f + g + h + i + j;" + " }" + "})()"); + Handle return_val = callable(object).ToHandleChecked(); + Handle expected = + factory->NewStringFromAsciiChecked("prefix_abcdefghij"); + CHECK(i::String::cast(*return_val)->Equals(*expected)); + } +} diff --git a/test/unittests/compiler/interpreter-assembler-unittest.cc b/test/unittests/compiler/interpreter-assembler-unittest.cc index 1dfaf58..048fbb6 100644 --- a/test/unittests/compiler/interpreter-assembler-unittest.cc +++ b/test/unittests/compiler/interpreter-assembler-unittest.cc @@ -4,6 +4,7 @@ #include "test/unittests/compiler/interpreter-assembler-unittest.h" +#include "src/code-factory.h" #include "src/compiler/graph.h" #include "src/compiler/node.h" #include "src/interface-descriptors.h" @@ -185,6 +186,9 @@ TARGET_TEST_F(InterpreterAssemblerTest, BytecodeOperand) { int number_of_operands = interpreter::Bytecodes::NumberOfOperands(bytecode); for (int i = 0; i < number_of_operands; i++) { switch (interpreter::Bytecodes::GetOperandType(bytecode, i)) { + case interpreter::OperandType::kCount: + EXPECT_THAT(m.BytecodeOperandCount(i), m.IsBytecodeOperand(i)); + break; case interpreter::OperandType::kIdx: EXPECT_THAT(m.BytecodeOperandIdx(i), m.IsBytecodeOperand(i)); break; @@ -235,6 +239,20 @@ TARGET_TEST_F(InterpreterAssemblerTest, GetSetAccumulator) { } +TARGET_TEST_F(InterpreterAssemblerTest, RegisterLocation) { + TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { + InterpreterAssemblerForTest m(this, bytecode); + Node* reg_index_node = m.Int32Constant(44); + Node* reg_location_node = m.RegisterLocation(reg_index_node); + EXPECT_THAT( + reg_location_node, + IsIntPtrAdd( + IsParameter(Linkage::kInterpreterRegisterFileParameter), + IsWordShl(reg_index_node, IsInt32Constant(kPointerSizeLog2)))); + } +} + + TARGET_TEST_F(InterpreterAssemblerTest, LoadRegister) { TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { InterpreterAssemblerForTest m(this, bytecode); @@ -277,6 +295,38 @@ TARGET_TEST_F(InterpreterAssemblerTest, SmiTag) { } +TARGET_TEST_F(InterpreterAssemblerTest, IntPtrAdd) { + TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { + InterpreterAssemblerForTest m(this, bytecode); + Node* a = m.Int32Constant(0); + Node* b = m.Int32Constant(1); + Node* add = m.IntPtrAdd(a, b); + EXPECT_THAT(add, IsIntPtrAdd(a, b)); + } +} + + +TARGET_TEST_F(InterpreterAssemblerTest, IntPtrSub) { + TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { + InterpreterAssemblerForTest m(this, bytecode); + Node* a = m.Int32Constant(0); + Node* b = m.Int32Constant(1); + Node* add = m.IntPtrSub(a, b); + EXPECT_THAT(add, IsIntPtrSub(a, b)); + } +} + + +TARGET_TEST_F(InterpreterAssemblerTest, WordShl) { + TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { + InterpreterAssemblerForTest m(this, bytecode); + Node* a = m.Int32Constant(0); + Node* add = m.WordShl(a, 10); + EXPECT_THAT(add, IsWordShl(a, IsInt32Constant(10))); + } +} + + TARGET_TEST_F(InterpreterAssemblerTest, LoadConstantPoolEntry) { TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { InterpreterAssemblerForTest m(this, bytecode); @@ -358,6 +408,22 @@ TARGET_TEST_F(InterpreterAssemblerTest, CallIC) { } +TARGET_TEST_F(InterpreterAssemblerTest, CallJS) { + TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { + InterpreterAssemblerForTest m(this, bytecode); + Callable builtin = CodeFactory::PushArgsAndCall(isolate()); + Node* function = m.Int32Constant(0); + Node* first_arg = m.Int32Constant(1); + Node* arg_count = m.Int32Constant(2); + Node* call_js = m.CallJS(function, first_arg, arg_count); + EXPECT_THAT( + call_js, + m.IsCall(_, IsHeapConstant(builtin.code()), arg_count, first_arg, + function, IsParameter(Linkage::kInterpreterContextParameter))); + } +} + + TARGET_TEST_F(InterpreterAssemblerTest, LoadTypeFeedbackVector) { TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) { InterpreterAssemblerForTest m(this, bytecode); diff --git a/test/unittests/interpreter/bytecode-array-builder-unittest.cc b/test/unittests/interpreter/bytecode-array-builder-unittest.cc index d1cee8c..357aa67 100644 --- a/test/unittests/interpreter/bytecode-array-builder-unittest.cc +++ b/test/unittests/interpreter/bytecode-array-builder-unittest.cc @@ -45,6 +45,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { .StoreNamedProperty(reg, reg, 0, LanguageMode::SLOPPY) .StoreKeyedProperty(reg, reg, 0, LanguageMode::SLOPPY); + // Call operations. + builder.Call(reg, reg, 0); + // Emit binary operators invocations. builder.BinaryOperation(Token::Value::ADD, reg) .BinaryOperation(Token::Value::SUB, reg) -- 2.7.4