[turbofan] Add basic support for calling to (a subset of) C functions.
authorBenedikt Meurer <bmeurer@chromium.org>
Thu, 25 Jun 2015 08:32:06 +0000 (10:32 +0200)
committerBenedikt Meurer <bmeurer@chromium.org>
Thu, 25 Jun 2015 08:32:32 +0000 (08:32 +0000)
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}

30 files changed:
src/compiler/arm/code-generator-arm.cc
src/compiler/arm/instruction-codes-arm.h
src/compiler/arm/instruction-selector-arm.cc
src/compiler/arm/linkage-arm.cc
src/compiler/arm64/code-generator-arm64.cc
src/compiler/arm64/instruction-selector-arm64.cc
src/compiler/arm64/linkage-arm64.cc
src/compiler/code-generator-impl.h
src/compiler/code-generator.cc
src/compiler/ia32/code-generator-ia32.cc
src/compiler/ia32/instruction-codes-ia32.h
src/compiler/ia32/instruction-selector-ia32.cc
src/compiler/ia32/linkage-ia32.cc
src/compiler/instruction-codes.h
src/compiler/instruction-selector.cc
src/compiler/linkage-impl.h
src/compiler/linkage.h
src/compiler/mips/code-generator-mips.cc
src/compiler/mips/instruction-selector-mips.cc
src/compiler/mips/linkage-mips.cc
src/compiler/mips64/code-generator-mips64.cc
src/compiler/mips64/instruction-selector-mips64.cc
src/compiler/mips64/linkage-mips64.cc
src/compiler/x64/code-generator-x64.cc
src/compiler/x64/instruction-codes-x64.h
src/compiler/x64/instruction-selector-x64.cc
src/compiler/x64/linkage-x64.cc
test/cctest/compiler/test-run-machops.cc
test/unittests/compiler/raw-machine-assembler.cc
test/unittests/compiler/raw-machine-assembler.h

index a4ab884f8f203697026127fbec31449abb66106c..f147dd609b06aa1d1b3889170cdd8ed9e767fbeb 100644 (file)
@@ -373,6 +373,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       DCHECK_EQ(LeaveCC, i.OutputSBit());
       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:
       AssembleArchJump(i.InputRpo(0));
       DCHECK_EQ(LeaveCC, i.OutputSBit());
@@ -806,6 +822,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ Push(i.InputRegister(0));
       DCHECK_EQ(LeaveCC, i.OutputSBit());
       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: {
       Register object = i.InputRegister(0);
       Register index = i.InputRegister(1);
index 377fd8672c3ddc6bb1eab2aedb03658c040c64cb..c210c171e4335c82c8a7f693b228837110d71a5d 100644 (file)
@@ -93,6 +93,7 @@ namespace compiler {
   V(ArmLdr)                        \
   V(ArmStr)                        \
   V(ArmPush)                       \
+  V(ArmPoke)                       \
   V(ArmStoreWriteBarrier)
 
 
index 6fb0c5b5d5f61d8ef88418f8c86b191030f0613f..b5324544600c506a69813c309e691a49e3d8bb9d 100644 (file)
@@ -1096,11 +1096,27 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // 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
   // heuristics in the register allocator for where to emit constants.
-  InitializeCallBuffer(node, &buffer, true, false);
-
-  // Push any stack arguments.
-  for (Node* node : base::Reversed(buffer.pushed_nodes)) {
-    Emit(kArmPush, g.NoOutput(), g.UseRegister(node));
+  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.
+    for (Node* node : base::Reversed(buffer.pushed_nodes)) {
+      Emit(kArmPush, g.NoOutput(), g.UseRegister(node));
+    }
   }
 
   // Pass label of exception handler block.
@@ -1118,18 +1134,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Select the appropriate opcode based on the call type.
   InstructionCode opcode;
   switch (descriptor->kind()) {
-    case CallDescriptor::kCallCodeObject: {
-      opcode = kArchCallCodeObject;
+    case CallDescriptor::kCallAddress:
+      opcode =
+          kArchCallCFunction |
+          MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
+      break;
+    case CallDescriptor::kCallCodeObject:
+      opcode = kArchCallCodeObject | MiscField::encode(flags);
       break;
-    }
     case CallDescriptor::kCallJSFunction:
-      opcode = kArchCallJSFunction;
+      opcode = kArchCallJSFunction | MiscField::encode(flags);
       break;
     default:
       UNREACHABLE();
       return;
   }
-  opcode |= MiscField::encode(flags);
 
   // Emit the call instruction.
   size_t const output_count = buffer.outputs.size();
index 2b1faa2aca27ba3aa2b6fc86f3710532fd8f5e58..fcf6187804e4be65ea16761bb37259c4dc22a53b 100644 (file)
@@ -28,6 +28,7 @@ struct ArmLinkageHelperTraits {
     return register_parameters[i];
   }
   static int CRegisterParametersLength() { return 4; }
+  static int CStackBackingStoreLength() { return 0; }
 };
 
 
index 736d5638d8edddb63ade5e5c4bf5c1e2d67675c4..2307d034e60509ca8adef91c2908d4b6a7e60ff6 100644 (file)
@@ -420,6 +420,23 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ Jump(x10);
       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:
       AssembleArchJump(i.InputRpo(0));
       break;
index e051d65a06871b28cc7a3fa0ddaef5e243487530..b32ccf58cd22a17216a65db35fcf24c21ae26474 100644 (file)
@@ -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
   // register if there are multiple uses of it. Improve constant pool and the
   // 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.
   int aligned_push_count = static_cast<int>(buffer.pushed_nodes.size());
@@ -1392,18 +1392,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Select the appropriate opcode based on the call type.
   InstructionCode opcode;
   switch (descriptor->kind()) {
-    case CallDescriptor::kCallCodeObject: {
-      opcode = kArchCallCodeObject;
+    case CallDescriptor::kCallAddress:
+      opcode =
+          kArchCallCFunction |
+          MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
+      break;
+    case CallDescriptor::kCallCodeObject:
+      opcode = kArchCallCodeObject | MiscField::encode(flags);
       break;
-    }
     case CallDescriptor::kCallJSFunction:
-      opcode = kArchCallJSFunction;
+      opcode = kArchCallJSFunction | MiscField::encode(flags);
       break;
     default:
       UNREACHABLE();
       return;
   }
-  opcode |= MiscField::encode(flags);
 
   // Emit the call instruction.
   size_t const output_count = buffer.outputs.size();
index 745eb5cde694cc676552b9f32cb42cd84e33ea87..036da0f1bd221668cf2db28437dd0cd7024976f0 100644 (file)
@@ -28,6 +28,7 @@ struct Arm64LinkageHelperTraits {
     return register_parameters[i];
   }
   static int CRegisterParametersLength() { return 8; }
+  static int CStackBackingStoreLength() { return 0; }
 };
 
 
index 1cab854ce8f2759d194c9bfa584db6520075536e..f0762e9bbec2e0657c6e8372e643c0eed4db47d0 100644 (file)
@@ -57,6 +57,10 @@ class InstructionOperandConverter {
     return static_cast<uint8_t>(InputInt32(index) & 0x3F);
   }
 
+  ExternalReference InputExternalReference(size_t index) {
+    return ToExternalReference(instr_->InputAt(index));
+  }
+
   Handle<HeapObject> InputHeapObject(size_t index) {
     return ToHeapObject(instr_->InputAt(index));
   }
@@ -108,6 +112,10 @@ class InstructionOperandConverter {
 
   double ToDouble(InstructionOperand* op) { return ToConstant(op).ToFloat64(); }
 
+  ExternalReference ToExternalReference(InstructionOperand* op) {
+    return ToConstant(op).ToExternalReference();
+  }
+
   Handle<HeapObject> ToHeapObject(InstructionOperand* op) {
     return ToConstant(op).ToHeapObject();
   }
index 38301c2d1a63107e9e243132190678f70f4811bb..8d4baa3145a3ce0fef9eaa409c861f91c5cf8e51 100644 (file)
@@ -62,6 +62,11 @@ CodeGenerator::CodeGenerator(Frame* frame, Linkage* linkage,
 Handle<Code> CodeGenerator::GenerateCode() {
   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.
   PositionsRecorder* recorder = masm()->positions_recorder();
   LOG_CODE_EVENT(isolate(), CodeStartLinePosInfoRecordEvent(recorder));
index de3bac15e13539d1033d681a2d652b9a4f44cd27..cad28535ff0d4b660999db2623280b5e44e33966 100644 (file)
@@ -352,6 +352,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
       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:
       AssembleArchJump(i.InputRpo(0));
       break;
@@ -870,6 +886,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
         __ push(i.InputOperand(0));
       }
       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: {
       Register object = i.InputRegister(0);
       Register value = i.InputRegister(2);
index 3962040104c8f697b5272a053b230e99c090d0fb..4002a6776df6b08a1ad64c238a8682fc95cd1002 100644 (file)
@@ -91,6 +91,7 @@ namespace compiler {
   V(IA32Movsd)                     \
   V(IA32Lea)                       \
   V(IA32Push)                      \
+  V(IA32Poke)                      \
   V(IA32StoreWriteBarrier)         \
   V(IA32StackCheck)
 
index e7ddc1ad18287b909eb05a3fbb8ced724459e865..2720910ca3786678b4e5a52f1b5cf1031876a57b 100644 (file)
@@ -830,14 +830,33 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Compute InstructionOperands for inputs and outputs.
   InitializeCallBuffer(node, &buffer, true, true);
 
-  // Push any stack arguments.
-  for (Node* node : base::Reversed(buffer.pushed_nodes)) {
-    // TODO(titzer): handle pushing double parameters.
-    InstructionOperand value =
-        g.CanBeImmediate(node)
-            ? g.UseImmediate(node)
-            : IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node);
-    Emit(kIA32Push, g.NoOutput(), value);
+  // 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.
+    for (Node* node : base::Reversed(buffer.pushed_nodes)) {
+      // TODO(titzer): handle pushing double parameters.
+      InstructionOperand value =
+          g.CanBeImmediate(node)
+              ? g.UseImmediate(node)
+              : IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node);
+      Emit(kIA32Push, g.NoOutput(), value);
+    }
   }
 
   // Pass label of exception handler block.
@@ -855,18 +874,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Select the appropriate opcode based on the call type.
   InstructionCode opcode;
   switch (descriptor->kind()) {
-    case CallDescriptor::kCallCodeObject: {
-      opcode = kArchCallCodeObject;
+    case CallDescriptor::kCallAddress:
+      opcode =
+          kArchCallCFunction |
+          MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
+      break;
+    case CallDescriptor::kCallCodeObject:
+      opcode = kArchCallCodeObject | MiscField::encode(flags);
       break;
-    }
     case CallDescriptor::kCallJSFunction:
-      opcode = kArchCallJSFunction;
+      opcode = kArchCallJSFunction | MiscField::encode(flags);
       break;
     default:
       UNREACHABLE();
       return;
   }
-  opcode |= MiscField::encode(flags);
 
   // Emit the call instruction.
   size_t const output_count = buffer.outputs.size();
index bfe201bf6fcbdbd6a963a28113a2a2b02e7f1910..400d7a8cc80bc5cfb3546737b3d66b11b8d5bccf 100644 (file)
@@ -24,6 +24,7 @@ struct IA32LinkageHelperTraits {
   }
   static Register CRegisterParameter(int i) { return no_reg; }
   static int CRegisterParametersLength() { return 0; }
+  static int CStackBackingStoreLength() { return 0; }
 };
 
 typedef LinkageHelper<IA32LinkageHelperTraits> LH;
index 1bf732cfab3cf0e33a305f337deb31dc8565a858..a9db281fd3f4553a2731cafebe9e075166460eff 100644 (file)
@@ -35,32 +35,34 @@ namespace compiler {
 
 // Target-specific opcodes that specify which assembly sequence to emit.
 // Most opcodes specify a single instruction.
-#define ARCH_OPCODE_LIST(V) \
-  V(ArchCallCodeObject)     \
-  V(ArchTailCallCodeObject) \
-  V(ArchCallJSFunction)     \
-  V(ArchTailCallJSFunction) \
-  V(ArchJmp)                \
-  V(ArchLookupSwitch)       \
-  V(ArchTableSwitch)        \
-  V(ArchNop)                \
-  V(ArchDeoptimize)         \
-  V(ArchRet)                \
-  V(ArchStackPointer)       \
-  V(ArchFramePointer)       \
-  V(ArchTruncateDoubleToI)  \
-  V(CheckedLoadInt8)        \
-  V(CheckedLoadUint8)       \
-  V(CheckedLoadInt16)       \
-  V(CheckedLoadUint16)      \
-  V(CheckedLoadWord32)      \
-  V(CheckedLoadFloat32)     \
-  V(CheckedLoadFloat64)     \
-  V(CheckedStoreWord8)      \
-  V(CheckedStoreWord16)     \
-  V(CheckedStoreWord32)     \
-  V(CheckedStoreFloat32)    \
-  V(CheckedStoreFloat64)    \
+#define ARCH_OPCODE_LIST(V)   \
+  V(ArchCallCodeObject)       \
+  V(ArchTailCallCodeObject)   \
+  V(ArchCallJSFunction)       \
+  V(ArchTailCallJSFunction)   \
+  V(ArchPrepareCallCFunction) \
+  V(ArchCallCFunction)        \
+  V(ArchJmp)                  \
+  V(ArchLookupSwitch)         \
+  V(ArchTableSwitch)          \
+  V(ArchNop)                  \
+  V(ArchDeoptimize)           \
+  V(ArchRet)                  \
+  V(ArchStackPointer)         \
+  V(ArchFramePointer)         \
+  V(ArchTruncateDoubleToI)    \
+  V(CheckedLoadInt8)          \
+  V(CheckedLoadUint8)         \
+  V(CheckedLoadInt16)         \
+  V(CheckedLoadUint16)        \
+  V(CheckedLoadWord32)        \
+  V(CheckedLoadFloat32)       \
+  V(CheckedLoadFloat64)       \
+  V(CheckedStoreWord8)        \
+  V(CheckedStoreWord16)       \
+  V(CheckedStoreWord32)       \
+  V(CheckedStoreFloat32)      \
+  V(CheckedStoreFloat64)      \
   TARGET_ARCH_OPCODE_LIST(V)
 
 enum ArchOpcode {
index e7ca5e2532c531d48de6fc9309edc283c6c72fb5..6ab063e5165275775c9fdce384941f9e7086884c 100644 (file)
@@ -336,8 +336,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
     case CallDescriptor::kCallAddress:
       buffer->instruction_args.push_back(
           (call_address_immediate &&
-           (callee->opcode() == IrOpcode::kInt32Constant ||
-            callee->opcode() == IrOpcode::kInt64Constant))
+           callee->opcode() == IrOpcode::kExternalConstant)
               ? g.UseImmediate(callee)
               : g.UseRegister(callee));
       break;
@@ -373,7 +372,7 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
   // not appear as arguments to the call. Everything else ends up
   // as an InstructionOperand argument to the call.
   auto iter(call->inputs().begin());
-  int pushed_count = 0;
+  size_t pushed_count = 0;
   for (size_t index = 0; index < input_count; ++iter, ++index) {
     DCHECK(iter != call->inputs().end());
     DCHECK((*iter)->op()->opcode() != IrOpcode::kFrameState);
@@ -393,10 +392,8 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
       buffer->instruction_args.push_back(op);
     }
   }
-  CHECK_EQ(pushed_count, static_cast<int>(buffer->pushed_nodes.size()));
-  DCHECK(static_cast<size_t>(input_count) ==
-         (buffer->instruction_args.size() + buffer->pushed_nodes.size() -
-          buffer->frame_state_value_count()));
+  DCHECK_EQ(input_count, buffer->instruction_args.size() + pushed_count -
+                             buffer->frame_state_value_count());
 }
 
 
index 76b40faf350965291742520f2d37f695c8605c46..0a18f6e8db21301d833ab31ed8746e745d11d40f 100644 (file)
@@ -204,11 +204,13 @@ class LinkageHelper {
 
     // Add register and/or stack parameter(s).
     const int parameter_count = static_cast<int>(msig->parameter_count());
+    int stack_offset = LinkageTraits::CStackBackingStoreLength();
     for (int i = 0; i < parameter_count; i++) {
       if (i < LinkageTraits::CRegisterParametersLength()) {
         locations.AddParam(regloc(LinkageTraits::CRegisterParameter(i)));
       } else {
-        locations.AddParam(stackloc(-1 - i));
+        locations.AddParam(stackloc(-1 - stack_offset));
+        stack_offset++;
       }
     }
 
index abdef7a4efc47eafaf9f32105600c03a6a40d3f0..0358cd28f1b054509750e1a402bb354fa909c7ee 100644 (file)
@@ -102,12 +102,18 @@ class CallDescriptor final : public ZoneObject {
   // Returns the kind of this call.
   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.
   bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; }
 
   // The number of return values from this call.
   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
   // object.
   size_t JSParameterCount() const { return js_param_count_; }
index 3afb86af45556328c1af7f805c3cf578e4eb216a..f7c1e01f80153325ded7c75424b1ab1f737790b9 100644 (file)
@@ -463,6 +463,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ Jump(at);
       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:
       AssembleArchJump(i.InputRpo(0));
       break;
index c042ea11e67de559428dfb716773aa78259ae80d..ce40753995db0a93dded1fd3b1c8e03b1b6a4f84 100644 (file)
@@ -524,18 +524,34 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
 
   // Compute InstructionOperands for inputs and outputs.
-  InitializeCallBuffer(node, &buffer, true, false);
-  // Possibly align stack here for functions.
-  int push_count = buffer.pushed_nodes.size();
-  if (push_count > 0) {
-    Emit(kMipsStackClaim, g.NoOutput(),
-         g.TempImmediate(push_count << kPointerSizeLog2));
-  }
-  int slot = buffer.pushed_nodes.size() - 1;
-  for (Node* node : base::Reversed(buffer.pushed_nodes)) {
-    Emit(kMipsStoreToStackSlot, g.NoOutput(), g.UseRegister(node),
-         g.TempImmediate(slot << kPointerSizeLog2));
-    slot--;
+  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.
+    int push_count = buffer.pushed_nodes.size();
+    if (push_count > 0) {
+      Emit(kMipsStackClaim, g.NoOutput(),
+           g.TempImmediate(push_count << kPointerSizeLog2));
+    }
+    int slot = buffer.pushed_nodes.size() - 1;
+    for (Node* node : base::Reversed(buffer.pushed_nodes)) {
+      Emit(kMipsStoreToStackSlot, g.NoOutput(), g.UseRegister(node),
+           g.TempImmediate(slot << kPointerSizeLog2));
+      slot--;
+    }
   }
 
   // Pass label of exception handler block.
@@ -553,18 +569,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Select the appropriate opcode based on the call type.
   InstructionCode opcode;
   switch (descriptor->kind()) {
-    case CallDescriptor::kCallCodeObject: {
-      opcode = kArchCallCodeObject;
+    case CallDescriptor::kCallAddress:
+      opcode =
+          kArchCallCFunction |
+          MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
+      break;
+    case CallDescriptor::kCallCodeObject:
+      opcode = kArchCallCodeObject | MiscField::encode(flags);
       break;
-    }
     case CallDescriptor::kCallJSFunction:
-      opcode = kArchCallJSFunction;
+      opcode = kArchCallJSFunction | MiscField::encode(flags);
       break;
     default:
       UNREACHABLE();
       return;
   }
-  opcode |= MiscField::encode(flags);
 
   // Emit the call instruction.
   size_t const output_count = buffer.outputs.size();
index 9480b73eae8fb786431256ee44d75f57eb4ad610..3378213af3237f5fd9651c60ba9afd4667e4da2e 100644 (file)
@@ -28,6 +28,7 @@ struct MipsLinkageHelperTraits {
     return register_parameters[i];
   }
   static int CRegisterParametersLength() { return 4; }
+  static int CStackBackingStoreLength() { return 0; }
 };
 
 
index 998a4f3f894bdb08d4396fe4357fc9f6e17d2b8e..bd84c442b7fdacab4dfc20ee02bb72a80ab99a08 100644 (file)
@@ -463,6 +463,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ Jump(at);
       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:
       AssembleArchJump(i.InputRpo(0));
       break;
index 4040337e0ce423cea62f10502d96575b9b92b48c..48185a676ffb495c5104f86ff9998be100b74046 100644 (file)
@@ -673,18 +673,33 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
 
   // Compute InstructionOperands for inputs and outputs.
-  InitializeCallBuffer(node, &buffer, true, false);
+  InitializeCallBuffer(node, &buffer, true, true);
 
-  const int32_t push_count = static_cast<int32_t>(buffer.pushed_nodes.size());
-  if (push_count > 0) {
-    Emit(kMips64StackClaim, g.NoOutput(),
-         g.TempImmediate(push_count << kPointerSizeLog2));
-  }
-  int32_t slot = push_count - 1;
-  for (Node* node : base::Reversed(buffer.pushed_nodes)) {
-    Emit(kMips64StoreToStackSlot, g.NoOutput(), g.UseRegister(node),
-         g.TempImmediate(slot << kPointerSizeLog2));
-    slot--;
+  // 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());
+    if (push_count > 0) {
+      Emit(kMips64StackClaim, g.NoOutput(),
+           g.TempImmediate(push_count << kPointerSizeLog2));
+    }
+    int32_t slot = push_count - 1;
+    for (Node* node : base::Reversed(buffer.pushed_nodes)) {
+      Emit(kMips64StoreToStackSlot, g.NoOutput(), g.UseRegister(node),
+           g.TempImmediate(slot << kPointerSizeLog2));
+      slot--;
+    }
   }
 
   // Pass label of exception handler block.
@@ -702,12 +717,16 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Select the appropriate opcode based on the call type.
   InstructionCode opcode;
   switch (descriptor->kind()) {
-    case CallDescriptor::kCallCodeObject: {
-      opcode = kArchCallCodeObject;
+    case CallDescriptor::kCallAddress:
+      opcode =
+          kArchCallCFunction |
+          MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
+      break;
+    case CallDescriptor::kCallCodeObject:
+      opcode = kArchCallCodeObject | MiscField::encode(flags);
       break;
-    }
     case CallDescriptor::kCallJSFunction:
-      opcode = kArchCallJSFunction;
+      opcode = kArchCallJSFunction | MiscField::encode(flags);
       break;
     default:
       UNREACHABLE();
index 6fed0617d05518223bee6d122193fd39c8565592..52eded235294466698f5fa22d62061773fd89829 100644 (file)
@@ -28,6 +28,7 @@ struct MipsLinkageHelperTraits {
     return register_parameters[i];
   }
   static int CRegisterParametersLength() { return 8; }
+  static int CStackBackingStoreLength() { return 0; }
 };
 
 
index 5f55b33f8385b1cebb7f72931546110bed6306c4..5de3ad1d38c20897c8547f2b4c485b7cdd124236 100644 (file)
@@ -602,6 +602,22 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
       __ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
       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:
       AssembleArchJump(i.InputRpo(0));
       break;
@@ -1215,6 +1231,15 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
         }
       }
       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: {
       Register object = i.InputRegister(0);
       Register value = i.InputRegister(2);
index 428b7dc1865ee74dc1fc51694dcb016f6db6e59d..2e107299545c13f7ee67991471dbb53743d4de3c 100644 (file)
@@ -114,6 +114,7 @@ namespace compiler {
   V(X64Dec32)                      \
   V(X64Inc32)                      \
   V(X64Push)                       \
+  V(X64Poke)                       \
   V(X64StoreWriteBarrier)          \
   V(X64StackCheck)
 
index 839af8e45542dd64bcf7ab157decf681da805527..35785c37a8ebcb8a7230ef330741fdbf19690c6e 100644 (file)
@@ -1038,14 +1038,31 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Compute InstructionOperands for inputs and outputs.
   InitializeCallBuffer(node, &buffer, true, true);
 
-  // Push any stack arguments.
-  for (Node* node : base::Reversed(buffer.pushed_nodes)) {
-    // TODO(titzer): handle pushing double parameters.
-    InstructionOperand value =
-        g.CanBeImmediate(node)
-            ? g.UseImmediate(node)
-            : IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node);
-    Emit(kX64Push, g.NoOutput(), value);
+  // 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.
+    for (Node* node : base::Reversed(buffer.pushed_nodes)) {
+      // TODO(titzer): handle pushing double parameters.
+      InstructionOperand value =
+          g.CanBeImmediate(node)
+              ? g.UseImmediate(node)
+              : IsSupported(ATOM) ? g.UseRegister(node) : g.Use(node);
+      Emit(kX64Push, g.NoOutput(), value);
+    }
   }
 
   // Pass label of exception handler block.
@@ -1063,17 +1080,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
   // Select the appropriate opcode based on the call type.
   InstructionCode opcode;
   switch (descriptor->kind()) {
+    case CallDescriptor::kCallAddress:
+      opcode =
+          kArchCallCFunction |
+          MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
+      break;
     case CallDescriptor::kCallCodeObject:
-      opcode = kArchCallCodeObject;
+      opcode = kArchCallCodeObject | MiscField::encode(flags);
       break;
     case CallDescriptor::kCallJSFunction:
-      opcode = kArchCallJSFunction;
+      opcode = kArchCallJSFunction | MiscField::encode(flags);
       break;
     default:
       UNREACHABLE();
       return;
   }
-  opcode |= MiscField::encode(flags);
 
   // Emit the call instruction.
   size_t const output_count = buffer.outputs.size();
index 1b840a995d8003a0dbe1c763cdb0502395f53783..87e8ca57f028dd4019c263194605ad9f9daddad3 100644 (file)
@@ -43,6 +43,7 @@ struct X64LinkageHelperTraits {
     }
   }
   static int CRegisterParametersLength() { return kWin64 ? 4 : 6; }
+  static int CStackBackingStoreLength() { return kWin64 ? 4 : 0; }
 };
 
 typedef LinkageHelper<X64LinkageHelperTraits> LH;
index 251afdfb70f070c580bff952aa83b2fa40e66a86..172f4c9e673f2aeb0416b590e71cc2afaa46f0a1 100644 (file)
@@ -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
index 20948108aaa4dd17073bd52f4f6a0e5884a7a9a3..9055d7e736c284ed7bd7853cadfca85f5209ec11 100644 (file)
@@ -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) {
   DCHECK(current_block_ == NULL);
   DCHECK(!label->bound_);
index 45e1e3ec31e27d00b25d61b6ce93d8b69eab89bf..d87da8d4daab86550e04aeb64b3b41c3baf5caf9 100644 (file)
@@ -471,6 +471,23 @@ class RawMachineAssembler : public GraphBuilder {
   // Call to a runtime function with zero parameters.
   Node* CallRuntime1(Runtime::FunctionId function, Node* arg0, Node* context,
                      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 Bind(Label* label);
   void Deoptimize(Node* state);