Index: test/cctest/compiler/test-run-native-calls.cc |
diff --git a/test/cctest/compiler/test-run-native-calls.cc b/test/cctest/compiler/test-run-native-calls.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ac85c9bdf2c0922e6b5802ec164a94d78cfb7d53 |
--- /dev/null |
+++ b/test/cctest/compiler/test-run-native-calls.cc |
@@ -0,0 +1,347 @@ |
+// Copyright 2014 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/codegen.h" |
+#include "src/compiler/linkage.h" |
+#include "src/compiler/machine-type.h" |
+#include "src/compiler/raw-machine-assembler.h" |
+ |
+#include "test/cctest/cctest.h" |
+#include "test/cctest/compiler/codegen-tester.h" |
+#include "test/cctest/compiler/graph-builder-tester.h" |
+#include "test/cctest/compiler/value-helper.h" |
+ |
+using namespace v8::base; |
+using namespace v8::internal; |
+using namespace v8::internal::compiler; |
+ |
+typedef RawMachineAssembler::Label MLabel; |
+ |
+ |
+#if V8_TURBOFAN_TARGET |
+ |
+namespace { |
+// Helper for allocating either an GP or FP reg, or the next stack slot. |
+struct Allocator { |
+ Allocator(int* gp, int gpc, int* fp, int fpc) |
+ : gp_count(gpc), |
+ gp_offset(0), |
+ gp_regs(gp), |
+ fp_count(fpc), |
+ fp_offset(0), |
+ fp_regs(fp), |
+ stack_offset(0) {} |
+ |
+ int gp_count; |
+ int gp_offset; |
+ int* gp_regs; |
+ |
+ int fp_count; |
+ int fp_offset; |
+ int* fp_regs; |
+ |
+ int stack_offset; |
+ |
+ LinkageLocation Next(MachineType type) { |
+ if (IsFloatingPoint(type)) { |
+ // Allocate a floating point register/stack location. |
+ if (fp_offset < fp_count) { |
+ return LinkageLocation::ForRegister(fp_regs[fp_offset++]); |
+ } else { |
+ int offset = -1 - stack_offset; |
+ stack_offset += Words(type); |
+ return LinkageLocation::ForCallerFrameSlot(offset); |
+ } |
+ } else { |
+ // Allocate a general purpose register/stack location. |
+ if (gp_offset < gp_count) { |
+ return LinkageLocation::ForRegister(gp_regs[gp_offset++]); |
+ } else { |
+ int offset = -1 - stack_offset; |
+ stack_offset += Words(type); |
+ return LinkageLocation::ForCallerFrameSlot(offset); |
+ } |
+ } |
+ } |
+ bool IsFloatingPoint(MachineType type) { |
+ return RepresentationOf(type) == kRepFloat32 || |
+ RepresentationOf(type) == kRepFloat64; |
+ } |
+ int Words(MachineType type) { |
+ int size = ElementSizeOf(type); |
+ return size <= kPointerSize ? 1 : size / kPointerSize; |
+ } |
+ void Reset() { |
+ fp_offset = 0; |
+ gp_offset = 0; |
+ stack_offset = 0; |
+ } |
+}; |
+ |
+ |
+class RegisterConfig { |
+ public: |
+ RegisterConfig(Allocator& p, Allocator& r) : params(p), rets(r) {} |
+ |
+ CallDescriptor* Create(Zone* zone, MachineSignature* msig) { |
+ rets.Reset(); |
+ params.Reset(); |
+ |
+ LocationSignature::Builder locations(zone, msig->return_count(), |
+ msig->parameter_count()); |
+ // Add return location(s). |
+ const int return_count = static_cast<int>(locations.return_count_); |
+ for (int i = 0; i < return_count; i++) { |
+ locations.AddReturn(rets.Next(msig->GetReturn(i))); |
+ } |
+ |
+ // Add register and/or stack parameter(s). |
+ const int parameter_count = static_cast<int>(msig->parameter_count()); |
+ for (int i = 0; i < parameter_count; i++) { |
+ locations.AddParam(params.Next(msig->GetParam(i))); |
+ } |
+ |
+ const RegList kCalleeSaveRegisters = 0; |
+ const RegList kCalleeSaveFPRegisters = 0; |
+ |
+ MachineType target_type = compiler::kMachAnyTagged; |
+ LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); |
+ int stack_param_count = params.stack_offset; |
+ return new (zone) CallDescriptor( // -- |
+ CallDescriptor::kCallCodeObject, // kind |
+ target_type, // target MachineType |
+ target_loc, // target location |
+ msig, // machine_sig |
+ locations.Build(), // location_sig |
+ stack_param_count, // stack_parameter_count |
+ compiler::Operator::kNoProperties, // properties |
+ kCalleeSaveRegisters, // callee-saved registers |
+ kCalleeSaveFPRegisters, // callee-saved fp regs |
+ CallDescriptor::kNoFlags, // flags |
+ "c-call"); |
+ } |
+ |
+ private: |
+ Allocator& params; |
+ Allocator& rets; |
+}; |
+ |
+} // namespace |
+ |
+ |
+static Handle<Code> CompileGraph(const char* name, CallDescriptor* desc, |
+ Graph* graph, Schedule* schedule = nullptr) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ Handle<Code> code = |
+ Pipeline::GenerateCodeForTesting(isolate, desc, graph, schedule); |
+ CHECK(!code.is_null()); |
+#ifdef ENABLE_DISASSEMBLER |
+ if (FLAG_print_opt_code) { |
+ OFStream os(stdout); |
+ code->Disassemble(name, os); |
+ } |
+#endif |
+ return code; |
+} |
+ |
+ |
+static Handle<Code> WrapWithCFunction(Handle<Code> inner, |
+ CallDescriptor* desc) { |
+ Zone zone; |
+ MachineSignature* msig = |
+ const_cast<MachineSignature*>(desc->GetMachineSignature()); |
+ int param_count = static_cast<int>(msig->parameter_count()); |
+ GraphAndBuilders caller(&zone); |
+ { |
+ GraphAndBuilders& b = caller; |
+ Node* start = b.graph()->NewNode(b.common()->Start(param_count + 3)); |
+ b.graph()->SetStart(start); |
+ Unique<HeapObject> unique = Unique<HeapObject>::CreateUninitialized(inner); |
+ Node* target = b.graph()->NewNode(b.common()->HeapConstant(unique)); |
+ |
+ // Add arguments to the call. |
+ Node** args = zone.NewArray<Node*>(param_count + 3); |
+ int index = 0; |
+ args[index++] = target; |
+ for (int i = 0; i < param_count; i++) { |
+ args[index] = b.graph()->NewNode(b.common()->Parameter(i), start); |
+ index++; |
+ } |
+ args[index++] = start; // effect. |
+ args[index++] = start; // control. |
+ |
+ // Build the call and return nodes. |
+ Node* call = |
+ b.graph()->NewNode(b.common()->Call(desc), param_count + 3, args); |
+ Node* ret = b.graph()->NewNode(b.common()->Return(), call, call, start); |
+ b.graph()->SetEnd(ret); |
+ } |
+ |
+ CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, msig); |
+ |
+ return CompileGraph("wrapper", cdesc, caller.graph()); |
+} |
+ |
+ |
+static void TestInt32Sub(CallDescriptor* desc) { |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ Zone zone; |
+ GraphAndBuilders inner(&zone); |
+ { |
+ // Build the add function. |
+ GraphAndBuilders& b = inner; |
+ Node* start = b.graph()->NewNode(b.common()->Start(5)); |
+ b.graph()->SetStart(start); |
+ Node* p0 = b.graph()->NewNode(b.common()->Parameter(0), start); |
+ Node* p1 = b.graph()->NewNode(b.common()->Parameter(1), start); |
+ Node* add = b.graph()->NewNode(b.machine()->Int32Sub(), p0, p1); |
+ Node* ret = b.graph()->NewNode(b.common()->Return(), add, start, start); |
+ b.graph()->SetEnd(ret); |
+ } |
+ |
+ Handle<Code> inner_code = CompileGraph("Int32Sub", desc, inner.graph()); |
+ Handle<Code> wrapper = WrapWithCFunction(inner_code, desc); |
+ MachineSignature* msig = |
+ const_cast<MachineSignature*>(desc->GetMachineSignature()); |
+ CodeRunner<int32_t> runnable(isolate, wrapper, |
+ CSignature::FromMachine(&zone, msig)); |
+ |
+ FOR_INT32_INPUTS(i) { |
+ FOR_INT32_INPUTS(j) { |
+ int32_t expected = static_cast<int32_t>(static_cast<uint32_t>(*i) - |
+ static_cast<uint32_t>(*j)); |
+ int32_t result = runnable.Call(*i, *j); |
+ CHECK_EQ(expected, result); |
+ } |
+ } |
+} |
+ |
+ |
+static void CopyTwentyInt32(CallDescriptor* desc) { |
+ const int kNumParams = 20; |
+ int32_t input[kNumParams]; |
+ int32_t output[kNumParams]; |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ HandleScope scope(isolate); |
+ Handle<Code> inner = Handle<Code>::null(); |
+ { |
+ // Writes all parameters into the output buffer. |
+ Zone zone; |
+ Graph graph(&zone); |
+ RawMachineAssembler raw(isolate, &graph, desc); |
+ Node* base = raw.PointerConstant(output); |
+ for (int i = 0; i < kNumParams; i++) { |
+ Node* offset = raw.Int32Constant(i * sizeof(int32_t)); |
+ raw.Store(kMachInt32, base, offset, raw.Parameter(i)); |
+ } |
+ raw.Return(raw.Int32Constant(42)); |
+ inner = CompileGraph("CopyTwentyInt32", desc, &graph, raw.Export()); |
+ } |
+ |
+ CSignature0<int32_t> csig; |
+ Handle<Code> wrapper = Handle<Code>::null(); |
+ { |
+ // Loads parameters from the input buffer and calls the above code. |
+ Zone zone; |
+ Graph graph(&zone); |
+ CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig); |
+ RawMachineAssembler raw(isolate, &graph, cdesc); |
+ Node* base = raw.PointerConstant(input); |
+ Unique<HeapObject> unique = Unique<HeapObject>::CreateUninitialized(inner); |
+ Node* target = raw.HeapConstant(unique); |
+ Node** args = zone.NewArray<Node*>(kNumParams); |
+ for (int i = 0; i < kNumParams; i++) { |
+ Node* offset = raw.Int32Constant(i * sizeof(int32_t)); |
+ args[i] = raw.Load(kMachInt32, base, offset); |
+ } |
+ |
+ Node* call = raw.CallN(desc, target, args); |
+ raw.Return(call); |
+ wrapper = |
+ CompileGraph("CopyTwentyInt32-wrapper", cdesc, &graph, raw.Export()); |
+ } |
+ |
+ CodeRunner<int32_t> runnable(isolate, wrapper, &csig); |
+ |
+ // Run the code, checking it correctly implements the memcpy. |
+ for (int i = 0; i < 5; i++) { |
+ uint32_t base = 1111111111u * i; |
+ for (int j = 0; j < kNumParams; j++) { |
+ input[j] = static_cast<int32_t>(base + 13 * j); |
+ } |
+ |
+ memset(output, 0, sizeof(output)); |
+ CHECK_EQ(42, runnable.Call()); |
+ |
+ for (int j = 0; j < kNumParams; j++) { |
+ CHECK_EQ(input[j], output[j]); |
+ } |
+ } |
+} |
+ |
+ |
+TEST(Run_Int32Sub_all_allocatable_pairs) { |
+ MachineType types[] = {kMachInt32, kMachInt32, kMachInt32}; |
+ MachineSignature sig(1, 2, types); |
+ for (int r = 0; r < Register::kMaxNumAllocatableRegisters; r++) { |
+ Zone zone; |
+ for (int p0 = 0; p0 < Register::kMaxNumAllocatableRegisters; p0++) { |
+ for (int p1 = 0; p1 < Register::kMaxNumAllocatableRegisters; p1++) { |
+ if (p0 == p1) continue; |
+ int parray[] = {p0, p1}; |
+ int rarray[] = {r}; |
+ Allocator params(parray, 2, nullptr, 0); |
+ Allocator rets(rarray, 1, nullptr, 0); |
+ RegisterConfig config(params, rets); |
+ CallDescriptor* desc = config.Create(&zone, &sig); |
+ TestInt32Sub(desc); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+TEST(Run_Int32Sub_all_allocatable_single) { |
+ MachineType types[] = {kMachInt32, kMachInt32, kMachInt32}; |
+ MachineSignature sig(1, 2, types); |
+ for (int r = 0; r < Register::kMaxNumAllocatableRegisters; r++) { |
+ Zone zone; |
+ for (int p0 = 0; p0 < Register::kMaxNumAllocatableRegisters; p0++) { |
+ int parray[] = {p0}; |
+ int rarray[] = {r}; |
+ Allocator params(parray, 1, nullptr, 0); |
+ Allocator rets(rarray, 1, nullptr, 0); |
+ RegisterConfig config(params, rets); |
+ CallDescriptor* desc = config.Create(&zone, &sig); |
+ TestInt32Sub(desc); |
+ } |
+ } |
+} |
+ |
+ |
+TEST(Run_CopyTwentyInt32_all_allocatable_pairs) { |
+ MachineType types[] = { |
+ kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, |
+ kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, |
+ kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, kMachInt32, |
+ kMachInt32, kMachInt32, kMachInt32}; |
+ MachineSignature sig(1, 20, types); |
+ for (int p0 = 0; p0 < Register::kMaxNumAllocatableRegisters; p0++) { |
+ Zone zone; |
+ for (int p1 = 0; p1 < Register::kMaxNumAllocatableRegisters; p1++) { |
+ if (p0 == p1) continue; |
+ int parray[] = {p0, p1}; |
+ int rarray[] = {0}; |
+ Allocator params(parray, 2, nullptr, 0); |
+ Allocator rets(rarray, 1, nullptr, 0); |
+ RegisterConfig config(params, rets); |
+ CallDescriptor* desc = config.Create(&zone, &sig); |
+ CopyTwentyInt32(desc); |
+ } |
+ } |
+} |
+ |
+ |
+#endif // V8_TURBOFAN_TARGET |