| 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
|
|
|