[wasm][tail-call] Fix CanTailCall check

The CanTailCall check only passes if the return locations are the
same in the caller and the callee. However, stack returns are expected
to be at a different offset depending on the stack space reserved for
parameters.

R=clemensb@chromium.org

Bug: v8:7431
Change-Id: Iaac15fce889d6cd7d1ac88f320a872202281fb5a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2289789
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68933}
This commit is contained in:
Thibaud Michaud 2020-07-10 11:49:15 +02:00 committed by Commit Bot
parent 9df54e0767
commit 69553feaab
2 changed files with 53 additions and 2 deletions

View File

@ -125,10 +125,18 @@ int CallDescriptor::GetTaggedParameterSlots() const {
bool CallDescriptor::CanTailCall(const CallDescriptor* callee) const {
if (ReturnCount() != callee->ReturnCount()) return false;
const int stack_param_delta = callee->GetStackParameterDelta(this);
for (size_t i = 0; i < ReturnCount(); ++i) {
if (!LinkageLocation::IsSameLocation(GetReturnLocation(i),
callee->GetReturnLocation(i)))
if (GetReturnLocation(i).IsCallerFrameSlot() &&
callee->GetReturnLocation(i).IsCallerFrameSlot()) {
if (GetReturnLocation(i).AsCallerFrameSlot() - stack_param_delta !=
callee->GetReturnLocation(i).AsCallerFrameSlot()) {
return false;
}
} else if (!LinkageLocation::IsSameLocation(GetReturnLocation(i),
callee->GetReturnLocation(i))) {
return false;
}
}
return true;
}

View File

@ -159,3 +159,46 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
print(" --right--");
assertEquals(3, module.exports.main(0, -2, 3));
})();
(function TestMultiReturnCallWithLongSig() {
print(arguments.callee.name);
const callee_inputs = 10;
// Tail call from a function with less, as many, or more parameters than the
// callee.
for (caller_inputs = 9; caller_inputs <= 11; ++caller_inputs) {
let builder = new WasmModuleBuilder();
// f just returns its arguments in reverse order.
const f_params = new Array(callee_inputs).fill(kWasmI32);
const f_returns = f_params;
const f_sig = builder.addType(makeSig(f_params, f_returns));
let f_body = [];
for (i = 0; i < callee_inputs; ++i) {
f_body.push(kExprLocalGet, callee_inputs - i - 1);
}
const f = builder.addFunction("f", f_sig).addBody(f_body);
// Slice or pad the caller inputs to match the callee.
const main_params = new Array(caller_inputs).fill(kWasmI32);
const main_sig = builder.addType(makeSig(main_params, f_returns));
let main_body = [];
for (i = 0; i < callee_inputs; ++i) {
main_body.push(kExprLocalGet, Math.min(caller_inputs - 1, i));
}
main_body.push(kExprReturnCall, f.index);
builder.addFunction("main", main_sig).addBody(main_body).exportFunc();
let module = builder.instantiate();
inputs = [];
for (i = 0; i < caller_inputs; ++i) {
inputs.push(i);
}
let expect = inputs.slice(0, callee_inputs);
while (expect.length < callee_inputs) {
expect.push(inputs[inputs.length - 1]);
}
expect.reverse();
assertEquals(expect, module.exports.main(...inputs));
}
})();