5319b50c85
This is preparation for using TF to create builtins that handle variable number of arguments and have to remove these arguments dynamically from the stack upon return. The gist of the changes: - Added a second argument to the Return node which specifies the number of stack slots to pop upon return in addition to those specified by the Linkage of the compiled function. - Removed Tail -> Non-Tail fallback in the instruction selector. Since TF now should handles all tail-call cases except where the return value type differs, this fallback was not really useful and in fact caused unexpected behavior with variable sized argument popping, since it wasn't possible to materialize a Return node with the right pop count from the TailCall without additional context. - Modified existing Return generation to pass a constant zero as the additional pop argument since the variable pop functionality LOG=N Review-Url: https://codereview.chromium.org/2446543002 Cr-Commit-Position: refs/heads/master@{#40678}
1218 lines
39 KiB
C++
1218 lines
39 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** 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(), 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** 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(), 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** 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);
|
|
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** 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);
|
|
|
|
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** 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(), 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* 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
|