v8/test/cctest/compiler/test-run-native-calls.cc
danno 3e2085eba4 [turbofan] Add MachineType to LinkageLocation
By adding MachineType to LinkageLocation, it is possible not only to reason
about the location of a LinkageLocation on the stack, but also about it's
size. This will be useful in follow-on CLs that attempt to merge some of the
parameter passing logic of tail calls and normal (non-tail) calls.

As a nice side-effect, it is no longer necessary to separately keep a
MachineSignature in a CallDescriptor, because the MachineTypes contianed in
LinkageLocation for all of the Descriptor's parameters and return types are
sufficient. This CL therefore removes the MachineSignature from the
CallDescriptor and adjusts all the calling code accordingly, simplifying and
de-duplicating code in a bunch of places.

R=titzer@chromium.org, bmeurer@chromium.org
LOG=N

Review-Url: https://codereview.chromium.org/2124023003
Cr-Commit-Position: refs/heads/master@{#37633}
2016-07-11 10:39:34 +00:00

1213 lines
38 KiB
C++

// Copyright 2015 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/assembler.h"
#include "src/codegen.h"
#include "src/compiler/linkage.h"
#include "src/compiler/raw-machine-assembler.h"
#include "src/machine-type.h"
#include "src/register-configuration.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"
namespace v8 {
namespace internal {
namespace compiler {
const auto GetRegConfig = RegisterConfiguration::Turbofan;
namespace {
typedef float float32;
typedef double float64;
// Picks a representative pair of integers from the given range.
// If there are less than {max_pairs} possible pairs, do them all, otherwise try
// to select a representative set.
class Pairs {
public:
Pairs(int max_pairs, int range, const int* codes)
: range_(range),
codes_(codes),
max_pairs_(std::min(max_pairs, range_ * range_)),
counter_(0) {}
bool More() { return counter_ < max_pairs_; }
void Next(int* r0, int* r1, bool same_is_ok) {
do {
// Find the next pair.
if (exhaustive()) {
*r0 = codes_[counter_ % range_];
*r1 = codes_[counter_ / range_];
} else {
// Try each integer at least once for both r0 and r1.
int index = counter_ / 2;
if (counter_ & 1) {
*r0 = codes_[index % range_];
*r1 = codes_[index / range_];
} else {
*r1 = codes_[index % range_];
*r0 = codes_[index / range_];
}
}
counter_++;
if ((same_is_ok) || (*r0 != *r1)) break;
if (counter_ == max_pairs_) {
// For the last hurrah, reg#0 with reg#n-1
*r0 = codes_[0];
*r1 = codes_[range_ - 1];
break;
}
} while (true);
}
private:
int range_;
const int* codes_;
int max_pairs_;
int counter_;
bool exhaustive() { return max_pairs_ == (range_ * range_); }
};
// Pairs of general purpose registers.
class RegisterPairs : public Pairs {
public:
RegisterPairs()
: Pairs(100, GetRegConfig()->num_allocatable_general_registers(),
GetRegConfig()->allocatable_general_codes()) {}
};
// Pairs of double registers.
class Float32RegisterPairs : public Pairs {
public:
Float32RegisterPairs()
: Pairs(
100,
#if V8_TARGET_ARCH_ARM
// TODO(bbudge) Modify wasm linkage to allow use of all float regs.
GetRegConfig()->num_allocatable_double_registers() / 2 - 2,
#else
GetRegConfig()->num_allocatable_double_registers(),
#endif
GetRegConfig()->allocatable_double_codes()) {
}
};
// Pairs of double registers.
class Float64RegisterPairs : public Pairs {
public:
Float64RegisterPairs()
: Pairs(100, GetRegConfig()->num_allocatable_double_registers(),
GetRegConfig()->allocatable_double_codes()) {}
};
// 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.representation())) {
// Allocate a floating point register/stack location.
if (fp_offset < fp_count) {
int code = fp_regs[fp_offset++];
#if V8_TARGET_ARCH_ARM
// TODO(bbudge) Modify wasm linkage to allow use of all float regs.
if (type.representation() == MachineRepresentation::kFloat32) code *= 2;
#endif
return LinkageLocation::ForRegister(code, type);
} else {
int offset = -1 - stack_offset;
stack_offset += StackWords(type);
return LinkageLocation::ForCallerFrameSlot(offset, type);
}
} else {
// Allocate a general purpose register/stack location.
if (gp_offset < gp_count) {
return LinkageLocation::ForRegister(gp_regs[gp_offset++], type);
} else {
int offset = -1 - stack_offset;
stack_offset += StackWords(type);
return LinkageLocation::ForCallerFrameSlot(offset, type);
}
}
}
int StackWords(MachineType type) {
int size = 1 << ElementSizeLog2Of(type.representation());
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 = MachineType::AnyTagged();
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
locations.Build(), // location_sig
stack_param_count, // stack_parameter_count
compiler::Operator::kNoProperties, // properties
kCalleeSaveRegisters, // callee-saved registers
kCalleeSaveFPRegisters, // callee-saved fp regs
CallDescriptor::kUseNativeStack, // flags
"c-call");
}
private:
Allocator& params;
Allocator& rets;
};
const int kMaxParamCount = 64;
MachineType kIntTypes[kMaxParamCount + 1] = {
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32(), MachineType::Int32()};
// For making uniform int32 signatures shorter.
class Int32Signature : public MachineSignature {
public:
explicit Int32Signature(int param_count)
: MachineSignature(1, param_count, kIntTypes) {
CHECK(param_count <= kMaxParamCount);
}
};
Handle<Code> CompileGraph(const char* name, CallDescriptor* desc, Graph* graph,
Schedule* schedule = nullptr) {
Isolate* isolate = CcTest::InitIsolateOnce();
CompilationInfo info(ArrayVector("testing"), isolate, graph->zone());
Handle<Code> code =
Pipeline::GenerateCodeForTesting(&info, 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;
}
Handle<Code> WrapWithCFunction(Handle<Code> inner, CallDescriptor* desc) {
Zone zone(inner->GetIsolate()->allocator());
int param_count = static_cast<int>(desc->ParameterCount());
GraphAndBuilders caller(&zone);
{
GraphAndBuilders& b = caller;
Node* start = b.graph()->NewNode(b.common()->Start(param_count + 3));
b.graph()->SetStart(start);
Node* target = b.graph()->NewNode(b.common()->HeapConstant(inner));
// 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);
}
MachineSignature* msig = desc->GetMachineSignature(&zone);
CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, msig);
return CompileGraph("wrapper", cdesc, caller.graph());
}
template <typename CType>
class ArgsBuffer {
public:
static const int kMaxParamCount = 64;
explicit ArgsBuffer(int count, int seed = 1) : count_(count), seed_(seed) {
// initialize the buffer with "seed 0"
seed_ = 0;
Mutate();
seed_ = seed;
}
class Sig : public MachineSignature {
public:
explicit Sig(int param_count)
: MachineSignature(1, param_count, MachTypes()) {
CHECK(param_count <= kMaxParamCount);
}
};
static MachineType* MachTypes() {
MachineType t = MachineTypeForC<CType>();
static MachineType kTypes[kMaxParamCount + 1] = {
t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t,
t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t,
t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t};
return kTypes;
}
Node* MakeConstant(RawMachineAssembler& raw, int32_t value) {
return raw.Int32Constant(value);
}
Node* MakeConstant(RawMachineAssembler& raw, int64_t value) {
return raw.Int64Constant(value);
}
Node* MakeConstant(RawMachineAssembler& raw, float32 value) {
return raw.Float32Constant(value);
}
Node* MakeConstant(RawMachineAssembler& raw, float64 value) {
return raw.Float64Constant(value);
}
Node* LoadInput(RawMachineAssembler& raw, Node* base, int index) {
Node* offset = raw.Int32Constant(index * sizeof(CType));
return raw.Load(MachineTypeForC<CType>(), base, offset);
}
Node* StoreOutput(RawMachineAssembler& raw, Node* value) {
Node* base = raw.PointerConstant(&output);
Node* offset = raw.Int32Constant(0);
return raw.Store(MachineTypeForC<CType>().representation(), base, offset,
value, kNoWriteBarrier);
}
// Computes the next set of inputs by updating the {input} array.
void Mutate();
void Reset() { memset(input, 0, sizeof(input)); }
int count_;
int seed_;
CType input[kMaxParamCount];
CType output;
};
template <>
void ArgsBuffer<int32_t>::Mutate() {
uint32_t base = 1111111111u * seed_;
for (int j = 0; j < count_ && j < kMaxParamCount; j++) {
input[j] = static_cast<int32_t>(256 + base + j + seed_ * 13);
}
output = -1;
seed_++;
}
template <>
void ArgsBuffer<int64_t>::Mutate() {
uint64_t base = 11111111111111111ull * seed_;
for (int j = 0; j < count_ && j < kMaxParamCount; j++) {
input[j] = static_cast<int64_t>(256 + base + j + seed_ * 13);
}
output = -1;
seed_++;
}
template <>
void ArgsBuffer<float32>::Mutate() {
float64 base = -33.25 * seed_;
for (int j = 0; j < count_ && j < kMaxParamCount; j++) {
input[j] = 256 + base + j + seed_ * 13;
}
output = std::numeric_limits<float32>::quiet_NaN();
seed_++;
}
template <>
void ArgsBuffer<float64>::Mutate() {
float64 base = -111.25 * seed_;
for (int j = 0; j < count_ && j < kMaxParamCount; j++) {
input[j] = 256 + base + j + seed_ * 13;
}
output = std::numeric_limits<float64>::quiet_NaN();
seed_++;
}
int ParamCount(CallDescriptor* desc) {
return static_cast<int>(desc->ParameterCount());
}
template <typename CType>
class Computer {
public:
static void Run(CallDescriptor* desc,
void (*build)(CallDescriptor*, RawMachineAssembler&),
CType (*compute)(CallDescriptor*, CType* inputs),
int seed = 1) {
int num_params = ParamCount(desc);
CHECK_LE(num_params, kMaxParamCount);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
Handle<Code> inner = Handle<Code>::null();
{
// Build the graph for the computation.
Zone zone(isolate->allocator());
Graph graph(&zone);
RawMachineAssembler raw(isolate, &graph, desc);
build(desc, raw);
inner = CompileGraph("Compute", desc, &graph, raw.Export());
}
CSignature0<int32_t> csig;
ArgsBuffer<CType> io(num_params, seed);
{
// constant mode.
Handle<Code> wrapper = Handle<Code>::null();
{
// Wrap the above code with a callable function that passes constants.
Zone zone(isolate->allocator());
Graph graph(&zone);
CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig);
RawMachineAssembler raw(isolate, &graph, cdesc);
Node* target = raw.HeapConstant(inner);
Node** args = zone.NewArray<Node*>(num_params);
for (int i = 0; i < num_params; i++) {
args[i] = io.MakeConstant(raw, io.input[i]);
}
Node* call = raw.CallN(desc, target, args);
Node* store = io.StoreOutput(raw, call);
USE(store);
raw.Return(raw.Int32Constant(seed));
wrapper =
CompileGraph("Compute-wrapper-const", cdesc, &graph, raw.Export());
}
CodeRunner<int32_t> runnable(isolate, wrapper, &csig);
// Run the code, checking it against the reference.
CType expected = compute(desc, io.input);
int32_t check_seed = runnable.Call();
CHECK_EQ(seed, check_seed);
CHECK_EQ(expected, io.output);
}
{
// buffer mode.
Handle<Code> wrapper = Handle<Code>::null();
{
// Wrap the above code with a callable function that loads from {input}.
Zone zone(isolate->allocator());
Graph graph(&zone);
CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig);
RawMachineAssembler raw(isolate, &graph, cdesc);
Node* base = raw.PointerConstant(io.input);
Node* target = raw.HeapConstant(inner);
Node** args = zone.NewArray<Node*>(kMaxParamCount);
for (int i = 0; i < num_params; i++) {
args[i] = io.LoadInput(raw, base, i);
}
Node* call = raw.CallN(desc, target, args);
Node* store = io.StoreOutput(raw, call);
USE(store);
raw.Return(raw.Int32Constant(seed));
wrapper = CompileGraph("Compute-wrapper", cdesc, &graph, raw.Export());
}
CodeRunner<int32_t> runnable(isolate, wrapper, &csig);
// Run the code, checking it against the reference.
for (int i = 0; i < 5; i++) {
CType expected = compute(desc, io.input);
int32_t check_seed = runnable.Call();
CHECK_EQ(seed, check_seed);
CHECK_EQ(expected, io.output);
io.Mutate();
}
}
}
};
} // namespace
static void TestInt32Sub(CallDescriptor* desc) {
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
Zone zone(isolate->allocator());
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 = desc->GetMachineSignature(&zone);
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(isolate->allocator());
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(MachineRepresentation::kWord32, base, offset, raw.Parameter(i),
kNoWriteBarrier);
}
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(isolate->allocator());
Graph graph(&zone);
CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig);
RawMachineAssembler raw(isolate, &graph, cdesc);
Node* base = raw.PointerConstant(input);
Node* target = raw.HeapConstant(inner);
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(MachineType::Int32(), 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]);
}
}
}
static void Test_RunInt32SubWithRet(int retreg) {
Int32Signature sig(2);
base::AccountingAllocator allocator;
Zone zone(&allocator);
RegisterPairs pairs;
while (pairs.More()) {
int parray[2];
int rarray[] = {retreg};
pairs.Next(&parray[0], &parray[1], false);
Allocator params(parray, 2, nullptr, 0);
Allocator rets(rarray, 1, nullptr, 0);
RegisterConfig config(params, rets);
CallDescriptor* desc = config.Create(&zone, &sig);
TestInt32Sub(desc);
}
}
// Separate tests for parallelization.
#define TEST_INT32_SUB_WITH_RET(x) \
TEST(Run_Int32Sub_all_allocatable_pairs_##x) { \
if (x < Register::kNumRegisters && \
GetRegConfig()->IsAllocatableGeneralCode(x)) { \
Test_RunInt32SubWithRet(x); \
} \
}
TEST_INT32_SUB_WITH_RET(0)
TEST_INT32_SUB_WITH_RET(1)
TEST_INT32_SUB_WITH_RET(2)
TEST_INT32_SUB_WITH_RET(3)
TEST_INT32_SUB_WITH_RET(4)
TEST_INT32_SUB_WITH_RET(5)
TEST_INT32_SUB_WITH_RET(6)
TEST_INT32_SUB_WITH_RET(7)
TEST_INT32_SUB_WITH_RET(8)
TEST_INT32_SUB_WITH_RET(9)
TEST_INT32_SUB_WITH_RET(10)
TEST_INT32_SUB_WITH_RET(11)
TEST_INT32_SUB_WITH_RET(12)
TEST_INT32_SUB_WITH_RET(13)
TEST_INT32_SUB_WITH_RET(14)
TEST_INT32_SUB_WITH_RET(15)
TEST_INT32_SUB_WITH_RET(16)
TEST_INT32_SUB_WITH_RET(17)
TEST_INT32_SUB_WITH_RET(18)
TEST_INT32_SUB_WITH_RET(19)
TEST(Run_Int32Sub_all_allocatable_single) {
Int32Signature sig(2);
RegisterPairs pairs;
while (pairs.More()) {
base::AccountingAllocator allocator;
Zone zone(&allocator);
int parray[1];
int rarray[1];
pairs.Next(&rarray[0], &parray[0], true);
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) {
Int32Signature sig(20);
RegisterPairs pairs;
while (pairs.More()) {
base::AccountingAllocator allocator;
Zone zone(&allocator);
int parray[2];
int rarray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)};
pairs.Next(&parray[0], &parray[1], false);
Allocator params(parray, 2, nullptr, 0);
Allocator rets(rarray, 1, nullptr, 0);
RegisterConfig config(params, rets);
CallDescriptor* desc = config.Create(&zone, &sig);
CopyTwentyInt32(desc);
}
}
template <typename CType>
static void Run_Computation(
CallDescriptor* desc, void (*build)(CallDescriptor*, RawMachineAssembler&),
CType (*compute)(CallDescriptor*, CType* inputs), int seed = 1) {
Computer<CType>::Run(desc, build, compute, seed);
}
static uint32_t coeff[] = {1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
79, 83, 89, 97, 101, 103, 107, 109, 113};
static void Build_Int32_WeightedSum(CallDescriptor* desc,
RawMachineAssembler& raw) {
Node* result = raw.Int32Constant(0);
for (int i = 0; i < ParamCount(desc); i++) {
Node* term = raw.Int32Mul(raw.Parameter(i), raw.Int32Constant(coeff[i]));
result = raw.Int32Add(result, term);
}
raw.Return(result);
}
static int32_t Compute_Int32_WeightedSum(CallDescriptor* desc, int32_t* input) {
uint32_t result = 0;
for (int i = 0; i < ParamCount(desc); i++) {
result += static_cast<uint32_t>(input[i]) * coeff[i];
}
return static_cast<int32_t>(result);
}
static void Test_Int32_WeightedSum_of_size(int count) {
Int32Signature sig(count);
for (int p0 = 0; p0 < Register::kNumRegisters; p0++) {
if (GetRegConfig()->IsAllocatableGeneralCode(p0)) {
base::AccountingAllocator allocator;
Zone zone(&allocator);
int parray[] = {p0};
int rarray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)};
Allocator params(parray, 1, nullptr, 0);
Allocator rets(rarray, 1, nullptr, 0);
RegisterConfig config(params, rets);
CallDescriptor* desc = config.Create(&zone, &sig);
Run_Computation<int32_t>(desc, Build_Int32_WeightedSum,
Compute_Int32_WeightedSum, 257 + count);
}
}
}
// Separate tests for parallelization.
#define TEST_INT32_WEIGHTEDSUM(x) \
TEST(Run_Int32_WeightedSum_##x) { Test_Int32_WeightedSum_of_size(x); }
TEST_INT32_WEIGHTEDSUM(1)
TEST_INT32_WEIGHTEDSUM(2)
TEST_INT32_WEIGHTEDSUM(3)
TEST_INT32_WEIGHTEDSUM(4)
TEST_INT32_WEIGHTEDSUM(5)
TEST_INT32_WEIGHTEDSUM(7)
TEST_INT32_WEIGHTEDSUM(9)
TEST_INT32_WEIGHTEDSUM(11)
TEST_INT32_WEIGHTEDSUM(17)
TEST_INT32_WEIGHTEDSUM(19)
template <int which>
static void Build_Select(CallDescriptor* desc, RawMachineAssembler& raw) {
raw.Return(raw.Parameter(which));
}
template <typename CType, int which>
static CType Compute_Select(CallDescriptor* desc, CType* inputs) {
return inputs[which];
}
template <typename CType, int which>
static void RunSelect(CallDescriptor* desc) {
int count = ParamCount(desc);
if (count <= which) return;
Run_Computation<CType>(desc, Build_Select<which>,
Compute_Select<CType, which>,
1044 + which + 3 * sizeof(CType));
}
template <int which>
void Test_Int32_Select() {
int parray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)};
int rarray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)};
Allocator params(parray, 1, nullptr, 0);
Allocator rets(rarray, 1, nullptr, 0);
RegisterConfig config(params, rets);
base::AccountingAllocator allocator;
Zone zone(&allocator);
for (int i = which + 1; i <= 64; i++) {
Int32Signature sig(i);
CallDescriptor* desc = config.Create(&zone, &sig);
RunSelect<int32_t, which>(desc);
}
}
// Separate tests for parallelization.
#define TEST_INT32_SELECT(x) \
TEST(Run_Int32_Select_##x) { Test_Int32_Select<x>(); }
TEST_INT32_SELECT(0)
TEST_INT32_SELECT(1)
TEST_INT32_SELECT(2)
TEST_INT32_SELECT(3)
TEST_INT32_SELECT(4)
TEST_INT32_SELECT(5)
TEST_INT32_SELECT(6)
TEST_INT32_SELECT(11)
TEST_INT32_SELECT(15)
TEST_INT32_SELECT(19)
TEST_INT32_SELECT(45)
TEST_INT32_SELECT(62)
TEST_INT32_SELECT(63)
TEST(Int64Select_registers) {
if (GetRegConfig()->num_allocatable_general_registers() < 2) return;
if (kPointerSize < 8) return; // TODO(titzer): int64 on 32-bit platforms
int rarray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)};
ArgsBuffer<int64_t>::Sig sig(2);
RegisterPairs pairs;
base::AccountingAllocator allocator;
Zone zone(&allocator);
while (pairs.More()) {
int parray[2];
pairs.Next(&parray[0], &parray[1], false);
Allocator params(parray, 2, nullptr, 0);
Allocator rets(rarray, 1, nullptr, 0);
RegisterConfig config(params, rets);
CallDescriptor* desc = config.Create(&zone, &sig);
RunSelect<int64_t, 0>(desc);
RunSelect<int64_t, 1>(desc);
}
}
TEST(Float32Select_registers) {
if (GetRegConfig()->num_allocatable_double_registers() < 2) {
return;
}
int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
ArgsBuffer<float32>::Sig sig(2);
Float32RegisterPairs pairs;
base::AccountingAllocator allocator;
Zone zone(&allocator);
while (pairs.More()) {
int parray[2];
pairs.Next(&parray[0], &parray[1], false);
Allocator params(nullptr, 0, parray, 2);
Allocator rets(nullptr, 0, rarray, 1);
RegisterConfig config(params, rets);
CallDescriptor* desc = config.Create(&zone, &sig);
RunSelect<float32, 0>(desc);
RunSelect<float32, 1>(desc);
}
}
TEST(Float64Select_registers) {
if (GetRegConfig()->num_allocatable_double_registers() < 2) return;
if (GetRegConfig()->num_allocatable_general_registers() < 2) return;
int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
ArgsBuffer<float64>::Sig sig(2);
Float64RegisterPairs pairs;
base::AccountingAllocator allocator;
Zone zone(&allocator);
while (pairs.More()) {
int parray[2];
pairs.Next(&parray[0], &parray[1], false);
Allocator params(nullptr, 0, parray, 2);
Allocator rets(nullptr, 0, rarray, 1);
RegisterConfig config(params, rets);
CallDescriptor* desc = config.Create(&zone, &sig);
RunSelect<float64, 0>(desc);
RunSelect<float64, 1>(desc);
}
}
TEST(Float32Select_stack_params_return_reg) {
int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
Allocator params(nullptr, 0, nullptr, 0);
Allocator rets(nullptr, 0, rarray, 1);
RegisterConfig config(params, rets);
base::AccountingAllocator allocator;
Zone zone(&allocator);
for (int count = 1; count < 6; count++) {
ArgsBuffer<float32>::Sig sig(count);
CallDescriptor* desc = config.Create(&zone, &sig);
RunSelect<float32, 0>(desc);
RunSelect<float32, 1>(desc);
RunSelect<float32, 2>(desc);
RunSelect<float32, 3>(desc);
RunSelect<float32, 4>(desc);
RunSelect<float32, 5>(desc);
}
}
TEST(Float64Select_stack_params_return_reg) {
int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
Allocator params(nullptr, 0, nullptr, 0);
Allocator rets(nullptr, 0, rarray, 1);
RegisterConfig config(params, rets);
base::AccountingAllocator allocator;
Zone zone(&allocator);
for (int count = 1; count < 6; count++) {
ArgsBuffer<float64>::Sig sig(count);
CallDescriptor* desc = config.Create(&zone, &sig);
RunSelect<float64, 0>(desc);
RunSelect<float64, 1>(desc);
RunSelect<float64, 2>(desc);
RunSelect<float64, 3>(desc);
RunSelect<float64, 4>(desc);
RunSelect<float64, 5>(desc);
}
}
template <typename CType, int which>
static void Build_Select_With_Call(CallDescriptor* desc,
RawMachineAssembler& raw) {
Handle<Code> inner = Handle<Code>::null();
int num_params = ParamCount(desc);
CHECK_LE(num_params, kMaxParamCount);
{
Isolate* isolate = CcTest::InitIsolateOnce();
// Build the actual select.
Zone zone(isolate->allocator());
Graph graph(&zone);
RawMachineAssembler raw(isolate, &graph, desc);
raw.Return(raw.Parameter(which));
inner = CompileGraph("Select-indirection", desc, &graph, raw.Export());
CHECK(!inner.is_null());
CHECK(inner->IsCode());
}
{
// Build a call to the function that does the select.
Node* target = raw.HeapConstant(inner);
Node** args = raw.zone()->NewArray<Node*>(num_params);
for (int i = 0; i < num_params; i++) {
args[i] = raw.Parameter(i);
}
Node* call = raw.CallN(desc, target, args);
raw.Return(call);
}
}
TEST(Float64StackParamsToStackParams) {
int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
Allocator params(nullptr, 0, nullptr, 0);
Allocator rets(nullptr, 0, rarray, 1);
base::AccountingAllocator allocator;
Zone zone(&allocator);
ArgsBuffer<float64>::Sig sig(2);
RegisterConfig config(params, rets);
CallDescriptor* desc = config.Create(&zone, &sig);
Run_Computation<float64>(desc, Build_Select_With_Call<float64, 0>,
Compute_Select<float64, 0>, 1098);
Run_Computation<float64>(desc, Build_Select_With_Call<float64, 1>,
Compute_Select<float64, 1>, 1099);
}
void MixedParamTest(int start) {
if (GetRegConfig()->num_double_registers() < 2) return;
// TODO(titzer): mix in 64-bit types on all platforms when supported.
#if V8_TARGET_ARCH_32_BIT
static MachineType types[] = {
MachineType::Int32(), MachineType::Float32(), MachineType::Float64(),
MachineType::Int32(), MachineType::Float64(), MachineType::Float32(),
MachineType::Float32(), MachineType::Float64(), MachineType::Int32(),
MachineType::Float32(), MachineType::Int32(), MachineType::Float64(),
MachineType::Float64(), MachineType::Float32(), MachineType::Int32(),
MachineType::Float64(), MachineType::Int32(), MachineType::Float32()};
#else
static MachineType types[] = {
MachineType::Int32(), MachineType::Int64(), MachineType::Float32(),
MachineType::Float64(), MachineType::Int32(), MachineType::Float64(),
MachineType::Float32(), MachineType::Int64(), MachineType::Int64(),
MachineType::Float32(), MachineType::Float32(), MachineType::Int32(),
MachineType::Float64(), MachineType::Float64(), MachineType::Int64(),
MachineType::Int32(), MachineType::Float64(), MachineType::Int32(),
MachineType::Float32()};
#endif
Isolate* isolate = CcTest::InitIsolateOnce();
// Build machine signature
MachineType* params = &types[start];
const int num_params = static_cast<int>(arraysize(types) - start);
// Build call descriptor
int parray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0),
GetRegConfig()->GetAllocatableGeneralCode(1)};
int rarray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0)};
int parray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0),
GetRegConfig()->GetAllocatableDoubleCode(1)};
int rarray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
Allocator palloc(parray_gp, 2, parray_fp, 2);
Allocator ralloc(rarray_gp, 1, rarray_fp, 1);
RegisterConfig config(palloc, ralloc);
for (int which = 0; which < num_params; which++) {
base::AccountingAllocator allocator;
Zone zone(&allocator);
HandleScope scope(isolate);
MachineSignature::Builder builder(&zone, 1, num_params);
builder.AddReturn(params[which]);
for (int j = 0; j < num_params; j++) builder.AddParam(params[j]);
MachineSignature* sig = builder.Build();
CallDescriptor* desc = config.Create(&zone, sig);
Handle<Code> select;
{
// build the select.
Zone zone(&allocator);
Graph graph(&zone);
RawMachineAssembler raw(isolate, &graph, desc);
raw.Return(raw.Parameter(which));
select = CompileGraph("Compute", desc, &graph, raw.Export());
}
{
// call the select.
Handle<Code> wrapper = Handle<Code>::null();
int32_t expected_ret;
char bytes[kDoubleSize];
V8_ALIGNED(8) char output[kDoubleSize];
int expected_size = 0;
CSignature0<int32_t> csig;
{
// Wrap the select code with a callable function that passes constants.
Zone zone(&allocator);
Graph graph(&zone);
CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig);
RawMachineAssembler raw(isolate, &graph, cdesc);
Node* target = raw.HeapConstant(select);
Node** args = zone.NewArray<Node*>(num_params);
int64_t constant = 0x0102030405060708;
for (int i = 0; i < num_params; i++) {
MachineType param_type = sig->GetParam(i);
Node* konst = nullptr;
if (param_type == MachineType::Int32()) {
int32_t value[] = {static_cast<int32_t>(constant)};
konst = raw.Int32Constant(value[0]);
if (i == which) memcpy(bytes, value, expected_size = 4);
}
if (param_type == MachineType::Int64()) {
int64_t value[] = {static_cast<int64_t>(constant)};
konst = raw.Int64Constant(value[0]);
if (i == which) memcpy(bytes, value, expected_size = 8);
}
if (param_type == MachineType::Float32()) {
float32 value[] = {static_cast<float32>(constant)};
konst = raw.Float32Constant(value[0]);
if (i == which) memcpy(bytes, value, expected_size = 4);
}
if (param_type == MachineType::Float64()) {
float64 value[] = {static_cast<float64>(constant)};
konst = raw.Float64Constant(value[0]);
if (i == which) memcpy(bytes, value, expected_size = 8);
}
CHECK_NOT_NULL(konst);
args[i] = konst;
constant += 0x1010101010101010;
}
Node* call = raw.CallN(desc, target, args);
Node* store =
raw.StoreToPointer(output, sig->GetReturn().representation(), call);
USE(store);
expected_ret = static_cast<int32_t>(constant);
raw.Return(raw.Int32Constant(expected_ret));
wrapper = CompileGraph("Select-mixed-wrapper-const", cdesc, &graph,
raw.Export());
}
CodeRunner<int32_t> runnable(isolate, wrapper, &csig);
CHECK_EQ(expected_ret, runnable.Call());
for (int i = 0; i < expected_size; i++) {
CHECK_EQ(static_cast<int>(bytes[i]), static_cast<int>(output[i]));
}
}
}
}
TEST(MixedParams_0) { MixedParamTest(0); }
TEST(MixedParams_1) { MixedParamTest(1); }
TEST(MixedParams_2) { MixedParamTest(2); }
TEST(MixedParams_3) { MixedParamTest(3); }
template <typename T>
void TestStackSlot(MachineType slot_type, T expected) {
// Test: Generate with a function f which reserves a stack slot, call an inner
// function g from f which writes into the stack slot of f.
if (GetRegConfig()->num_allocatable_double_registers() < 2) return;
Isolate* isolate = CcTest::InitIsolateOnce();
// Lots of code to generate the build descriptor for the inner function.
int parray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0),
GetRegConfig()->GetAllocatableGeneralCode(1)};
int rarray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0)};
int parray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0),
GetRegConfig()->GetAllocatableDoubleCode(1)};
int rarray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
Allocator palloc(parray_gp, 2, parray_fp, 2);
Allocator ralloc(rarray_gp, 1, rarray_fp, 1);
RegisterConfig config(palloc, ralloc);
Zone zone(isolate->allocator());
HandleScope scope(isolate);
MachineSignature::Builder builder(&zone, 1, 12);
builder.AddReturn(MachineType::Int32());
for (int i = 0; i < 10; i++) {
builder.AddParam(MachineType::Int32());
}
builder.AddParam(slot_type);
builder.AddParam(MachineType::Pointer());
MachineSignature* sig = builder.Build();
CallDescriptor* desc = config.Create(&zone, sig);
// Create inner function g. g has lots of parameters so that they are passed
// over the stack.
Handle<Code> inner;
Graph graph(&zone);
RawMachineAssembler g(isolate, &graph, desc);
g.Store(slot_type.representation(), g.Parameter(11), g.Parameter(10),
WriteBarrierKind::kNoWriteBarrier);
g.Return(g.Parameter(9));
inner = CompileGraph("Compute", desc, &graph, g.Export());
// Create function f with a stack slot which calls the inner function g.
BufferedRawMachineAssemblerTester<T> f(slot_type);
Node* target = f.HeapConstant(inner);
Node* stack_slot = f.StackSlot(slot_type.representation());
Node* args[12];
for (int i = 0; i < 10; i++) {
args[i] = f.Int32Constant(i);
}
args[10] = f.Parameter(0);
args[11] = stack_slot;
f.CallN(desc, target, args);
f.Return(f.Load(slot_type, stack_slot, f.IntPtrConstant(0)));
CHECK_EQ(expected, f.Call(expected));
}
TEST(RunStackSlotInt32) {
int32_t magic = 0x12345678;
TestStackSlot(MachineType::Int32(), magic);
}
#if !V8_TARGET_ARCH_32_BIT
TEST(RunStackSlotInt64) {
int64_t magic = 0x123456789abcdef0;
TestStackSlot(MachineType::Int64(), magic);
}
#endif
TEST(RunStackSlotFloat32) {
float magic = 1234.125f;
TestStackSlot(MachineType::Float32(), magic);
}
TEST(RunStackSlotFloat64) {
double magic = 3456.375;
TestStackSlot(MachineType::Float64(), magic);
}
} // namespace compiler
} // namespace internal
} // namespace v8