[Interpreter] Add CallRuntime support to the interpreter.

Adds support for calling runtime functions from the interpreter. Adds the
CallRuntime bytecode which takes a Runtime::FunctionId of the function to call
and the arguments in sequential registers. Adds a InterpreterCEntry builtin
to enable the interpreter to enter C++ code based on the functionId.

Also renames Builtin::PushArgsAndCall to Builtin::InterpreterPushArgsAndCall
and groups all the interpreter builtins together.

BUG=v8:4280
LOG=N

Committed: https://crrev.com/40e8424b744f8b6e3e1d93e20f23487419911dfc
Cr-Commit-Position: refs/heads/master@{#31064}

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

Cr-Commit-Position: refs/heads/master@{#31076}
This commit is contained in:
rmcilroy 2015-10-02 07:11:55 -07:00 committed by Commit bot
parent 7b7a8205d9
commit c991d8f384
48 changed files with 813 additions and 328 deletions

View File

@ -966,6 +966,35 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(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_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);
@ -1696,35 +1725,6 @@ void Builtins::Generate_Construct(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

View File

@ -974,14 +974,21 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// fp: frame pointer (restored after C call)
// sp: stack pointer (restored as callee's sp after C call)
// cp: current context (C callee-saved)
//
// If argv_in_register():
// r2: pointer to the first argument
ProfileEntryHookStub::MaybeCallEntryHook(masm);
__ mov(r5, Operand(r1));
// Compute the argv pointer in a callee-saved register.
__ add(r1, sp, Operand(r0, LSL, kPointerSizeLog2));
__ sub(r1, r1, Operand(kPointerSize));
if (!argv_in_register()) {
// Compute the argv pointer in a callee-saved register.
__ add(r1, sp, Operand(r0, LSL, kPointerSizeLog2));
__ sub(r1, r1, Operand(kPointerSize));
} else {
// Move argv into the correct register.
__ mov(r1, Operand(r2));
}
// Enter the exit frame that transitions from JavaScript to C++.
FrameScope scope(masm, StackFrame::MANUAL);
@ -1057,8 +1064,12 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// r0:r1: result
// sp: stack pointer
// fp: frame pointer
// Callee-saved register r4 still holds argc.
__ LeaveExitFrame(save_doubles(), r4, true);
Register argc;
if (!argv_in_register()) {
// Callee-saved register r4 still holds argc.
argc = r4;
}
__ LeaveExitFrame(save_doubles(), argc, true);
__ mov(pc, lr);
// Handling of exception.

View File

@ -417,16 +417,27 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
r0, // argument count (including receiver)
r0, // argument count (not including receiver)
r2, // address of first argument
r1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
r0, // argument count (argc)
r2, // address of first argument (argv)
r1 // the runtime function to call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8

View File

@ -1753,7 +1753,7 @@ void Builtins::Generate_Construct(MacroAssembler* masm) {
// static
void Builtins::Generate_PushArgsAndCall(MacroAssembler* masm) {
void Builtins::Generate_InterpreterPushArgsAndCall(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

View File

@ -1067,6 +1067,8 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// Register parameters:
// x0: argc (including receiver, untagged)
// x1: target
// If argv_in_register():
// x11: argv (pointer to first argument)
//
// The stack on entry holds the arguments and the receiver, with the receiver
// at the highest address:
@ -1098,9 +1100,11 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// (arg[argc-2]), or just below the receiver in case there are no arguments.
// - Adjust for the arg[] array.
Register temp_argv = x11;
__ Add(temp_argv, jssp, Operand(x0, LSL, kPointerSizeLog2));
// - Adjust for the receiver.
__ Sub(temp_argv, temp_argv, 1 * kPointerSize);
if (!argv_in_register()) {
__ Add(temp_argv, jssp, Operand(x0, LSL, kPointerSizeLog2));
// - Adjust for the receiver.
__ Sub(temp_argv, temp_argv, 1 * kPointerSize);
}
// Enter the exit frame. Reserve three slots to preserve x21-x23 callee-saved
// registers.
@ -1204,12 +1208,10 @@ void CEntryStub::Generate(MacroAssembler* masm) {
__ LeaveExitFrame(save_doubles(), x10, true);
DCHECK(jssp.Is(__ StackPointer()));
// Pop or drop the remaining stack slots and return from the stub.
// jssp[24]: Arguments array (of size argc), including receiver.
// jssp[16]: Preserved x23 (used for target).
// jssp[8]: Preserved x22 (used for argc).
// jssp[0]: Preserved x21 (used for argv).
__ Drop(x11);
if (!argv_in_register()) {
// Drop the remaining stack slots and return from the stub.
__ Drop(x11);
}
__ AssertFPCRState();
__ Ret();

View File

@ -446,16 +446,28 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
x0, // argument count (including receiver)
x0, // argument count (not including receiver)
x2, // address of first argument
x1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
x0, // argument count (argc)
x11, // address of first argument (argv)
x1 // the runtime function to call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8

View File

@ -1445,6 +1445,13 @@ ExternalReference ExternalReference::vector_store_virtual_register(
}
ExternalReference ExternalReference::runtime_function_table_address(
Isolate* isolate) {
return ExternalReference(
const_cast<Runtime::Function*>(Runtime::RuntimeFunctionTable(isolate)));
}
double power_helper(double x, double y) {
int y_int = static_cast<int>(y);
if (y == y_int) {

View File

@ -319,6 +319,8 @@ class Label {
enum SaveFPRegsMode { kDontSaveFPRegs, kSaveFPRegs };
enum ArgvMode { kArgvOnStack, kArgvInRegister };
// Specifies whether to perform icache flush operations on RelocInfo updates.
// If FLUSH_ICACHE_IF_NEEDED, the icache will always be flushed if an
// instruction was modified. If SKIP_ICACHE_FLUSH the flush will always be
@ -992,6 +994,8 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference vector_store_virtual_register(Isolate* isolate);
static ExternalReference runtime_function_table_address(Isolate* isolate);
Address address() const { return reinterpret_cast<Address>(address_); }
// Used to check if single stepping is enabled in generated code.

View File

@ -254,6 +254,7 @@ namespace internal {
"Unexpected number of pre-allocated property fields") \
V(kUnexpectedFPCRMode, "Unexpected FPCR mode.") \
V(kUnexpectedSmi, "Unexpected smi value") \
V(kUnexpectedStackPointer, "The stack pointer is not the expected value") \
V(kUnexpectedStringFunction, "Unexpected String function") \
V(kUnexpectedStringType, "Unexpected string type") \
V(kUnexpectedStringWrapperInstanceSize, \

View File

@ -81,16 +81,12 @@ enum BuiltinExtraArguments {
V(ConstructProxy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(Construct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(PushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubForDerived, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructStubApi, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterExitTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(CompileLazy, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(CompileOptimized, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(CompileOptimizedConcurrent, BUILTIN, UNINITIALIZED, kNoExtraICState) \
@ -100,6 +96,10 @@ enum BuiltinExtraArguments {
V(NotifyStubFailure, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(NotifyStubFailureSaveDoubles, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(InterpreterEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterExitTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(InterpreterPushArgsAndCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
\
V(LoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(KeyedLoadIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
V(StoreIC_Miss, BUILTIN, UNINITIALIZED, kNoExtraICState) \
@ -266,8 +266,6 @@ class Builtins {
static void Generate_JSConstructStubApi(MacroAssembler* masm);
static void Generate_JSEntryTrampoline(MacroAssembler* masm);
static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
static void Generate_InterpreterEntryTrampoline(MacroAssembler* masm);
static void Generate_InterpreterExitTrampoline(MacroAssembler* masm);
static void Generate_NotifyDeoptimized(MacroAssembler* masm);
static void Generate_NotifySoftDeoptimized(MacroAssembler* masm);
static void Generate_NotifyLazyDeoptimized(MacroAssembler* masm);
@ -287,8 +285,6 @@ class Builtins {
// ES6 section 7.3.13 Construct (F, [argumentsList], [newTarget])
static void Generate_Construct(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);
@ -304,6 +300,10 @@ class Builtins {
static void Generate_InterruptCheck(MacroAssembler* masm);
static void Generate_StackCheck(MacroAssembler* masm);
static void Generate_InterpreterEntryTrampoline(MacroAssembler* masm);
static void Generate_InterpreterExitTrampoline(MacroAssembler* masm);
static void Generate_InterpreterPushArgsAndCall(MacroAssembler* masm);
#define DECLARE_CODE_AGE_BUILTIN_GENERATOR(C) \
static void Generate_Make##C##CodeYoungAgainEvenMarking( \
MacroAssembler* masm); \

View File

@ -269,9 +269,19 @@ Callable CodeFactory::CallFunction(Isolate* isolate, int argc,
// static
Callable CodeFactory::PushArgsAndCall(Isolate* isolate) {
return Callable(isolate->builtins()->PushArgsAndCall(),
PushArgsAndCallDescriptor(isolate));
Callable CodeFactory::InterpreterPushArgsAndCall(Isolate* isolate) {
return Callable(isolate->builtins()->InterpreterPushArgsAndCall(),
InterpreterPushArgsAndCallDescriptor(isolate));
}
// static
Callable CodeFactory::InterpreterCEntry(Isolate* isolate) {
// TODO(rmcilroy): Deal with runtime functions that return two values.
// Note: If we ever use fpregs in the interpreter then we will need to
// save fpregs too.
CEntryStub stub(isolate, 1, kDontSaveFPRegs, kArgvInRegister);
return Callable(stub.GetCode(), InterpreterCEntryDescriptor(isolate));
}
} // namespace internal

View File

@ -97,7 +97,8 @@ class CodeFactory final {
static Callable CallFunction(Isolate* isolate, int argc,
CallFunctionFlags flags);
static Callable PushArgsAndCall(Isolate* isolate);
static Callable InterpreterPushArgsAndCall(Isolate* isolate);
static Callable InterpreterCEntry(Isolate* isolate);
};
} // namespace internal

View File

@ -1824,9 +1824,11 @@ std::ostream& operator<<(std::ostream& os, const CompareNilICStub::State& s);
class CEntryStub : public PlatformCodeStub {
public:
CEntryStub(Isolate* isolate, int result_size,
SaveFPRegsMode save_doubles = kDontSaveFPRegs)
SaveFPRegsMode save_doubles = kDontSaveFPRegs,
ArgvMode argv_mode = kArgvOnStack)
: PlatformCodeStub(isolate) {
minor_key_ = SaveDoublesBits::encode(save_doubles == kSaveFPRegs);
minor_key_ = SaveDoublesBits::encode(save_doubles == kSaveFPRegs) |
ArgvMode::encode(argv_mode == kArgvInRegister);
DCHECK(result_size == 1 || result_size == 2);
#if _WIN64 || V8_TARGET_ARCH_PPC
minor_key_ = ResultSizeBits::update(minor_key_, result_size);
@ -1841,6 +1843,7 @@ class CEntryStub : public PlatformCodeStub {
private:
bool save_doubles() const { return SaveDoublesBits::decode(minor_key_); }
bool argv_in_register() const { return ArgvMode::decode(minor_key_); }
#if _WIN64 || V8_TARGET_ARCH_PPC
int result_size() const { return ResultSizeBits::decode(minor_key_); }
#endif // _WIN64
@ -1848,7 +1851,8 @@ class CEntryStub : public PlatformCodeStub {
bool NeedsImmovableCode() override;
class SaveDoublesBits : public BitField<bool, 0, 1> {};
class ResultSizeBits : public BitField<int, 1, 3> {};
class ArgvMode : public BitField<bool, 1, 1> {};
class ResultSizeBits : public BitField<int, 2, 3> {};
DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR();
DEFINE_PLATFORM_CODE_STUB(CEntry, PlatformCodeStub);

View File

@ -285,6 +285,12 @@ void BytecodeGraphBuilder::VisitCall(
}
void BytecodeGraphBuilder::VisitCallRuntime(
const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED();
}
void BytecodeGraphBuilder::BuildBinaryOp(
const Operator* js_op, const interpreter::BytecodeArrayIterator& iterator) {
Node* left = environment()->LookupRegister(iterator.GetRegisterOperand(0));

View File

@ -314,13 +314,30 @@ Node* InterpreterAssembler::LoadTypeFeedbackVector() {
}
Node* InterpreterAssembler::CallN(CallDescriptor* descriptor,
Node* code_target,
Node** args) {
Node* stack_pointer_before_call = nullptr;
if (FLAG_debug_code) {
stack_pointer_before_call = raw_assembler_->LoadStackPointer();
}
Node* return_val = raw_assembler_->CallN(descriptor, code_target, args);
if (FLAG_debug_code) {
Node* stack_pointer_after_call = raw_assembler_->LoadStackPointer();
AbortIfWordNotEqual(stack_pointer_before_call, stack_pointer_after_call,
kUnexpectedStackPointer);
}
return return_val;
}
Node* InterpreterAssembler::CallJS(Node* function, Node* first_arg,
Node* arg_count) {
Callable builtin = CodeFactory::PushArgsAndCall(isolate());
Callable callable = CodeFactory::InterpreterPushArgsAndCall(isolate());
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), builtin.descriptor(), 0, CallDescriptor::kNoFlags);
isolate(), zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags);
Node* code_target = HeapConstant(builtin.code());
Node* code_target = HeapConstant(callable.code());
Node** args = zone()->NewArray<Node*>(4);
args[0] = arg_count;
@ -328,7 +345,7 @@ Node* InterpreterAssembler::CallJS(Node* function, Node* first_arg,
args[2] = function;
args[3] = ContextTaggedPointer();
return raw_assembler_->CallN(descriptor, code_target, args);
return CallN(descriptor, code_target, args);
}
@ -336,7 +353,7 @@ Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
Node* target, Node** args) {
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), descriptor, 0, CallDescriptor::kNoFlags);
return raw_assembler_->CallN(call_descriptor, target, args);
return CallN(call_descriptor, target, args);
}
@ -367,6 +384,33 @@ Node* InterpreterAssembler::CallIC(CallInterfaceDescriptor descriptor,
}
Node* InterpreterAssembler::CallRuntime(Node* function_id, Node* first_arg,
Node* arg_count) {
Callable callable = CodeFactory::InterpreterCEntry(isolate());
CallDescriptor* descriptor = Linkage::GetStubCallDescriptor(
isolate(), zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags);
Node* code_target = HeapConstant(callable.code());
// Get the function entry from the function id.
Node* function_table = raw_assembler_->ExternalConstant(
ExternalReference::runtime_function_table_address(isolate()));
Node* function_offset = raw_assembler_->Int32Mul(
function_id, Int32Constant(sizeof(Runtime::Function)));
Node* function = IntPtrAdd(function_table, function_offset);
Node* function_entry = raw_assembler_->Load(
kMachPtr, function, Int32Constant(offsetof(Runtime::Function, entry)));
Node** args = zone()->NewArray<Node*>(4);
args[0] = arg_count;
args[1] = first_arg;
args[2] = function_entry;
args[3] = ContextTaggedPointer();
return CallN(descriptor, code_target, args);
}
Node* InterpreterAssembler::CallRuntime(Runtime::FunctionId function_id,
Node* arg1) {
return raw_assembler_->CallRuntime1(function_id, arg1,
@ -464,6 +508,19 @@ void InterpreterAssembler::DispatchTo(Node* new_bytecode_offset) {
}
void InterpreterAssembler::AbortIfWordNotEqual(
Node* lhs, Node* rhs, BailoutReason bailout_reason) {
RawMachineAssembler::Label match, no_match;
Node* condition = raw_assembler_->WordEqual(lhs, rhs);
raw_assembler_->Branch(condition, &match, &no_match);
raw_assembler_->Bind(&no_match);
Node* abort_id = SmiTag(Int32Constant(bailout_reason));
CallRuntime(Runtime::kAbort, abort_id);
Return();
raw_assembler_->Bind(&match);
}
void InterpreterAssembler::AddEndInput(Node* input) {
DCHECK_NOT_NULL(input);
end_nodes_.push_back(input);

View File

@ -110,6 +110,7 @@ class InterpreterAssembler {
Node* arg2, Node* arg3, Node* arg4, Node* arg5);
// Call runtime function.
Node* CallRuntime(Node* function_id, Node* first_arg, Node* arg_count);
Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1);
Node* CallRuntime(Runtime::FunctionId function_id, Node* arg1, Node* arg2);
@ -156,6 +157,7 @@ class InterpreterAssembler {
Node* BytecodeOperandSignExtended(int operand_index);
Node* BytecodeOperandShort(int operand_index);
Node* CallN(CallDescriptor* descriptor, Node* code_target, Node** args);
Node* CallIC(CallInterfaceDescriptor descriptor, Node* target, Node** args);
Node* CallJSBuiltin(int context_index, Node* receiver, Node** js_args,
int js_arg_count);
@ -168,6 +170,9 @@ class InterpreterAssembler {
// Starts next instruction dispatch at |new_bytecode_offset|.
void DispatchTo(Node* new_bytecode_offset);
// Abort operations for debug code.
void AbortIfWordNotEqual(Node* lhs, Node* rhs, BailoutReason bailout_reason);
// Adds an end node of the graph.
void AddEndInput(Node* input);

View File

@ -708,6 +708,41 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(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_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);
@ -1626,41 +1661,6 @@ void Builtins::Generate_Construct(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

View File

@ -2425,11 +2425,23 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// esp: stack pointer (restored after C call)
// esi: current context (C callee-saved)
// edi: JS function of the caller (C callee-saved)
//
// If argv_in_register():
// ecx: pointer to the first argument
ProfileEntryHookStub::MaybeCallEntryHook(masm);
// Enter the exit frame that transitions from JavaScript to C++.
__ EnterExitFrame(save_doubles());
if (argv_in_register()) {
DCHECK(!save_doubles());
__ EnterApiExitFrame(3);
// Move argc and argv into the correct registers.
__ mov(esi, ecx);
__ mov(edi, eax);
} else {
__ EnterExitFrame(save_doubles());
}
// ebx: pointer to C function (C callee-saved)
// ebp: frame pointer (restored after C call)
@ -2474,7 +2486,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
}
// Exit the JavaScript to C++ exit frame.
__ LeaveExitFrame(save_doubles());
__ LeaveExitFrame(save_doubles(), !argv_in_register());
__ ret(0);
// Handling of exception.

View File

@ -400,16 +400,27 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
eax, // argument count (including receiver)
eax, // argument count (not including receiver)
ebx, // address of first argument
edi // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
eax, // argument count (argc)
ecx, // address of first argument (argv)
ebx // the runtime function to call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8

View File

@ -974,7 +974,7 @@ void MacroAssembler::EnterApiExitFrame(int argc) {
}
void MacroAssembler::LeaveExitFrame(bool save_doubles) {
void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) {
// Optionally restore all XMM registers.
if (save_doubles) {
const int offset = -2 * kPointerSize;
@ -984,15 +984,20 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles) {
}
}
// Get the return address from the stack and restore the frame pointer.
mov(ecx, Operand(ebp, 1 * kPointerSize));
mov(ebp, Operand(ebp, 0 * kPointerSize));
if (pop_arguments) {
// Get the return address from the stack and restore the frame pointer.
mov(ecx, Operand(ebp, 1 * kPointerSize));
mov(ebp, Operand(ebp, 0 * kPointerSize));
// Pop the arguments and the receiver from the caller stack.
lea(esp, Operand(esi, 1 * kPointerSize));
// Pop the arguments and the receiver from the caller stack.
lea(esp, Operand(esi, 1 * kPointerSize));
// Push the return address to get ready to return.
push(ecx);
// Push the return address to get ready to return.
push(ecx);
} else {
// Otherwise just leave the exit frame.
leave();
}
LeaveExitFrameEpilogue(true);
}

View File

@ -278,8 +278,8 @@ class MacroAssembler: public Assembler {
// Leave the current exit frame. Expects the return value in
// register eax:edx (untouched) and the pointer to the first
// argument in register esi.
void LeaveExitFrame(bool save_doubles);
// argument in register esi (if pop_arguments == true).
void LeaveExitFrame(bool save_doubles, bool pop_arguments = true);
// Leave the current exit frame. Expects the return value in
// register eax (untouched).

View File

@ -38,7 +38,6 @@ class PlatformInterfaceDescriptor;
V(CallFunctionWithFeedbackAndVector) \
V(CallConstruct) \
V(CallTrampoline) \
V(PushArgsAndCall) \
V(RegExpConstructResult) \
V(TransitionElementsKind) \
V(AllocateHeapNumber) \
@ -70,7 +69,9 @@ class PlatformInterfaceDescriptor;
V(ContextOnly) \
V(GrowArrayElements) \
V(MathRoundVariantCallFromUnoptimizedCode) \
V(MathRoundVariantCallFromOptimizedCode)
V(MathRoundVariantCallFromOptimizedCode) \
V(InterpreterPushArgsAndCall) \
V(InterpreterCEntry)
class CallInterfaceDescriptorData {
@ -706,11 +707,19 @@ class GrowArrayElementsDescriptor : public CallInterfaceDescriptor {
};
class PushArgsAndCallDescriptor : public CallInterfaceDescriptor {
class InterpreterPushArgsAndCallDescriptor : public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(PushArgsAndCallDescriptor, CallInterfaceDescriptor)
DECLARE_DESCRIPTOR(InterpreterPushArgsAndCallDescriptor,
CallInterfaceDescriptor)
};
class InterpreterCEntryDescriptor : public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(InterpreterCEntryDescriptor, CallInterfaceDescriptor)
};
#undef DECLARE_DESCRIPTOR

View File

@ -161,7 +161,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(Handle<Object> object) {
size_t entry = GetConstantPoolEntry(object);
if (FitsInIdxOperand(entry)) {
if (FitsInIdx8Operand(entry)) {
Output(Bytecode::kLdaConstant, static_cast<uint8_t>(entry));
} else {
UNIMPLEMENTED();
@ -216,7 +216,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreAccumulatorInRegister(
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadGlobal(int slot_index) {
DCHECK(slot_index >= 0);
if (FitsInIdxOperand(slot_index)) {
if (FitsInIdx8Operand(slot_index)) {
Output(Bytecode::kLdaGlobal, static_cast<uint8_t>(slot_index));
} else {
UNIMPLEMENTED();
@ -230,7 +230,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadNamedProperty(
UNIMPLEMENTED();
}
if (FitsInIdxOperand(feedback_slot)) {
if (FitsInIdx8Operand(feedback_slot)) {
Output(Bytecode::kLoadIC, object.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
@ -246,7 +246,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadKeyedProperty(
UNIMPLEMENTED();
}
if (FitsInIdxOperand(feedback_slot)) {
if (FitsInIdx8Operand(feedback_slot)) {
Output(Bytecode::kKeyedLoadIC, object.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
@ -263,7 +263,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreNamedProperty(
UNIMPLEMENTED();
}
if (FitsInIdxOperand(feedback_slot)) {
if (FitsInIdx8Operand(feedback_slot)) {
Output(Bytecode::kStoreIC, object.ToOperand(), name.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
@ -280,7 +280,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
UNIMPLEMENTED();
}
if (FitsInIdxOperand(feedback_slot)) {
if (FitsInIdx8Operand(feedback_slot)) {
Output(Bytecode::kKeyedStoreIC, object.ToOperand(), key.ToOperand(),
static_cast<uint8_t>(feedback_slot));
} else {
@ -376,7 +376,7 @@ void BytecodeArrayBuilder::PatchJump(
} else {
// Update the jump type and operand
size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
if (FitsInIdxOperand(entry)) {
if (FitsInIdx8Operand(entry)) {
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
*jump_location++ = Bytecodes::ToByte(jump_bytecode);
*jump_location = static_cast<uint8_t>(entry);
@ -414,7 +414,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
Output(jump_bytecode, static_cast<uint8_t>(delta));
} else {
size_t entry = GetConstantPoolEntry(handle(Smi::FromInt(delta), isolate()));
if (FitsInIdxOperand(entry)) {
if (FitsInIdx8Operand(entry)) {
Output(GetJumpWithConstantOperand(jump_bytecode),
static_cast<uint8_t>(entry));
} else {
@ -467,7 +467,7 @@ void BytecodeArrayBuilder::EnsureReturn() {
BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
Register receiver,
size_t arg_count) {
if (FitsInIdxOperand(arg_count)) {
if (FitsInIdx8Operand(arg_count)) {
Output(Bytecode::kCall, callable.ToOperand(), receiver.ToOperand(),
static_cast<uint8_t>(arg_count));
} else {
@ -477,6 +477,16 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Call(Register callable,
}
BytecodeArrayBuilder& BytecodeArrayBuilder::CallRuntime(
Runtime::FunctionId function_id, Register first_arg, size_t arg_count) {
DCHECK(FitsInIdx16Operand(function_id));
DCHECK(FitsInIdx8Operand(arg_count));
Output(Bytecode::kCallRuntime, static_cast<uint16_t>(function_id),
first_arg.ToOperand(), static_cast<uint8_t>(arg_count));
return *this;
}
size_t BytecodeArrayBuilder::GetConstantPoolEntry(Handle<Object> object) {
// These constants shouldn't be added to the constant pool, the should use
// specialzed bytecodes instead.
@ -597,13 +607,13 @@ Bytecode BytecodeArrayBuilder::BytecodeForCompareOperation(Token::Value op) {
// static
bool BytecodeArrayBuilder::FitsInIdxOperand(int value) {
bool BytecodeArrayBuilder::FitsInIdx8Operand(int value) {
return kMinUInt8 <= value && value <= kMaxUInt8;
}
// static
bool BytecodeArrayBuilder::FitsInIdxOperand(size_t value) {
bool BytecodeArrayBuilder::FitsInIdx8Operand(size_t value) {
return value <= static_cast<size_t>(kMaxUInt8);
}
@ -614,6 +624,12 @@ bool BytecodeArrayBuilder::FitsInImm8Operand(int value) {
}
// static
bool BytecodeArrayBuilder::FitsInIdx16Operand(int value) {
return kMinUInt16 <= value && value <= kMaxUInt16;
}
TemporaryRegisterScope::TemporaryRegisterScope(BytecodeArrayBuilder* builder)
: builder_(builder), count_(0), last_register_index_(-1) {}

View File

@ -75,6 +75,12 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& Call(Register callable, Register receiver,
size_t arg_count);
// Call the runtime function with |function_id|. The first argument should be
// in |first_arg| and all subsequent arguments should be in registers
// <first_arg + 1> to <first_arg + 1 + arg_count>.
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
Register first_arg, size_t arg_count);
// Operators (register == lhs, accumulator = rhs).
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
@ -107,9 +113,12 @@ class BytecodeArrayBuilder {
static Bytecode BytecodeForBinaryOperation(Token::Value op);
static Bytecode BytecodeForCompareOperation(Token::Value op);
static bool FitsInIdxOperand(int value);
static bool FitsInIdxOperand(size_t value);
static bool FitsInIdx8Operand(int value);
static bool FitsInIdx8Operand(size_t value);
static bool FitsInImm8Operand(int value);
static bool FitsInIdx16Operand(int value);
static Bytecode GetJumpWithConstantOperand(Bytecode jump_with_smi8_operand);
template <size_t N>
@ -119,6 +128,7 @@ class BytecodeArrayBuilder {
void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1);
void Output(Bytecode bytecode, uint32_t operand0);
void Output(Bytecode bytecode);
BytecodeArrayBuilder& OutputJump(Bytecode jump_bytecode,
BytecodeLabel* label);
void PatchJump(const ZoneVector<uint8_t>::iterator& jump_target,

View File

@ -59,6 +59,12 @@ int8_t BytecodeArrayIterator::GetImmediateOperand(int operand_index) const {
}
int BytecodeArrayIterator::GetCountOperand(int operand_index) const {
uint32_t operand = GetRawOperand(operand_index, OperandType::kCount8);
return static_cast<int>(operand);
}
int BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
OperandSize size =
Bytecodes::GetOperandSize(current_bytecode(), operand_index);

View File

@ -27,6 +27,7 @@ class BytecodeArrayIterator {
int8_t GetImmediateOperand(int operand_index) const;
int GetIndexOperand(int operand_index) const;
int GetCountOperand(int operand_index) const;
Register GetRegisterOperand(int operand_index) const;
Handle<Object> GetConstantForIndexOperand(int operand_index) const;

View File

@ -593,7 +593,30 @@ void BytecodeGenerator::VisitCall(Call* expr) {
void BytecodeGenerator::VisitCallNew(CallNew* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) { UNIMPLEMENTED(); }
void BytecodeGenerator::VisitCallRuntime(CallRuntime* expr) {
if (expr->is_jsruntime()) {
UNIMPLEMENTED();
}
// Evaluate all arguments to the runtime call.
ZoneList<Expression*>* args = expr->arguments();
TemporaryRegisterScope temporary_register_scope(&builder_);
// Ensure we always have a valid first_arg register even if there are no
// arguments to pass.
Register first_arg = temporary_register_scope.NewRegister();
for (int i = 0; i < args->length(); ++i) {
Register arg =
(i == 0) ? first_arg : temporary_register_scope.NewRegister();
Visit(args->at(i));
DCHECK_EQ(arg.index() - i, first_arg.index());
builder()->StoreAccumulatorInRegister(arg);
}
// TODO(rmcilroy): support multiple return values.
DCHECK_LE(expr->function()->result_size, 1);
Runtime::FunctionId function_id = expr->function()->function_id;
builder()->CallRuntime(function_id, first_arg, args->length());
}
void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {

View File

@ -67,6 +67,8 @@ namespace interpreter {
\
/* Call operations. */ \
V(Call, OperandType::kReg8, OperandType::kReg8, OperandType::kCount8) \
V(CallRuntime, OperandType::kIdx16, OperandType::kReg8, \
OperandType::kCount8) \
\
/* Test Operators */ \
V(TestEqual, OperandType::kReg8) \

View File

@ -334,10 +334,10 @@ void Interpreter::DoMod(compiler::InterpreterAssembler* assembler) {
}
// Call <receiver> <arg_count>
// Call <callable> <receiver> <arg_count>
//
// Call a JS function with receiver and |arg_count| arguments in subsequent
// registers. The JSfunction or Callable to call is in the accumulator.
// Call a JSfunction or Callable in |callable| with receiver and |arg_count|
// arguments in subsequent registers.
void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
Node* function_reg = __ BytecodeOperandReg8(0);
Node* function = __ LoadRegister(function_reg);
@ -350,6 +350,21 @@ void Interpreter::DoCall(compiler::InterpreterAssembler* assembler) {
}
// CallRuntime <function_id> <first_arg> <arg_count>
//
// Call the runtime function |function_id| with first argument in register
// |first_arg| and |arg_count| arguments in subsequent registers.
void Interpreter::DoCallRuntime(compiler::InterpreterAssembler* assembler) {
Node* function_id = __ BytecodeOperandIdx16(0);
Node* first_arg_reg = __ BytecodeOperandReg8(1);
Node* first_arg = __ RegisterLocation(first_arg_reg);
Node* args_count = __ BytecodeOperandCount8(2);
Node* result = __ CallRuntime(function_id, first_arg, args_count);
__ SetAccumulator(result);
__ Dispatch();
}
// TestEqual <src>
//
// Test if the value in the <src> register equals the accumulator.

View File

@ -963,6 +963,35 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(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_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);
@ -1710,35 +1739,6 @@ void Builtins::Generate_Construct(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 -------------

View File

@ -1066,13 +1066,20 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// fp: frame pointer (restored after C call)
// sp: stack pointer (restored as callee's sp after C call)
// cp: current context (C callee-saved)
//
// If argv_in_register():
// a2: pointer to the first argument
ProfileEntryHookStub::MaybeCallEntryHook(masm);
// Compute the argv pointer in a callee-saved register.
__ sll(s1, a0, kPointerSizeLog2);
__ Addu(s1, sp, s1);
__ Subu(s1, s1, kPointerSize);
if (!argv_in_register()) {
// Compute the argv pointer in a callee-saved register.
__ sll(s1, a0, kPointerSizeLog2);
__ Addu(s1, sp, s1);
__ Subu(s1, s1, kPointerSize);
} else {
__ mov(s1, a2);
}
// Enter the exit frame that transitions from JavaScript to C++.
FrameScope scope(masm, StackFrame::MANUAL);
@ -1153,8 +1160,12 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// v0:v1: result
// sp: stack pointer
// fp: frame pointer
// s0: still holds argc (callee-saved).
__ LeaveExitFrame(save_doubles(), s0, true, EMIT_RETURN);
Register argc;
if (!argv_in_register()) {
// s0: still holds argc (callee-saved).
argc = s0;
}
__ LeaveExitFrame(save_doubles(), argc, true, EMIT_RETURN);
// Handling of exception.
__ bind(&exception_returned);

View File

@ -392,16 +392,27 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (including receiver)
a0, // argument count (not including receiver)
a2, // address of first argument
a1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (argc)
a2, // address of first argument (argv)
a1 // the runtime function to call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8

View File

@ -960,6 +960,35 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(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_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);
@ -1706,35 +1735,6 @@ void Builtins::Generate_Construct(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 -------------

View File

@ -1064,13 +1064,20 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// fp: frame pointer (restored after C call)
// sp: stack pointer (restored as callee's sp after C call)
// cp: current context (C callee-saved)
//
// If argv_in_register():
// a2: pointer to the first argument
ProfileEntryHookStub::MaybeCallEntryHook(masm);
// Compute the argv pointer in a callee-saved register.
__ dsll(s1, a0, kPointerSizeLog2);
__ Daddu(s1, sp, s1);
__ Dsubu(s1, s1, kPointerSize);
if (!argv_in_register()) {
// Compute the argv pointer in a callee-saved register.
__ dsll(s1, a0, kPointerSizeLog2);
__ Daddu(s1, sp, s1);
__ Dsubu(s1, s1, kPointerSize);
} else {
__ mov(s1, a2);
}
// Enter the exit frame that transitions from JavaScript to C++.
FrameScope scope(masm, StackFrame::MANUAL);
@ -1150,8 +1157,12 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// v0:v1: result
// sp: stack pointer
// fp: frame pointer
// s0: still holds argc (callee-saved).
__ LeaveExitFrame(save_doubles(), s0, true, EMIT_RETURN);
Register argc;
if (!argv_in_register()) {
// s0: still holds argc (callee-saved).
argc = s0;
}
__ LeaveExitFrame(save_doubles(), argc, true, EMIT_RETURN);
// Handling of exception.
__ bind(&exception_returned);

View File

@ -392,16 +392,27 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (including receiver)
a0, // argument count (not including receiver)
a2, // address of first argument
a1 // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
a0, // argument count (argc)
a2, // address of first argument (argv)
a1 // the runtime function to call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8

View File

@ -4,6 +4,7 @@
#include "src/runtime/runtime.h"
#include "src/assembler.h"
#include "src/contexts.h"
#include "src/handles-inl.h"
#include "src/heap/heap.h"
@ -94,6 +95,31 @@ const Runtime::Function* Runtime::FunctionForId(Runtime::FunctionId id) {
}
const Runtime::Function* Runtime::RuntimeFunctionTable(Isolate* isolate) {
if (isolate->external_reference_redirector()) {
// When running with the simulator we need to provide a table which has
// redirected runtime entry addresses.
if (!isolate->runtime_state()->redirected_intrinsic_functions()) {
size_t function_count = arraysize(kIntrinsicFunctions);
Function* redirected_functions = new Function[function_count];
memcpy(redirected_functions, kIntrinsicFunctions,
sizeof(kIntrinsicFunctions));
for (size_t i = 0; i < function_count; i++) {
ExternalReference redirected_entry(static_cast<Runtime::FunctionId>(i),
isolate);
redirected_functions[i].entry = redirected_entry.address();
}
isolate->runtime_state()->set_redirected_intrinsic_functions(
redirected_functions);
}
return isolate->runtime_state()->redirected_intrinsic_functions();
} else {
return kIntrinsicFunctions;
}
}
std::ostream& operator<<(std::ostream& os, Runtime::FunctionId id) {
return os << Runtime::FunctionForId(id)->name;
}

View File

@ -1110,27 +1110,6 @@ FOR_EACH_INTRINSIC_RETURN_OBJECT(F)
//---------------------------------------------------------------------------
// Runtime provides access to all C++ runtime functions.
class RuntimeState {
public:
unibrow::Mapping<unibrow::ToUppercase, 128>* to_upper_mapping() {
return &to_upper_mapping_;
}
unibrow::Mapping<unibrow::ToLowercase, 128>* to_lower_mapping() {
return &to_lower_mapping_;
}
private:
RuntimeState() {}
unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping_;
unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping_;
friend class Isolate;
friend class Runtime;
DISALLOW_COPY_AND_ASSIGN(RuntimeState);
};
class Runtime : public AllStatic {
public:
enum FunctionId {
@ -1179,6 +1158,9 @@ class Runtime : public AllStatic {
// Get the intrinsic function with the given function entry address.
static const Function* FunctionForEntry(Address ref);
// Get the runtime intrinsic function table.
static const Function* RuntimeFunctionTable(Isolate* isolate);
MUST_USE_RESULT static MaybeHandle<Object> DeleteObjectProperty(
Isolate* isolate, Handle<JSReceiver> receiver, Handle<Object> key,
LanguageMode language_mode);
@ -1229,6 +1211,38 @@ class Runtime : public AllStatic {
};
class RuntimeState {
public:
unibrow::Mapping<unibrow::ToUppercase, 128>* to_upper_mapping() {
return &to_upper_mapping_;
}
unibrow::Mapping<unibrow::ToLowercase, 128>* to_lower_mapping() {
return &to_lower_mapping_;
}
Runtime::Function* redirected_intrinsic_functions() {
return redirected_intrinsic_functions_.get();
}
void set_redirected_intrinsic_functions(
Runtime::Function* redirected_intrinsic_functions) {
redirected_intrinsic_functions_.Reset(redirected_intrinsic_functions);
}
private:
RuntimeState() {}
unibrow::Mapping<unibrow::ToUppercase, 128> to_upper_mapping_;
unibrow::Mapping<unibrow::ToLowercase, 128> to_lower_mapping_;
base::SmartArrayPointer<Runtime::Function> redirected_intrinsic_functions_;
friend class Isolate;
friend class Runtime;
DISALLOW_COPY_AND_ASSIGN(RuntimeState);
};
std::ostream& operator<<(std::ostream&, Runtime::FunctionId);
//---------------------------------------------------------------------------

View File

@ -131,6 +131,8 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) {
"Isolate::stress_deopt_count_address()");
Add(ExternalReference::vector_store_virtual_register(isolate).address(),
"Isolate::vector_store_virtual_register()");
Add(ExternalReference::runtime_function_table_address(isolate).address(),
"Runtime::runtime_function_table_address()");
// Debug addresses
Add(ExternalReference::debug_after_break_target_address(isolate).address(),

View File

@ -771,6 +771,41 @@ void Builtins::Generate_InterpreterExitTrampoline(MacroAssembler* masm) {
}
// static
void Builtins::Generate_InterpreterPushArgsAndCall(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_CompileLazy(MacroAssembler* masm) {
CallRuntimePassFunction(masm, Runtime::kCompileLazy);
GenerateTailCallToReturnedCode(masm);
@ -1832,41 +1867,6 @@ void Builtins::Generate_Construct(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));

View File

@ -2268,6 +2268,9 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// rbp: frame pointer of calling JS frame (restored after C call)
// rsp: stack pointer (restored after C call)
// rsi: current context (restored)
//
// If argv_in_register():
// r15: pointer to the first argument
ProfileEntryHookStub::MaybeCallEntryHook(masm);
@ -2277,7 +2280,14 @@ void CEntryStub::Generate(MacroAssembler* masm) {
#else // _WIN64
int arg_stack_space = 0;
#endif // _WIN64
__ EnterExitFrame(arg_stack_space, save_doubles());
if (argv_in_register()) {
DCHECK(!save_doubles());
__ EnterApiExitFrame(arg_stack_space);
// Move argc into r14 (argv is already in r15).
__ movp(r14, rax);
} else {
__ EnterExitFrame(arg_stack_space, save_doubles());
}
// rbx: pointer to builtin function (C callee-saved).
// rbp: frame pointer of exit frame (restored after C call).
@ -2357,7 +2367,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
}
// Exit the JavaScript to C++ exit frame.
__ LeaveExitFrame(save_doubles());
__ LeaveExitFrame(save_doubles(), !argv_in_register());
__ ret(0);
// Handling of exception.

View File

@ -392,16 +392,27 @@ void MathRoundVariantCallFromOptimizedCodeDescriptor::
}
void PushArgsAndCallDescriptor::InitializePlatformSpecific(
void InterpreterPushArgsAndCallDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
rax, // argument count (including receiver)
rax, // argument count (not including receiver)
rbx, // address of first argument
rdi // the target callable to be call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void InterpreterCEntryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {
rax, // argument count (argc)
r15, // address of first argument (argv)
rbx // the runtime function to call
};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
} // namespace internal
} // namespace v8

View File

@ -3757,7 +3757,7 @@ void MacroAssembler::EnterApiExitFrame(int arg_stack_space) {
}
void MacroAssembler::LeaveExitFrame(bool save_doubles) {
void MacroAssembler::LeaveExitFrame(bool save_doubles, bool pop_arguments) {
// Registers:
// r15 : argv
if (save_doubles) {
@ -3769,15 +3769,21 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles) {
movsd(reg, Operand(rbp, offset - ((i + 1) * kDoubleSize)));
}
}
// Get the return address from the stack and restore the frame pointer.
movp(rcx, Operand(rbp, kFPOnStackSize));
movp(rbp, Operand(rbp, 0 * kPointerSize));
// Drop everything up to and including the arguments and the receiver
// from the caller stack.
leap(rsp, Operand(r15, 1 * kPointerSize));
if (pop_arguments) {
// Get the return address from the stack and restore the frame pointer.
movp(rcx, Operand(rbp, kFPOnStackSize));
movp(rbp, Operand(rbp, 0 * kPointerSize));
PushReturnAddressFrom(rcx);
// Drop everything up to and including the arguments and the receiver
// from the caller stack.
leap(rsp, Operand(r15, 1 * kPointerSize));
PushReturnAddressFrom(rcx);
} else {
// Otherwise just leave the exit frame.
leave();
}
LeaveExitFrameEpilogue(true);
}

View File

@ -342,8 +342,8 @@ class MacroAssembler: public Assembler {
// Leave the current exit frame. Expects/provides the return value in
// register rax:rdx (untouched) and the pointer to the first
// argument in register rsi.
void LeaveExitFrame(bool save_doubles = false);
// argument in register rsi (if pop_arguments == true).
void LeaveExitFrame(bool save_doubles = false, bool pop_arguments = true);
// Leave the current exit frame. Expects/provides the return value in
// register rax (untouched).

View File

@ -27,6 +27,7 @@ class BytecodeGeneratorHelper {
i::FLAG_ignition = true;
i::FLAG_ignition_filter = StrDup(kFunctionName);
i::FLAG_always_opt = false;
i::FLAG_allow_natives_syntax = true;
CcTest::i_isolate()->interpreter()->Initialize();
}
@ -64,6 +65,15 @@ class BytecodeGeneratorHelper {
#define U8(x) static_cast<uint8_t>((x) & 0xff)
#define R(x) static_cast<uint8_t>(-(x) & 0xff)
#define _ static_cast<uint8_t>(0x5a)
#if defined(V8_TARGET_LITTLE_ENDIAN)
#define U16(x) static_cast<uint8_t>((x) & 0xff), \
static_cast<uint8_t>(((x) >> kBitsPerByte) & 0xff)
#elif defined(V8_TARGET_BIG_ENDIAN)
#define U16(x) static_cast<uint8_t>(((x) >> kBitsPerByte) & 0xff), \
static_cast<uint8_t>((x) & 0xff)
#else
#error Unknown byte ordering
#endif
// Structure for containing expected bytecode snippets.
@ -684,20 +694,26 @@ TEST(LoadGlobal) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = {
{"var a = 1;\nfunction f() { return a; }\nf()",
0, 1, 3,
{
B(LdaGlobal), _,
B(Return)
},
ExpectedSnippet<int> snippets[] = {
{
"var a = 1;\nfunction f() { return a; }\nf()",
0,
1,
3,
{
B(LdaGlobal), _, //
B(Return) //
},
},
{"function t() { }\nfunction f() { return t; }\nf()",
0, 1, 3,
{
B(LdaGlobal), _,
B(Return)
},
{
"function t() { }\nfunction f() { return t; }\nf()",
0,
1,
3,
{
B(LdaGlobal), _, //
B(Return) //
},
},
};
@ -713,34 +729,93 @@ TEST(CallGlobal) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<const char*> snippets[] = {
{"function t() { }\nfunction f() { return t(); }\nf()",
2 * kPointerSize, 1, 12,
{
B(LdaUndefined),
B(Star), R(1),
B(LdaGlobal), _,
B(Star), R(0),
B(Call), R(0), R(1), U8(0),
B(Return)
},
ExpectedSnippet<int> snippets[] = {
{
"function t() { }\nfunction f() { return t(); }\nf()",
2 * kPointerSize,
1,
12,
{
B(LdaUndefined), //
B(Star), R(1), //
B(LdaGlobal), _, //
B(Star), R(0), //
B(Call), R(0), R(1), U8(0), //
B(Return) //
},
},
{"function t(a, b, c) { }\nfunction f() { return t(1, 2, 3); }\nf()",
5 * kPointerSize, 1, 24,
{
B(LdaUndefined),
B(Star), R(1),
B(LdaGlobal), _,
B(Star), R(0),
B(LdaSmi8), U8(1),
B(Star), R(2),
B(LdaSmi8), U8(2),
B(Star), R(3),
B(LdaSmi8), U8(3),
B(Star), R(4),
B(Call), R(0), R(1), U8(3),
B(Return)
},
{
"function t(a, b, c) { }\nfunction f() { return t(1, 2, 3); }\nf()",
5 * kPointerSize,
1,
24,
{
B(LdaUndefined), //
B(Star), R(1), //
B(LdaGlobal), _, //
B(Star), R(0), //
B(LdaSmi8), U8(1), //
B(Star), R(2), //
B(LdaSmi8), U8(2), //
B(Star), R(3), //
B(LdaSmi8), U8(3), //
B(Star), R(4), //
B(Call), R(0), R(1), U8(3), //
B(Return) //
},
},
};
size_t num_snippets = sizeof(snippets) / sizeof(snippets[0]);
for (size_t i = 0; i < num_snippets; i++) {
Handle<BytecodeArray> bytecode_array =
helper.MakeBytecode(snippets[i].code_snippet, "f");
CheckBytecodeArrayEqual(snippets[i], bytecode_array, true);
}
}
TEST(CallRuntime) {
InitializedHandleScope handle_scope;
BytecodeGeneratorHelper helper;
ExpectedSnippet<int> snippets[] = {
{
"function f() { %TheHole() }\nf()",
1 * kPointerSize,
1,
7,
{
B(CallRuntime), U16(Runtime::kTheHole), R(0), U8(0), //
B(LdaUndefined), //
B(Return) //
},
},
{
"function f(a) { return %IsArray(a) }\nf(undefined)",
1 * kPointerSize,
2,
10,
{
B(Ldar), R(helper.kLastParamIndex), //
B(Star), R(0), //
B(CallRuntime), U16(Runtime::kIsArray), R(0), U8(1), //
B(Return) //
},
},
{
"function f() { return %Add(1, 2) }\nf()",
2 * kPointerSize,
1,
14,
{
B(LdaSmi8), U8(1), //
B(Star), R(0), //
B(LdaSmi8), U8(2), //
B(Star), R(1), //
B(CallRuntime), U16(Runtime::kAdd), R(0), U8(2), //
B(Return) //
},
},
};

View File

@ -1289,3 +1289,25 @@ TEST(InterpreterTestIn) {
CHECK_EQ(return_value->BooleanValue(), expected_value);
}
}
TEST(InterpreterCallRuntime) {
HandleAndZoneScope handles;
BytecodeArrayBuilder builder(handles.main_isolate(), handles.main_zone());
builder.set_locals_count(2);
builder.set_parameter_count(1);
builder.LoadLiteral(Smi::FromInt(15))
.StoreAccumulatorInRegister(Register(0))
.LoadLiteral(Smi::FromInt(40))
.StoreAccumulatorInRegister(Register(1))
.CallRuntime(Runtime::kAdd, Register(0), 2)
.Return();
Handle<BytecodeArray> bytecode_array = builder.ToBytecodeArray();
InterpreterTester tester(handles.main_isolate(), bytecode_array);
auto callable = tester.GetCallable<>();
Handle<Object> return_val = callable().ToHandleChecked();
CHECK_EQ(Smi::cast(*return_val), Smi::FromInt(55));
}

View File

@ -503,7 +503,7 @@ TARGET_TEST_F(InterpreterAssemblerTest, LoadObjectField) {
}
TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime) {
TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime2) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Node* arg1 = m.Int32Constant(2);
@ -516,6 +516,33 @@ TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime) {
}
TARGET_TEST_F(InterpreterAssemblerTest, CallRuntime) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
Callable builtin = CodeFactory::InterpreterCEntry(isolate());
Node* function_id = m.Int32Constant(0);
Node* first_arg = m.Int32Constant(1);
Node* arg_count = m.Int32Constant(2);
Matcher<Node*> function_table = IsExternalConstant(
ExternalReference::runtime_function_table_address(isolate()));
Matcher<Node*> function = IsIntPtrAdd(
function_table,
IsInt32Mul(function_id, IsInt32Constant(sizeof(Runtime::Function))));
Matcher<Node*> function_entry =
m.IsLoad(kMachPtr, function,
IsInt32Constant(offsetof(Runtime::Function, entry)));
Node* call_runtime = m.CallRuntime(function_id, first_arg, arg_count);
EXPECT_THAT(call_runtime,
m.IsCall(_, IsHeapConstant(builtin.code()), arg_count,
first_arg, function_entry,
IsParameter(Linkage::kInterpreterContextParameter)));
}
}
TARGET_TEST_F(InterpreterAssemblerTest, CallIC) {
TRACED_FOREACH(interpreter::Bytecode, bytecode, kBytecodes) {
InterpreterAssemblerForTest m(this, bytecode);
@ -536,7 +563,7 @@ 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());
Callable builtin = CodeFactory::InterpreterPushArgsAndCall(isolate());
Node* function = m.Int32Constant(0);
Node* first_arg = m.Int32Constant(1);
Node* arg_count = m.Int32Constant(2);

View File

@ -51,6 +51,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Call operations.
builder.Call(reg, reg, 0);
builder.CallRuntime(Runtime::kIsArray, reg, 1);
// Emit binary operator invocations.
builder.BinaryOperation(Token::Value::ADD, reg)

View File

@ -37,9 +37,6 @@ TEST_F(BytecodeArrayIteratorTest, IteratesBytecodeArray) {
Register reg_2 = Register::FromParameterIndex(2, builder.parameter_count());
int feedback_slot = 97;
// TODO(rmcilroy): Add a test for a bytecode with a short operand when
// the CallRuntime bytecode is landed.
builder.LoadLiteral(heap_num_0)
.LoadLiteral(heap_num_1)
.LoadLiteral(zero)
@ -48,6 +45,7 @@ TEST_F(BytecodeArrayIteratorTest, IteratesBytecodeArray) {
.LoadAccumulatorWithRegister(reg_0)
.LoadNamedProperty(reg_1, feedback_slot, LanguageMode::SLOPPY)
.StoreAccumulatorInRegister(reg_2)
.CallRuntime(Runtime::kLoadIC_Miss, reg_0, 1)
.Return();
// Test iterator sees the expected output from the builder.
@ -92,6 +90,14 @@ TEST_F(BytecodeArrayIteratorTest, IteratesBytecodeArray) {
CHECK(!iterator.done());
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kCallRuntime);
CHECK_EQ(static_cast<Runtime::FunctionId>(iterator.GetIndexOperand(0)),
Runtime::kLoadIC_Miss);
CHECK_EQ(iterator.GetRegisterOperand(1).index(), reg_0.index());
CHECK_EQ(iterator.GetCountOperand(2), 1);
CHECK(!iterator.done());
iterator.Advance();
CHECK_EQ(iterator.current_bytecode(), Bytecode::kReturn);
CHECK(!iterator.done());
iterator.Advance();