Commit a58ba8d8 authored by Benedikt Meurer's avatar Benedikt Meurer

[turbofan] Add basic support for calling to (a subset of) C functions.

This introduces some initial building blocks for calling out to
C/C++ functions directly from TurboFan generated code objects.

R=svenpanne@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#29279}
parent 112f1973
...@@ -373,6 +373,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -373,6 +373,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
DCHECK_EQ(LeaveCC, i.OutputSBit()); DCHECK_EQ(LeaveCC, i.OutputSBit());
break; break;
} }
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);
break;
}
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters);
}
break;
}
case kArchJmp: case kArchJmp:
AssembleArchJump(i.InputRpo(0)); AssembleArchJump(i.InputRpo(0));
DCHECK_EQ(LeaveCC, i.OutputSBit()); DCHECK_EQ(LeaveCC, i.OutputSBit());
...@@ -806,6 +822,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -806,6 +822,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Push(i.InputRegister(0)); __ Push(i.InputRegister(0));
DCHECK_EQ(LeaveCC, i.OutputSBit()); DCHECK_EQ(LeaveCC, i.OutputSBit());
break; break;
case kArmPoke: {
int const slot = MiscField::decode(instr->opcode());
__ str(i.InputRegister(0), MemOperand(sp, slot * kPointerSize));
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArmStoreWriteBarrier: { case kArmStoreWriteBarrier: {
Register object = i.InputRegister(0); Register object = i.InputRegister(0);
Register index = i.InputRegister(1); Register index = i.InputRegister(1);
......
...@@ -93,6 +93,7 @@ namespace compiler { ...@@ -93,6 +93,7 @@ namespace compiler {
V(ArmLdr) \ V(ArmLdr) \
V(ArmStr) \ V(ArmStr) \
V(ArmPush) \ V(ArmPush) \
V(ArmPoke) \
V(ArmStoreWriteBarrier) V(ArmStoreWriteBarrier)
......
...@@ -1096,12 +1096,28 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1096,12 +1096,28 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// TODO(turbofan): on ARM it's probably better to use the code object in a // TODO(turbofan): on ARM it's probably better to use the code object in a
// register if there are multiple uses of it. Improve constant pool and the // register if there are multiple uses of it. Improve constant pool and the
// heuristics in the register allocator for where to emit constants. // heuristics in the register allocator for where to emit constants.
InitializeCallBuffer(node, &buffer, true, false); InitializeCallBuffer(node, &buffer, true, true);
// Prepare for C function call.
if (descriptor->IsCFunctionCall()) {
Emit(kArchPrepareCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount())),
0, nullptr, 0, nullptr);
// Poke any stack arguments.
for (size_t n = 0; n < buffer.pushed_nodes.size(); ++n) {
if (Node* node = buffer.pushed_nodes[n]) {
int const slot = static_cast<int>(n);
InstructionOperand value = g.UseRegister(node);
Emit(kArmPoke | MiscField::encode(slot), g.NoOutput(), value);
}
}
} else {
// Push any stack arguments. // Push any stack arguments.
for (Node* node : base::Reversed(buffer.pushed_nodes)) { for (Node* node : base::Reversed(buffer.pushed_nodes)) {
Emit(kArmPush, g.NoOutput(), g.UseRegister(node)); Emit(kArmPush, g.NoOutput(), g.UseRegister(node));
} }
}
// Pass label of exception handler block. // Pass label of exception handler block.
CallDescriptor::Flags flags = descriptor->flags(); CallDescriptor::Flags flags = descriptor->flags();
...@@ -1118,18 +1134,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1118,18 +1134,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
switch (descriptor->kind()) { switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject: { case CallDescriptor::kCallAddress:
opcode = kArchCallCodeObject; opcode =
kArchCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
break;
case CallDescriptor::kCallCodeObject:
opcode = kArchCallCodeObject | MiscField::encode(flags);
break; break;
}
case CallDescriptor::kCallJSFunction: case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction; opcode = kArchCallJSFunction | MiscField::encode(flags);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return; return;
} }
opcode |= MiscField::encode(flags);
// Emit the call instruction. // Emit the call instruction.
size_t const output_count = buffer.outputs.size(); size_t const output_count = buffer.outputs.size();
......
...@@ -28,6 +28,7 @@ struct ArmLinkageHelperTraits { ...@@ -28,6 +28,7 @@ struct ArmLinkageHelperTraits {
return register_parameters[i]; return register_parameters[i];
} }
static int CRegisterParametersLength() { return 4; } static int CRegisterParametersLength() { return 4; }
static int CStackBackingStoreLength() { return 0; }
}; };
......
...@@ -420,6 +420,23 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -420,6 +420,23 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Jump(x10); __ Jump(x10);
break; break;
} }
case kArchPrepareCallCFunction:
// We don't need kArchPrepareCallCFunction on arm64 as the instruction
// selector already perform a Claim to reserve space on the stack and
// guarantee correct alignment of stack pointer.
UNREACHABLE();
break;
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters, 0);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters, 0);
}
break;
}
case kArchJmp: case kArchJmp:
AssembleArchJump(i.InputRpo(0)); AssembleArchJump(i.InputRpo(0));
break; break;
......
...@@ -1345,7 +1345,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1345,7 +1345,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// TODO(turbofan): on ARM64 it's probably better to use the code object in a // TODO(turbofan): on ARM64 it's probably better to use the code object in a
// register if there are multiple uses of it. Improve constant pool and the // register if there are multiple uses of it. Improve constant pool and the
// heuristics in the register allocator for where to emit constants. // heuristics in the register allocator for where to emit constants.
InitializeCallBuffer(node, &buffer, true, false); InitializeCallBuffer(node, &buffer, true, true);
// Push the arguments to the stack. // Push the arguments to the stack.
int aligned_push_count = static_cast<int>(buffer.pushed_nodes.size()); int aligned_push_count = static_cast<int>(buffer.pushed_nodes.size());
...@@ -1392,18 +1392,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1392,18 +1392,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
switch (descriptor->kind()) { switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject: { case CallDescriptor::kCallAddress:
opcode = kArchCallCodeObject; opcode =
kArchCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
break;
case CallDescriptor::kCallCodeObject:
opcode = kArchCallCodeObject | MiscField::encode(flags);
break; break;
}
case CallDescriptor::kCallJSFunction: case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction; opcode = kArchCallJSFunction | MiscField::encode(flags);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return; return;
} }
opcode |= MiscField::encode(flags);
// Emit the call instruction. // Emit the call instruction.
size_t const output_count = buffer.outputs.size(); size_t const output_count = buffer.outputs.size();
......
...@@ -28,6 +28,7 @@ struct Arm64LinkageHelperTraits { ...@@ -28,6 +28,7 @@ struct Arm64LinkageHelperTraits {
return register_parameters[i]; return register_parameters[i];
} }
static int CRegisterParametersLength() { return 8; } static int CRegisterParametersLength() { return 8; }
static int CStackBackingStoreLength() { return 0; }
}; };
......
...@@ -57,6 +57,10 @@ class InstructionOperandConverter { ...@@ -57,6 +57,10 @@ class InstructionOperandConverter {
return static_cast<uint8_t>(InputInt32(index) & 0x3F); return static_cast<uint8_t>(InputInt32(index) & 0x3F);
} }
ExternalReference InputExternalReference(size_t index) {
return ToExternalReference(instr_->InputAt(index));
}
Handle<HeapObject> InputHeapObject(size_t index) { Handle<HeapObject> InputHeapObject(size_t index) {
return ToHeapObject(instr_->InputAt(index)); return ToHeapObject(instr_->InputAt(index));
} }
...@@ -108,6 +112,10 @@ class InstructionOperandConverter { ...@@ -108,6 +112,10 @@ class InstructionOperandConverter {
double ToDouble(InstructionOperand* op) { return ToConstant(op).ToFloat64(); } double ToDouble(InstructionOperand* op) { return ToConstant(op).ToFloat64(); }
ExternalReference ToExternalReference(InstructionOperand* op) {
return ToConstant(op).ToExternalReference();
}
Handle<HeapObject> ToHeapObject(InstructionOperand* op) { Handle<HeapObject> ToHeapObject(InstructionOperand* op) {
return ToConstant(op).ToHeapObject(); return ToConstant(op).ToHeapObject();
} }
......
...@@ -62,6 +62,11 @@ CodeGenerator::CodeGenerator(Frame* frame, Linkage* linkage, ...@@ -62,6 +62,11 @@ CodeGenerator::CodeGenerator(Frame* frame, Linkage* linkage,
Handle<Code> CodeGenerator::GenerateCode() { Handle<Code> CodeGenerator::GenerateCode() {
CompilationInfo* info = this->info(); CompilationInfo* info = this->info();
// Open a frame scope to indicate that there is a frame on the stack. The
// MANUAL indicates that the scope shouldn't actually generate code to set up
// the frame (that is done in AssemblePrologue).
FrameScope frame_scope(masm(), StackFrame::MANUAL);
// Emit a code line info recording start event. // Emit a code line info recording start event.
PositionsRecorder* recorder = masm()->positions_recorder(); PositionsRecorder* recorder = masm()->positions_recorder();
LOG_CODE_EVENT(isolate(), CodeStartLinePosInfoRecordEvent(recorder)); LOG_CODE_EVENT(isolate(), CodeStartLinePosInfoRecordEvent(recorder));
......
...@@ -352,6 +352,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -352,6 +352,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset)); __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break; break;
} }
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, i.TempRegister(0));
break;
}
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (HasImmediateInput(instr, 0)) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters);
}
break;
}
case kArchJmp: case kArchJmp:
AssembleArchJump(i.InputRpo(0)); AssembleArchJump(i.InputRpo(0));
break; break;
...@@ -870,6 +886,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -870,6 +886,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ push(i.InputOperand(0)); __ push(i.InputOperand(0));
} }
break; break;
case kIA32Poke: {
int const slot = MiscField::decode(instr->opcode());
if (HasImmediateInput(instr, 0)) {
__ mov(Operand(esp, slot * kPointerSize), i.InputImmediate(0));
} else {
__ mov(Operand(esp, slot * kPointerSize), i.InputRegister(0));
}
break;
}
case kIA32StoreWriteBarrier: { case kIA32StoreWriteBarrier: {
Register object = i.InputRegister(0); Register object = i.InputRegister(0);
Register value = i.InputRegister(2); Register value = i.InputRegister(2);
......
...@@ -91,6 +91,7 @@ namespace compiler { ...@@ -91,6 +91,7 @@ namespace compiler {
V(IA32Movsd) \ V(IA32Movsd) \
V(IA32Lea) \ V(IA32Lea) \
V(IA32Push) \ V(IA32Push) \
V(IA32Poke) \
V(IA32StoreWriteBarrier) \ V(IA32StoreWriteBarrier) \
V(IA32StackCheck) V(IA32StackCheck)
......
...@@ -830,6 +830,24 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -830,6 +830,24 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(node, &buffer, true, true); InitializeCallBuffer(node, &buffer, true, true);
// Prepare for C function call.
if (descriptor->IsCFunctionCall()) {
InstructionOperand temps[] = {g.TempRegister()};
size_t const temp_count = arraysize(temps);
Emit(kArchPrepareCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount())),
0, nullptr, 0, nullptr, temp_count, temps);
// Poke any stack arguments.
for (size_t n = 0; n < buffer.pushed_nodes.size(); ++n) {
if (Node* node = buffer.pushed_nodes[n]) {
int const slot = static_cast<int>(n);
InstructionOperand value =
g.CanBeImmediate(node) ? g.UseImmediate(node) : g.UseRegister(node);
Emit(kIA32Poke | MiscField::encode(slot), g.NoOutput(), value);
}
}
} else {
// Push any stack arguments. // Push any stack arguments.
for (Node* node : base::Reversed(buffer.pushed_nodes)) { for (Node* node : base::Reversed(buffer.pushed_nodes)) {
// TODO(titzer): handle pushing double parameters. // TODO(titzer): handle pushing double parameters.
...@@ -839,6 +857,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -839,6 +857,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
: IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node); : IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node);
Emit(kIA32Push, g.NoOutput(), value); Emit(kIA32Push, g.NoOutput(), value);
} }
}
// Pass label of exception handler block. // Pass label of exception handler block.
CallDescriptor::Flags flags = descriptor->flags(); CallDescriptor::Flags flags = descriptor->flags();
...@@ -855,18 +874,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -855,18 +874,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
switch (descriptor->kind()) { switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject: { case CallDescriptor::kCallAddress:
opcode = kArchCallCodeObject; opcode =
kArchCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
break;
case CallDescriptor::kCallCodeObject:
opcode = kArchCallCodeObject | MiscField::encode(flags);
break; break;
}
case CallDescriptor::kCallJSFunction: case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction; opcode = kArchCallJSFunction | MiscField::encode(flags);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return; return;
} }
opcode |= MiscField::encode(flags);
// Emit the call instruction. // Emit the call instruction.
size_t const output_count = buffer.outputs.size(); size_t const output_count = buffer.outputs.size();
......
...@@ -24,6 +24,7 @@ struct IA32LinkageHelperTraits { ...@@ -24,6 +24,7 @@ struct IA32LinkageHelperTraits {
} }
static Register CRegisterParameter(int i) { return no_reg; } static Register CRegisterParameter(int i) { return no_reg; }
static int CRegisterParametersLength() { return 0; } static int CRegisterParametersLength() { return 0; }
static int CStackBackingStoreLength() { return 0; }
}; };
typedef LinkageHelper<IA32LinkageHelperTraits> LH; typedef LinkageHelper<IA32LinkageHelperTraits> LH;
......
...@@ -40,6 +40,8 @@ namespace compiler { ...@@ -40,6 +40,8 @@ namespace compiler {
V(ArchTailCallCodeObject) \ V(ArchTailCallCodeObject) \
V(ArchCallJSFunction) \ V(ArchCallJSFunction) \
V(ArchTailCallJSFunction) \ V(ArchTailCallJSFunction) \
V(ArchPrepareCallCFunction) \
V(ArchCallCFunction) \
V(ArchJmp) \ V(ArchJmp) \
V(ArchLookupSwitch) \ V(ArchLookupSwitch) \
V(ArchTableSwitch) \ V(ArchTableSwitch) \
......
...@@ -336,8 +336,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -336,8 +336,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
case CallDescriptor::kCallAddress: case CallDescriptor::kCallAddress:
buffer->instruction_args.push_back( buffer->instruction_args.push_back(
(call_address_immediate && (call_address_immediate &&
(callee->opcode() == IrOpcode::kInt32Constant || callee->opcode() == IrOpcode::kExternalConstant)
callee->opcode() == IrOpcode::kInt64Constant))
? g.UseImmediate(callee) ? g.UseImmediate(callee)
: g.UseRegister(callee)); : g.UseRegister(callee));
break; break;
...@@ -373,7 +372,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -373,7 +372,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
// not appear as arguments to the call. Everything else ends up // not appear as arguments to the call. Everything else ends up
// as an InstructionOperand argument to the call. // as an InstructionOperand argument to the call.
auto iter(call->inputs().begin()); auto iter(call->inputs().begin());
int pushed_count = 0; size_t pushed_count = 0;
for (size_t index = 0; index < input_count; ++iter, ++index) { for (size_t index = 0; index < input_count; ++iter, ++index) {
DCHECK(iter != call->inputs().end()); DCHECK(iter != call->inputs().end());
DCHECK((*iter)->op()->opcode() != IrOpcode::kFrameState); DCHECK((*iter)->op()->opcode() != IrOpcode::kFrameState);
...@@ -393,10 +392,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer, ...@@ -393,10 +392,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
buffer->instruction_args.push_back(op); buffer->instruction_args.push_back(op);
} }
} }
CHECK_EQ(pushed_count, static_cast<int>(buffer->pushed_nodes.size())); DCHECK_EQ(input_count, buffer->instruction_args.size() + pushed_count -
DCHECK(static_cast<size_t>(input_count) == buffer->frame_state_value_count());
(buffer->instruction_args.size() + buffer->pushed_nodes.size() -
buffer->frame_state_value_count()));
} }
......
...@@ -204,11 +204,13 @@ class LinkageHelper { ...@@ -204,11 +204,13 @@ class LinkageHelper {
// Add register and/or stack parameter(s). // Add register and/or stack parameter(s).
const int parameter_count = static_cast<int>(msig->parameter_count()); const int parameter_count = static_cast<int>(msig->parameter_count());
int stack_offset = LinkageTraits::CStackBackingStoreLength();
for (int i = 0; i < parameter_count; i++) { for (int i = 0; i < parameter_count; i++) {
if (i < LinkageTraits::CRegisterParametersLength()) { if (i < LinkageTraits::CRegisterParametersLength()) {
locations.AddParam(regloc(LinkageTraits::CRegisterParameter(i))); locations.AddParam(regloc(LinkageTraits::CRegisterParameter(i)));
} else { } else {
locations.AddParam(stackloc(-1 - i)); locations.AddParam(stackloc(-1 - stack_offset));
stack_offset++;
} }
} }
......
...@@ -102,12 +102,18 @@ class CallDescriptor final : public ZoneObject { ...@@ -102,12 +102,18 @@ class CallDescriptor final : public ZoneObject {
// Returns the kind of this call. // Returns the kind of this call.
Kind kind() const { return kind_; } Kind kind() const { return kind_; }
// Returns {true} if this descriptor is a call to a C function.
bool IsCFunctionCall() const { return kind_ == kCallAddress; }
// Returns {true} if this descriptor is a call to a JSFunction. // Returns {true} if this descriptor is a call to a JSFunction.
bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; } bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; }
// The number of return values from this call. // The number of return values from this call.
size_t ReturnCount() const { return machine_sig_->return_count(); } size_t ReturnCount() const { return machine_sig_->return_count(); }
// The number of C parameters to this call.
size_t CParameterCount() const { return machine_sig_->parameter_count(); }
// The number of JavaScript parameters to this call, including the receiver // The number of JavaScript parameters to this call, including the receiver
// object. // object.
size_t JSParameterCount() const { return js_param_count_; } size_t JSParameterCount() const { return js_param_count_; }
......
...@@ -463,6 +463,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -463,6 +463,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Jump(at); __ Jump(at);
break; break;
} }
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);
break;
}
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters);
}
break;
}
case kArchJmp: case kArchJmp:
AssembleArchJump(i.InputRpo(0)); AssembleArchJump(i.InputRpo(0));
break; break;
......
...@@ -524,7 +524,22 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -524,7 +524,22 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
CallBuffer buffer(zone(), descriptor, frame_state_descriptor); CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(node, &buffer, true, false); InitializeCallBuffer(node, &buffer, true, true);
// Prepare for C function call.
if (descriptor->IsCFunctionCall()) {
Emit(kArchPrepareCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount())),
0, nullptr, 0, nullptr);
// Poke any stack arguments.
int slot = kCArgSlotCount;
for (Node* node : buffer.pushed_nodes) {
Emit(kMipsStoreToStackSlot, g.NoOutput(), g.UseRegister(node),
g.TempImmediate(slot << kPointerSizeLog2));
++slot;
}
} else {
// Possibly align stack here for functions. // Possibly align stack here for functions.
int push_count = buffer.pushed_nodes.size(); int push_count = buffer.pushed_nodes.size();
if (push_count > 0) { if (push_count > 0) {
...@@ -537,6 +552,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -537,6 +552,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
g.TempImmediate(slot << kPointerSizeLog2)); g.TempImmediate(slot << kPointerSizeLog2));
slot--; slot--;
} }
}
// Pass label of exception handler block. // Pass label of exception handler block.
CallDescriptor::Flags flags = descriptor->flags(); CallDescriptor::Flags flags = descriptor->flags();
...@@ -553,18 +569,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -553,18 +569,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
switch (descriptor->kind()) { switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject: { case CallDescriptor::kCallAddress:
opcode = kArchCallCodeObject; opcode =
kArchCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
break;
case CallDescriptor::kCallCodeObject:
opcode = kArchCallCodeObject | MiscField::encode(flags);
break; break;
}
case CallDescriptor::kCallJSFunction: case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction; opcode = kArchCallJSFunction | MiscField::encode(flags);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return; return;
} }
opcode |= MiscField::encode(flags);
// Emit the call instruction. // Emit the call instruction.
size_t const output_count = buffer.outputs.size(); size_t const output_count = buffer.outputs.size();
......
...@@ -28,6 +28,7 @@ struct MipsLinkageHelperTraits { ...@@ -28,6 +28,7 @@ struct MipsLinkageHelperTraits {
return register_parameters[i]; return register_parameters[i];
} }
static int CRegisterParametersLength() { return 4; } static int CRegisterParametersLength() { return 4; }
static int CStackBackingStoreLength() { return 0; }
}; };
......
...@@ -463,6 +463,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -463,6 +463,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Jump(at); __ Jump(at);
break; break;
} }
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);
break;
}
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (instr->InputAt(0)->IsImmediate()) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters);
}
break;
}
case kArchJmp: case kArchJmp:
AssembleArchJump(i.InputRpo(0)); AssembleArchJump(i.InputRpo(0));
break; break;
......
...@@ -673,8 +673,22 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -673,8 +673,22 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
CallBuffer buffer(zone(), descriptor, frame_state_descriptor); CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(node, &buffer, true, false); InitializeCallBuffer(node, &buffer, true, true);
// Prepare for C function call.
if (descriptor->IsCFunctionCall()) {
Emit(kArchPrepareCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount())),
0, nullptr, 0, nullptr);
// Poke any stack arguments.
int slot = kCArgSlotCount;
for (Node* node : buffer.pushed_nodes) {
Emit(kMips64StoreToStackSlot, g.NoOutput(), g.UseRegister(node),
g.TempImmediate(slot << kPointerSizeLog2));
++slot;
}
} else {
const int32_t push_count = static_cast<int32_t>(buffer.pushed_nodes.size()); const int32_t push_count = static_cast<int32_t>(buffer.pushed_nodes.size());
if (push_count > 0) { if (push_count > 0) {
Emit(kMips64StackClaim, g.NoOutput(), Emit(kMips64StackClaim, g.NoOutput(),
...@@ -686,6 +700,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -686,6 +700,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
g.TempImmediate(slot << kPointerSizeLog2)); g.TempImmediate(slot << kPointerSizeLog2));
slot--; slot--;
} }
}
// Pass label of exception handler block. // Pass label of exception handler block.
CallDescriptor::Flags flags = descriptor->flags(); CallDescriptor::Flags flags = descriptor->flags();
...@@ -702,12 +717,16 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -702,12 +717,16 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
switch (descriptor->kind()) { switch (descriptor->kind()) {
case CallDescriptor::kCallCodeObject: { case CallDescriptor::kCallAddress:
opcode = kArchCallCodeObject; opcode =
kArchCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
break;
case CallDescriptor::kCallCodeObject:
opcode = kArchCallCodeObject | MiscField::encode(flags);
break; break;
}
case CallDescriptor::kCallJSFunction: case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction; opcode = kArchCallJSFunction | MiscField::encode(flags);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
......
...@@ -28,6 +28,7 @@ struct MipsLinkageHelperTraits { ...@@ -28,6 +28,7 @@ struct MipsLinkageHelperTraits {
return register_parameters[i]; return register_parameters[i];
} }
static int CRegisterParametersLength() { return 8; } static int CRegisterParametersLength() { return 8; }
static int CStackBackingStoreLength() { return 0; }
}; };
......
...@@ -602,6 +602,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -602,6 +602,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset)); __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break; break;
} }
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters);
break;
}
case kArchCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
if (HasImmediateInput(instr, 0)) {
ExternalReference ref = i.InputExternalReference(0);
__ CallCFunction(ref, num_parameters);
} else {
Register func = i.InputRegister(0);
__ CallCFunction(func, num_parameters);
}
break;
}
case kArchJmp: case kArchJmp:
AssembleArchJump(i.InputRpo(0)); AssembleArchJump(i.InputRpo(0));
break; break;
...@@ -1215,6 +1231,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -1215,6 +1231,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} }
} }
break; break;
case kX64Poke: {
int const slot = MiscField::decode(instr->opcode());
if (HasImmediateInput(instr, 0)) {
__ movq(Operand(rsp, slot * kPointerSize), i.InputImmediate(0));
} else {
__ movq(Operand(rsp, slot * kPointerSize), i.InputRegister(0));
}
break;
}
case kX64StoreWriteBarrier: { case kX64StoreWriteBarrier: {
Register object = i.InputRegister(0); Register object = i.InputRegister(0);
Register value = i.InputRegister(2); Register value = i.InputRegister(2);
......
...@@ -114,6 +114,7 @@ namespace compiler { ...@@ -114,6 +114,7 @@ namespace compiler {
V(X64Dec32) \ V(X64Dec32) \
V(X64Inc32) \ V(X64Inc32) \
V(X64Push) \ V(X64Push) \
V(X64Poke) \
V(X64StoreWriteBarrier) \ V(X64StoreWriteBarrier) \
V(X64StackCheck) V(X64StackCheck)
......
...@@ -1038,6 +1038,22 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1038,6 +1038,22 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(node, &buffer, true, true); InitializeCallBuffer(node, &buffer, true, true);
// Prepare for C function call.
if (descriptor->IsCFunctionCall()) {
Emit(kArchPrepareCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount())),
0, nullptr, 0, nullptr);
// Poke any stack arguments.
for (size_t n = 0; n < buffer.pushed_nodes.size(); ++n) {
if (Node* node = buffer.pushed_nodes[n]) {
int const slot = static_cast<int>(n);
InstructionOperand value =
g.CanBeImmediate(node) ? g.UseImmediate(node) : g.UseRegister(node);
Emit(kX64Poke | MiscField::encode(slot), g.NoOutput(), value);
}
}
} else {
// Push any stack arguments. // Push any stack arguments.
for (Node* node : base::Reversed(buffer.pushed_nodes)) { for (Node* node : base::Reversed(buffer.pushed_nodes)) {
// TODO(titzer): handle pushing double parameters. // TODO(titzer): handle pushing double parameters.
...@@ -1047,6 +1063,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1047,6 +1063,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
: IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node); : IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node);
Emit(kX64Push, g.NoOutput(), value); Emit(kX64Push, g.NoOutput(), value);
} }
}
// Pass label of exception handler block. // Pass label of exception handler block.
CallDescriptor::Flags flags = descriptor->flags(); CallDescriptor::Flags flags = descriptor->flags();
...@@ -1063,17 +1080,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) { ...@@ -1063,17 +1080,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
// Select the appropriate opcode based on the call type. // Select the appropriate opcode based on the call type.
InstructionCode opcode; InstructionCode opcode;
switch (descriptor->kind()) { switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
kArchCallCFunction |
MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
break;
case CallDescriptor::kCallCodeObject: case CallDescriptor::kCallCodeObject:
opcode = kArchCallCodeObject; opcode = kArchCallCodeObject | MiscField::encode(flags);
break; break;
case CallDescriptor::kCallJSFunction: case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction; opcode = kArchCallJSFunction | MiscField::encode(flags);
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
return; return;
} }
opcode |= MiscField::encode(flags);
// Emit the call instruction. // Emit the call instruction.
size_t const output_count = buffer.outputs.size(); size_t const output_count = buffer.outputs.size();
......
...@@ -43,6 +43,7 @@ struct X64LinkageHelperTraits { ...@@ -43,6 +43,7 @@ struct X64LinkageHelperTraits {
} }
} }
static int CRegisterParametersLength() { return kWin64 ? 4 : 6; } static int CRegisterParametersLength() { return kWin64 ? 4 : 6; }
static int CStackBackingStoreLength() { return kWin64 ? 4 : 0; }
}; };
typedef LinkageHelper<X64LinkageHelperTraits> LH; typedef LinkageHelper<X64LinkageHelperTraits> LH;
......
...@@ -5138,4 +5138,83 @@ TEST(RunFloat64RoundTiesAway) { ...@@ -5138,4 +5138,83 @@ TEST(RunFloat64RoundTiesAway) {
} }
} }
#if !USE_SIMULATOR
namespace {
int32_t const kMagicFoo0 = 0xdeadbeef;
int32_t foo0() { return kMagicFoo0; }
int32_t foo1(int32_t x) { return x; }
int32_t foo2(int32_t x, int32_t y) { return x - y; }
int32_t foo8(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, int32_t f,
int32_t g, int32_t h) {
return a + b + c + d + e + f + g + h;
}
} // namespace
TEST(RunCallCFunction0) {
auto* foo0_ptr = &foo0;
RawMachineAssemblerTester<int32_t> m;
Node* function = m.LoadFromPointer(&foo0_ptr, kMachPtr);
m.Return(m.CallCFunction0(kMachInt32, function));
CHECK_EQ(kMagicFoo0, m.Call());
}
TEST(RunCallCFunction1) {
auto* foo1_ptr = &foo1;
RawMachineAssemblerTester<int32_t> m(kMachInt32);
Node* function = m.LoadFromPointer(&foo1_ptr, kMachPtr);
m.Return(m.CallCFunction1(kMachInt32, kMachInt32, function, m.Parameter(0)));
FOR_INT32_INPUTS(i) {
int32_t const expected = *i;
CHECK_EQ(expected, m.Call(expected));
}
}
TEST(RunCallCFunction2) {
auto* foo2_ptr = &foo2;
RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
Node* function = m.LoadFromPointer(&foo2_ptr, kMachPtr);
m.Return(m.CallCFunction2(kMachInt32, kMachInt32, kMachInt32, function,
m.Parameter(0), m.Parameter(1)));
FOR_INT32_INPUTS(i) {
int32_t const x = *i;
FOR_INT32_INPUTS(j) {
int32_t const y = *j;
CHECK_EQ(x - y, m.Call(x, y));
}
}
}
TEST(RunCallCFunction8) {
auto* foo8_ptr = &foo8;
RawMachineAssemblerTester<int32_t> m(kMachInt32);
Node* function = m.LoadFromPointer(&foo8_ptr, kMachPtr);
Node* param = m.Parameter(0);
m.Return(m.CallCFunction8(kMachInt32, kMachInt32, kMachInt32, kMachInt32,
kMachInt32, kMachInt32, kMachInt32, kMachInt32,
kMachInt32, function, param, param, param, param,
param, param, param, param));
FOR_INT32_INPUTS(i) {
int32_t const x = *i;
CHECK_EQ(x * 8, m.Call(x));
}
}
#endif // USE_SIMULATOR
#endif // V8_TURBOFAN_TARGET #endif // V8_TURBOFAN_TARGET
...@@ -144,6 +144,78 @@ Node* RawMachineAssembler::CallRuntime1(Runtime::FunctionId function, ...@@ -144,6 +144,78 @@ Node* RawMachineAssembler::CallRuntime1(Runtime::FunctionId function,
} }
Node* RawMachineAssembler::CallCFunction0(MachineType return_type,
Node* function) {
MachineSignature::Builder builder(zone(), 1, 0);
builder.AddReturn(return_type);
const CallDescriptor* descriptor =
Linkage::GetSimplifiedCDescriptor(zone(), builder.Build());
Node* call = graph()->NewNode(common()->Call(descriptor), function);
schedule()->AddNode(CurrentBlock(), call);
return call;
}
Node* RawMachineAssembler::CallCFunction1(MachineType return_type,
MachineType arg0_type, Node* function,
Node* arg0) {
MachineSignature::Builder builder(zone(), 1, 1);
builder.AddReturn(return_type);
builder.AddParam(arg0_type);
const CallDescriptor* descriptor =
Linkage::GetSimplifiedCDescriptor(zone(), builder.Build());
Node* call = graph()->NewNode(common()->Call(descriptor), function, arg0);
schedule()->AddNode(CurrentBlock(), call);
return call;
}
Node* RawMachineAssembler::CallCFunction2(MachineType return_type,
MachineType arg0_type,
MachineType arg1_type, Node* function,
Node* arg0, Node* arg1) {
MachineSignature::Builder builder(zone(), 1, 2);
builder.AddReturn(return_type);
builder.AddParam(arg0_type);
builder.AddParam(arg1_type);
const CallDescriptor* descriptor =
Linkage::GetSimplifiedCDescriptor(zone(), builder.Build());
Node* call =
graph()->NewNode(common()->Call(descriptor), function, arg0, arg1);
schedule()->AddNode(CurrentBlock(), call);
return call;
}
Node* RawMachineAssembler::CallCFunction8(
MachineType return_type, MachineType arg0_type, MachineType arg1_type,
MachineType arg2_type, MachineType arg3_type, MachineType arg4_type,
MachineType arg5_type, MachineType arg6_type, MachineType arg7_type,
Node* function, Node* arg0, Node* arg1, Node* arg2, Node* arg3, Node* arg4,
Node* arg5, Node* arg6, Node* arg7) {
MachineSignature::Builder builder(zone(), 1, 8);
builder.AddReturn(return_type);
builder.AddParam(arg0_type);
builder.AddParam(arg1_type);
builder.AddParam(arg2_type);
builder.AddParam(arg3_type);
builder.AddParam(arg4_type);
builder.AddParam(arg5_type);
builder.AddParam(arg6_type);
builder.AddParam(arg7_type);
const CallDescriptor* descriptor =
Linkage::GetSimplifiedCDescriptor(zone(), builder.Build());
Node* call = graph()->NewNode(common()->Call(descriptor), function, arg0,
arg1, arg2, arg3, arg4, arg5, arg6, arg7);
schedule()->AddNode(CurrentBlock(), call);
return call;
}
void RawMachineAssembler::Bind(Label* label) { void RawMachineAssembler::Bind(Label* label) {
DCHECK(current_block_ == NULL); DCHECK(current_block_ == NULL);
DCHECK(!label->bound_); DCHECK(!label->bound_);
......
...@@ -471,6 +471,23 @@ class RawMachineAssembler : public GraphBuilder { ...@@ -471,6 +471,23 @@ class RawMachineAssembler : public GraphBuilder {
// Call to a runtime function with zero parameters. // Call to a runtime function with zero parameters.
Node* CallRuntime1(Runtime::FunctionId function, Node* arg0, Node* context, Node* CallRuntime1(Runtime::FunctionId function, Node* arg0, Node* context,
Node* frame_state); Node* frame_state);
// Call to a C function with zero parameters.
Node* CallCFunction0(MachineType return_type, Node* function);
// Call to a C function with one parameter.
Node* CallCFunction1(MachineType return_type, MachineType arg0_type,
Node* function, Node* arg0);
// Call to a C function with two parameters.
Node* CallCFunction2(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, Node* function, Node* arg0,
Node* arg1);
// Call to a C function with eight parameters.
Node* CallCFunction8(MachineType return_type, MachineType arg0_type,
MachineType arg1_type, MachineType arg2_type,
MachineType arg3_type, MachineType arg4_type,
MachineType arg5_type, MachineType arg6_type,
MachineType arg7_type, Node* function, Node* arg0,
Node* arg1, Node* arg2, Node* arg3, Node* arg4,
Node* arg5, Node* arg6, Node* arg7);
void Return(Node* value); void Return(Node* value);
void Bind(Label* label); void Bind(Label* label);
void Deoptimize(Node* state); void Deoptimize(Node* state);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment