[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:
parent
2255e418b8
commit
e8708fe355
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user