52702e55aa
BUG= Review-Url: https://codereview.chromium.org/2586903002 Cr-Commit-Position: refs/heads/master@{#41798}
1230 lines
40 KiB
C++
1230 lines
40 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(),
|
|
Code::ComputeFlags(Code::STUB));
|
|
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(), ZONE_NAME);
|
|
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* zero = b.graph()->NewNode(b.common()->Int32Constant(0));
|
|
Node* ret =
|
|
b.graph()->NewNode(b.common()->Return(), zero, 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(), ZONE_NAME);
|
|
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(), ZONE_NAME);
|
|
Graph graph(&zone);
|
|
CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig);
|
|
RawMachineAssembler raw(isolate, &graph, cdesc);
|
|
Node* target = raw.HeapConstant(inner);
|
|
Node** inputs = zone.NewArray<Node*>(num_params + 1);
|
|
int input_count = 0;
|
|
inputs[input_count++] = target;
|
|
for (int i = 0; i < num_params; i++) {
|
|
inputs[input_count++] = io.MakeConstant(raw, io.input[i]);
|
|
}
|
|
|
|
Node* call = raw.CallN(desc, input_count, inputs);
|
|
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(), ZONE_NAME);
|
|
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** inputs = zone.NewArray<Node*>(kMaxParamCount + 1);
|
|
int input_count = 0;
|
|
inputs[input_count++] = target;
|
|
for (int i = 0; i < num_params; i++) {
|
|
inputs[input_count++] = io.LoadInput(raw, base, i);
|
|
}
|
|
|
|
Node* call = raw.CallN(desc, input_count, inputs);
|
|
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(), ZONE_NAME);
|
|
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* zero = b.graph()->NewNode(b.common()->Int32Constant(0));
|
|
Node* ret =
|
|
b.graph()->NewNode(b.common()->Return(), zero, 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(), ZONE_NAME);
|
|
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(), ZONE_NAME);
|
|
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** inputs = zone.NewArray<Node*>(kNumParams + 1);
|
|
int input_count = 0;
|
|
inputs[input_count++] = target;
|
|
for (int i = 0; i < kNumParams; i++) {
|
|
Node* offset = raw.Int32Constant(i * sizeof(int32_t));
|
|
inputs[input_count++] = raw.Load(MachineType::Int32(), base, offset);
|
|
}
|
|
|
|
Node* call = raw.CallN(desc, input_count, inputs);
|
|
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);
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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()) {
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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()) {
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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)) {
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
|
|
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);
|
|
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
|
|
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;
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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;
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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;
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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);
|
|
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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);
|
|
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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(), ZONE_NAME);
|
|
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** inputs = raw.zone()->NewArray<Node*>(num_params + 1);
|
|
int input_count = 0;
|
|
inputs[input_count++] = target;
|
|
for (int i = 0; i < num_params; i++) {
|
|
inputs[input_count++] = raw.Parameter(i);
|
|
}
|
|
|
|
Node* call = raw.CallN(desc, input_count, inputs);
|
|
raw.Return(call);
|
|
}
|
|
}
|
|
|
|
|
|
TEST(Float64StackParamsToStackParams) {
|
|
int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)};
|
|
Allocator params(nullptr, 0, nullptr, 0);
|
|
Allocator rets(nullptr, 0, rarray, 1);
|
|
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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++) {
|
|
v8::internal::AccountingAllocator allocator;
|
|
Zone zone(&allocator, ZONE_NAME);
|
|
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, ZONE_NAME);
|
|
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, ZONE_NAME);
|
|
Graph graph(&zone);
|
|
CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig);
|
|
RawMachineAssembler raw(isolate, &graph, cdesc);
|
|
Node* target = raw.HeapConstant(select);
|
|
Node** inputs = zone.NewArray<Node*>(num_params + 1);
|
|
int input_count = 0;
|
|
inputs[input_count++] = target;
|
|
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);
|
|
|
|
inputs[input_count++] = konst;
|
|
constant += 0x1010101010101010;
|
|
}
|
|
|
|
Node* call = raw.CallN(desc, input_count, inputs);
|
|
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(), ZONE_NAME);
|
|
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* nodes[14];
|
|
int input_count = 0;
|
|
nodes[input_count++] = target;
|
|
for (int i = 0; i < 10; i++) {
|
|
nodes[input_count++] = f.Int32Constant(i);
|
|
}
|
|
nodes[input_count++] = f.Parameter(0);
|
|
nodes[input_count++] = stack_slot;
|
|
|
|
f.CallN(desc, input_count, nodes);
|
|
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
|