[wasm] Int64 lowering for return values

R=titzer@chromium.org

Change-Id: Ie8c361efb48b56dc65719f09dfc79d505e0f3459
Reviewed-on: https://chromium-review.googlesource.com/735610
Commit-Queue: Andreas Rossberg <rossberg@chromium.org>
Reviewed-by: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49000}
This commit is contained in:
Andreas Rossberg 2017-10-27 12:39:27 +02:00 committed by Commit Bot
parent 07de62ca18
commit 776d6e9d5c
9 changed files with 197 additions and 36 deletions

View File

@ -75,7 +75,24 @@ void Int64Lowering::LowerGraph() {
namespace {
static int GetParameterIndexAfterLowering(
int GetReturnIndexAfterLowering(
CallDescriptor* descriptor, int old_index) {
int result = old_index;
for (int i = 0; i < old_index; i++) {
if (descriptor->GetReturnType(i).representation() ==
MachineRepresentation::kWord64) {
result++;
}
}
return result;
}
int GetReturnCountAfterLowering(CallDescriptor* descriptor) {
return GetReturnIndexAfterLowering(
descriptor, static_cast<int>(descriptor->ReturnCount()));
}
int GetParameterIndexAfterLowering(
Signature<MachineRepresentation>* signature, int old_index) {
int result = old_index;
for (int i = 0; i < old_index; i++) {
@ -276,13 +293,12 @@ void Int64Lowering::LowerNode(Node* node) {
++new_index;
NodeProperties::ChangeOp(node, common()->Parameter(new_index));
Node* high_node = nullptr;
if (signature()->GetParam(old_index) ==
MachineRepresentation::kWord64) {
high_node = graph()->NewNode(common()->Parameter(new_index + 1),
graph()->start());
Node* high_node = graph()->NewNode(common()->Parameter(new_index + 1),
graph()->start());
ReplaceNode(node, node, high_node);
}
ReplaceNode(node, node, high_node);
}
break;
}
@ -313,21 +329,48 @@ void Int64Lowering::LowerNode(Node* node) {
case IrOpcode::kCall: {
CallDescriptor* descriptor =
const_cast<CallDescriptor*>(CallDescriptorOf(node->op()));
if (DefaultLowering(node) ||
(descriptor->ReturnCount() == 1 &&
descriptor->GetReturnType(0) == MachineType::Int64())) {
bool returns_require_lowering =
GetReturnCountAfterLowering(descriptor) !=
static_cast<int>(descriptor->ReturnCount());
if (DefaultLowering(node) || returns_require_lowering) {
// We have to adjust the call descriptor.
NodeProperties::ChangeOp(
node, common()->Call(GetI32WasmCallDescriptor(zone(), descriptor)));
}
if (descriptor->ReturnCount() == 1 &&
descriptor->GetReturnType(0) == MachineType::Int64()) {
// We access the additional return values through projections.
Node* low_node =
graph()->NewNode(common()->Projection(0), node, graph()->start());
Node* high_node =
graph()->NewNode(common()->Projection(1), node, graph()->start());
ReplaceNode(node, low_node, high_node);
if (returns_require_lowering) {
size_t return_arity = descriptor->ReturnCount();
if (return_arity == 1) {
// We access the additional return values through projections.
Node* low_node =
graph()->NewNode(common()->Projection(0), node, graph()->start());
Node* high_node =
graph()->NewNode(common()->Projection(1), node, graph()->start());
ReplaceNode(node, low_node, high_node);
} else {
ZoneVector<Node*> projections(return_arity, zone());
NodeProperties::CollectValueProjections(node, projections.data(),
return_arity);
for (size_t old_index = 0, new_index = 0; old_index < return_arity;
++old_index, ++new_index) {
Node* use_node = projections[old_index];
DCHECK_EQ(ProjectionIndexOf(use_node->op()), old_index);
DCHECK_EQ(GetReturnIndexAfterLowering(descriptor,
static_cast<int>(old_index)),
static_cast<int>(new_index));
if (new_index != old_index) {
NodeProperties::ChangeOp(
use_node, common()->Projection(new_index));
}
if (descriptor->GetReturnType(old_index).representation() ==
MachineRepresentation::kWord64) {
Node* high_node = graph()->NewNode(
common()->Projection(new_index + 1), node,
graph()->start());
ReplaceNode(use_node, use_node, high_node);
++new_index;
}
}
}
}
break;
}
@ -812,18 +855,6 @@ void Int64Lowering::LowerNode(Node* node) {
}
break;
}
case IrOpcode::kProjection: {
Node* call = node->InputAt(0);
DCHECK_EQ(IrOpcode::kCall, call->opcode());
CallDescriptor* descriptor =
const_cast<CallDescriptor*>(CallDescriptorOf(call->op()));
for (size_t i = 0; i < descriptor->ReturnCount(); i++) {
if (descriptor->GetReturnType(i) == MachineType::Int64()) {
UNREACHABLE(); // TODO(titzer): implement multiple i64 returns.
}
}
break;
}
case IrOpcode::kWord64ReverseBytes: {
Node* input = node->InputAt(0);
ReplaceNode(node, graph()->NewNode(machine()->Word32ReverseBytes().op(),

View File

@ -276,6 +276,23 @@ Node* NodeProperties::FindProjection(Node* node, size_t projection_index) {
}
// static
void NodeProperties::CollectValueProjections(Node* node, Node** projections,
size_t projection_count) {
#ifdef DEBUG
for (size_t index = 0; index < projection_count; ++index) {
DCHECK_NULL(projections[index]);
}
#endif
for (Edge const edge : node->use_edges()) {
if (!IsValueEdge(edge)) continue;
Node* use = edge.from();
DCHECK_EQ(IrOpcode::kProjection, use->opcode());
projections[ProjectionIndexOf(use->op())] = use;
}
}
// static
void NodeProperties::CollectControlProjections(Node* node, Node** projections,
size_t projection_count) {

View File

@ -122,6 +122,9 @@ class V8_EXPORT_PRIVATE NodeProperties final {
// Collect the output-value projection for the given output index.
static Node* FindProjection(Node* node, size_t projection_index);
// Collect the value projections from a node.
static void CollectValueProjections(Node* node, Node** proj, size_t count);
// Collect the branch-related projections from a node, such as IfTrue,
// IfFalse, IfSuccess, IfException, IfValue and IfDefault.
// - Branch: [ IfTrue, IfFalse ]

View File

@ -47,7 +47,7 @@ LinkageLocation stackloc(int i, MachineType type) {
// == ia32 ===================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS esi, eax, edx, ecx, ebx
#define GP_RETURN_REGISTERS eax, edx
#define GP_RETURN_REGISTERS eax, edx, ecx
#define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
#define FP_RETURN_REGISTERS xmm1, xmm2
@ -56,7 +56,7 @@ LinkageLocation stackloc(int i, MachineType type) {
// == x64 ====================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS rsi, rax, rdx, rcx, rbx, rdi
#define GP_RETURN_REGISTERS rax, rdx
#define GP_RETURN_REGISTERS rax, rdx, rcx
#define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
#define FP_RETURN_REGISTERS xmm1, xmm2
@ -65,7 +65,7 @@ LinkageLocation stackloc(int i, MachineType type) {
// == arm ====================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS r3, r0, r1, r2
#define GP_RETURN_REGISTERS r0, r1
#define GP_RETURN_REGISTERS r0, r1, r3
#define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
#define FP_RETURN_REGISTERS d0, d1
@ -74,7 +74,7 @@ LinkageLocation stackloc(int i, MachineType type) {
// == arm64 ====================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS x7, x0, x1, x2, x3, x4, x5, x6
#define GP_RETURN_REGISTERS x0, x1
#define GP_RETURN_REGISTERS x0, x1, x2
#define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
#define FP_RETURN_REGISTERS d0, d1
@ -101,7 +101,7 @@ LinkageLocation stackloc(int i, MachineType type) {
// == ppc & ppc64 ============================================================
// ===========================================================================
#define GP_PARAM_REGISTERS r10, r3, r4, r5, r6, r7, r8, r9
#define GP_RETURN_REGISTERS r3, r4
#define GP_RETURN_REGISTERS r3, r4, r5
#define FP_PARAM_REGISTERS d1, d2, d3, d4, d5, d6, d7, d8
#define FP_RETURN_REGISTERS d1, d2
@ -110,7 +110,7 @@ LinkageLocation stackloc(int i, MachineType type) {
// == s390x ==================================================================
// ===========================================================================
#define GP_PARAM_REGISTERS r6, r2, r3, r4, r5
#define GP_RETURN_REGISTERS r2, r3
#define GP_RETURN_REGISTERS r2, r3, r4
#define FP_PARAM_REGISTERS d0, d2, d4, d6
#define FP_RETURN_REGISTERS d0, d2, d4, d6

View File

@ -1453,7 +1453,7 @@ class ThreadImpl {
// ^ 0 ^ sp_
DCHECK_LE(dest, sp_);
DCHECK_LE(dest + arity, sp_);
if (arity) memcpy(dest, sp_ - arity, arity * sizeof(*sp_));
if (arity) memmove(dest, sp_ - arity, arity * sizeof(*sp_));
sp_ = dest + arity;
}

View File

@ -329,7 +329,7 @@ bool IsJSCompatibleSignature(const FunctionSig* sig) {
for (auto type : sig->all()) {
if (type == wasm::kWasmI64 || type == wasm::kWasmS128) return false;
}
return true;
return sig->return_count() <= 1;
}
namespace {

View File

@ -923,6 +923,25 @@ WASM_EXEC_TEST(CallI64Parameter) {
}
}
WASM_EXEC_TEST(CallI64Return) {
ValueType return_types[3]; // TODO(rossberg): support more in the future
for (int i = 0; i < 3; i++) return_types[i] = kWasmI64;
return_types[1] = kWasmI32;
FunctionSig sig(2, 1, return_types);
WasmRunner<int64_t> r(execution_mode);
// Build the target function.
WasmFunctionCompiler& t = r.NewFunction(&sig);
BUILD(t, WASM_GET_LOCAL(0), WASM_I32V(7));
// Build the first calling function.
BUILD(r,
WASM_CALL_FUNCTION(
t.function_index(), WASM_I64V(0xbcd12340000000b)), WASM_DROP);
CHECK_EQ(0xbcd12340000000b, r.Call());
}
void TestI64Binop(WasmExecutionMode execution_mode, WasmOpcode opcode,
int64_t expected, int64_t a, int64_t b) {
{

View File

@ -231,3 +231,92 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(instance.exports.main(8, 3), 5);
assertEquals(instance.exports.main(0, 3), 3);
})();
(function MultiResultTest() {
print("MultiResultTest");
let builder = new WasmModuleBuilder();
let sig_i_ii = builder.addType(kSig_i_ii);
let sig_iii_ii = builder.addType(kSig_iii_ii);
builder.addFunction("callee", kSig_iii_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprI32Sub]);
builder.addFunction("main", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprCallFunction, 0,
kExprI32Mul,
kExprI32Add])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(0, 0), 0);
assertEquals(instance.exports.main(1, 0), 1);
assertEquals(instance.exports.main(2, 0), 2);
assertEquals(instance.exports.main(0, 1), -1);
assertEquals(instance.exports.main(0, 2), -4);
assertEquals(instance.exports.main(3, 4), -1);
assertEquals(instance.exports.main(4, 3), 7);
})();
(function MultiReturnTest() {
print("MultiReturnTest");
let builder = new WasmModuleBuilder();
let sig_i_i = builder.addType(kSig_i_i);
let sig_ii_i = builder.addType(kSig_ii_i);
builder.addFunction("callee", kSig_ii_i)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprI32Add,
kExprReturn]);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprCallFunction, 0,
kExprI32Mul])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(0), 0);
assertEquals(instance.exports.main(1), 2);
assertEquals(instance.exports.main(2), 8);
assertEquals(instance.exports.main(10), 200);
})();
(function MultiBrReturnTest() {
print("MultiBrReturnTest");
let builder = new WasmModuleBuilder();
let sig_i_i = builder.addType(kSig_i_i);
let sig_ii_i = builder.addType(kSig_ii_i);
builder.addFunction("callee", kSig_ii_i)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprGetLocal, 0,
kExprI32Add,
kExprBr, 0]);
builder.addFunction("main", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprCallFunction, 0,
kExprI32Mul])
.exportAs("main");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module);
assertEquals(instance.exports.main(0), 0);
assertEquals(instance.exports.main(1), 2);
assertEquals(instance.exports.main(2), 8);
assertEquals(instance.exports.main(10), 200);
})();

View File

@ -126,7 +126,9 @@ let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]);
let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]);
let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]);
let kSig_iii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]);
let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
let kSig_v_f = makeSig([kWasmF32], []);
let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);