[wasm][turbofan] Enable tail calls in inlined functions

Bug: v8:12166
Change-Id: If77ecea8102e4ba5d98d3e1e6700e9c1efaa319a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3144913
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76787}
This commit is contained in:
Manos Koukoutos 2021-09-13 09:23:09 +00:00 committed by V8 LUCI CQ
parent 2255e418b8
commit e8708fe355
3 changed files with 71 additions and 16 deletions

View File

@ -34,12 +34,11 @@ Reduction WasmInliner::ReduceCall(Node* call) {
if (static_cast<uint32_t>(info.value()) != inlinee_index_) return NoChange();
CHECK_LT(inlinee_index_, module()->functions.size());
const wasm::WasmFunction* function = &module()->functions[inlinee_index_];
base::Vector<const byte> function_bytes =
wire_bytes_->GetCode(function->code);
const wasm::FunctionBody inlinee_body(function->sig, function->code.offset(),
function_bytes.begin(),
function_bytes.end());
wire_bytes_->GetCode(inlinee()->code);
const wasm::FunctionBody inlinee_body(
inlinee()->sig, inlinee()->code.offset(), function_bytes.begin(),
function_bytes.end());
wasm::WasmFeatures detected;
WasmGraphBuilder builder(env_, zone(), mcgraph_, inlinee_body.sig, spt_);
std::vector<WasmLoopInfo> infos;
@ -111,14 +110,44 @@ Reduction WasmInliner::InlineCall(Node* call, Node* callee_start,
NodeProperties::MergeControlToEnd(graph(), common(), input);
Revisit(graph()->end());
break;
case IrOpcode::kTailCall:
// TODO(12166): A tail call in the inlined function has to be
// transformed into a regular call in the caller function.
UNIMPLEMENTED();
case IrOpcode::kTailCall: {
// A tail call in the inlined function has to be transformed into a
// regular call, and then returned from the inlinee. It will then be
// handled like any other return.
auto descriptor = CallDescriptorOf(input->op());
NodeProperties::ChangeOp(input, common()->Call(descriptor));
int return_arity = static_cast<int>(inlinee()->sig->return_count());
NodeVector return_inputs(zone());
// The first input of a return node is always the 0 constant.
return_inputs.push_back(graph()->NewNode(common()->Int32Constant(0)));
if (return_arity == 1) {
return_inputs.push_back(input);
} else if (return_arity > 1) {
for (int i = 0; i < return_arity; i++) {
return_inputs.push_back(
graph()->NewNode(common()->Projection(i), input, input));
}
}
// Add effect and control inputs.
return_inputs.push_back(input->op()->EffectOutputCount() > 0
? input
: NodeProperties::GetEffectInput(input));
return_inputs.push_back(input->op()->ControlOutputCount() > 0
? input
: NodeProperties::GetControlInput(input));
Node* ret = graph()->NewNode(common()->Return(return_arity),
static_cast<int>(return_inputs.size()),
return_inputs.data());
return_nodes.push_back(ret);
break;
}
default:
UNREACHABLE();
}
}
callee_end->Kill();
if (return_nodes.size() > 0) {
int const return_count = static_cast<int>(return_nodes.size());
@ -150,14 +179,14 @@ Reduction WasmInliner::InlineCall(Node* call, Node* callee_start,
ith_values.push_back(control_output);
// Find the correct machine representation for the return values from the
// inlinee signature.
const wasm::WasmFunction* function = &module()->functions[inlinee_index_];
MachineRepresentation repr =
function->sig->GetReturn(i).machine_representation();
inlinee()->sig->GetReturn(i).machine_representation();
Node* ith_value_output = graph()->NewNode(
common()->Phi(repr, return_count),
static_cast<int>(ith_values.size()), &ith_values.front());
values.push_back(ith_value_output);
}
for (Node* return_node : return_nodes) return_node->Kill();
if (return_arity == 0) {
// Void function, no value uses.
@ -190,6 +219,10 @@ Reduction WasmInliner::InlineCall(Node* call, Node* callee_start,
const wasm::WasmModule* WasmInliner::module() const { return env_->module; }
const wasm::WasmFunction* WasmInliner::inlinee() const {
return &module()->functions[inlinee_index_];
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -18,6 +18,7 @@ namespace internal {
namespace wasm {
struct CompilationEnv;
struct WasmModule;
struct WasmFunction;
class WireBytesStorage;
} // namespace wasm
@ -58,6 +59,7 @@ class WasmInliner final : public AdvancedReducer {
Graph* graph() const { return mcgraph_->graph(); }
MachineGraph* mcgraph() const { return mcgraph_; }
const wasm::WasmModule* module() const;
const wasm::WasmFunction* inlinee() const;
Reduction ReduceCall(Node* call);
Reduction InlineCall(Node* call, Node* callee_start, Node* callee_end);

View File

@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --wasm-inlining --no-liftoff
// Flags: --wasm-inlining --no-liftoff --experimental-wasm-return-call
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
// TODO(12166): Consider running tests with --trace-wasm and inspecting their
// output.
// output, or implementing testing infrastructure with --allow-natives-syntax.
(function SimpleInliningTest() {
let builder = new WasmModuleBuilder();
@ -22,7 +22,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.exportAs("main");
let instance = builder.instantiate();
assertEquals(instance.exports.main(10), 14);
assertEquals(14, instance.exports.main(10));
})();
(function MultiReturnTest() {
@ -38,7 +38,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.exportAs("main");
let instance = builder.instantiate();
assertEquals(instance.exports.main(10), 9 * 11);
assertEquals(9 * 11, instance.exports.main(10));
})();
(function NoReturnTest() {
@ -55,7 +55,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
.exportAs("main");
let instance = builder.instantiate();
assertEquals(instance.exports.main(10), 10);
assertEquals(10, instance.exports.main(10));
})();
(function InfiniteLoopTest() {
@ -75,3 +75,23 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
builder.instantiate();
})();
(function TailCallInCalleeTest() {
let builder = new WasmModuleBuilder();
// f(x) = g(x - 1)
let callee = builder.addFunction("callee", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub,
kExprReturnCall, 1]);
// g(x) = x * 2
builder.addFunction("inner_callee", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprI32Const, 2, kExprI32Mul]);
// h(x) = f(x) + 5
builder.addFunction("main", kSig_i_i)
.addBody([kExprLocalGet, 0, kExprCallFunction, callee.index,
kExprI32Const, 5, kExprI32Add])
.exportAs("main");
let instance = builder.instantiate();
assertEquals(23, instance.exports.main(10));
})();