[wasm] Use generic js-to-wasm wrapper for 0 or 1 int32 return case

The generic wrapper can be used for Wasm functions with int32 parameters
and 0 or 1 int32 return values.

Added tests for cases when the return value can & cannot be converted to
a Smi.

Bug: v8:10701
Change-Id: I470954ed0aced0e4ec6e65a9f38caac19c576549
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2390141
Commit-Queue: Eva Herencsárová <evih@google.com>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69700}
This commit is contained in:
evih 2020-09-03 16:06:31 +02:00 committed by Commit Bot
parent c878e00db8
commit 17cda1e6f1
3 changed files with 118 additions and 17 deletions

View File

@ -3295,6 +3295,10 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
Foreign::kForeignAddressOffset)));
foreign_signature = no_reg;
Register return_count = r8;
__ movq(return_count,
MemOperand(signature, wasm::FunctionSig::kReturnCountOffset));
Register param_count = signature;
__ movq(param_count,
MemOperand(signature, wasm::FunctionSig::kParameterCountOffset));
@ -3311,9 +3315,11 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
constexpr int kGCScanSlotCountOffset =
kFrameMarkerOffset - kSystemPointerSize;
constexpr int kParamCountOffset = kGCScanSlotCountOffset - kSystemPointerSize;
constexpr int kNumSpillSlots = 2;
constexpr int kReturnCountOffset = kParamCountOffset - kSystemPointerSize;
constexpr int kNumSpillSlots = 3;
__ subq(rsp, Immediate(kNumSpillSlots * kSystemPointerSize));
__ movq(MemOperand(rbp, kParamCountOffset), param_count);
__ movq(MemOperand(rbp, kReturnCountOffset), return_count);
__ cmpl(param_count, Immediate(0));
@ -3375,14 +3381,14 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
__ movq(param, MemOperand(rbp, current_param, times_1, 0));
__ addq(current_param, Immediate(increment));
Label not_smi;
__ JumpIfNotSmi(param, &not_smi);
Label param_not_smi;
__ JumpIfNotSmi(param, &param_not_smi);
// Change from smi to int32.
__ SmiUntag(param);
Label conversion_done;
__ bind(&conversion_done);
Label param_conversion_done;
__ bind(&param_conversion_done);
__ pushq(param);
@ -3436,7 +3442,8 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
function_entry = no_reg;
// Restore rsp to free the reserved stack slots for 5 param Registers.
constexpr int kLastSpillOffset = kParamCountOffset;
constexpr int kLastSpillOffset =
kFrameMarkerOffset - kNumSpillSlots * kSystemPointerSize;
__ leaq(rsp, MemOperand(rbp, kLastSpillOffset));
__ movq(param_count, MemOperand(rbp, kParamCountOffset));
@ -3446,9 +3453,23 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
thread_in_wasm_flag_addr,
MemOperand(kRootRegister, Isolate::thread_in_wasm_flag_address_offset()));
__ movl(MemOperand(thread_in_wasm_flag_addr, 0), Immediate(0));
thread_in_wasm_flag_addr = no_reg;
// Handle returns.
__ movq(return_count, MemOperand(rbp, kReturnCountOffset));
Register return_reg = rax;
// If we have 1 return value, then jump to conversion.
__ cmpl(return_count, Immediate(1));
Label convert_return;
__ j(equal, &convert_return);
// Otherwise load undefined.
__ LoadRoot(return_reg, RootIndex::kUndefinedValue);
Label return_done;
__ bind(&return_done);
// Deconstrunct the stack frame.
__ LeaveFrame(StackFrame::JS_TO_WASM);
@ -3469,7 +3490,7 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
__ ret(0);
// Handle the conversion to int32 when the param is not a smi.
__ bind(&not_smi);
__ bind(&param_not_smi);
// The order of pushes is important. We want the heap objects, that should be
// scanned by GC, to be on the top of the stack.
@ -3497,8 +3518,38 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
__ popq(param_limit);
__ popq(current_param);
__ movq(param_count, MemOperand(rbp, kParamCountOffset));
__ jmp(&param_conversion_done);
__ jmp(&conversion_done);
__ bind(&convert_return);
Label to_heapnumber;
// If pointer compression is disabled, we can convert the return to a smi.
if (SmiValuesAre32Bits()) {
__ SmiTag(return_reg);
} else {
Register temp = rbx;
__ movq(temp, return_reg);
// Double the return value to test if it can be a Smi.
__ addl(temp, return_reg);
// If there was overflow, convert the return value to a HeapNumber.
__ j(overflow, &to_heapnumber);
// If there was no overflow, we can convert to Smi.
__ SmiTag(return_reg);
}
__ jmp(&return_done);
// Handle the conversion of the return value to HeapNumber when it cannot be a
// smi.
__ bind(&to_heapnumber);
// We have to make sure that the kGCScanSlotCount is set correctly. For this
// builtin it's the same as for the Wasm call = 0, so we don't have to reset
// it.
// We don't need the JS context for this builtin call.
__ Call(BUILTIN_CODE(masm->isolate(), WasmInt32ToHeapNumber),
RelocInfo::CODE_TARGET);
// We will need the parameter_count later.
__ movq(param_count, MemOperand(rbp, kParamCountOffset));
__ jmp(&return_done);
}
namespace {

View File

@ -267,14 +267,21 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
namespace {
bool UseGenericWrapper(const FunctionSig* sig) {
// Work only for int32 parameters and no return values for now.
// Work only for int32 parameters and 1 or 0 return value for now.
#if V8_TARGET_ARCH_X64
if (sig->returns().size() > 1) {
return false;
}
if (sig->returns().size() == 1 &&
sig->GetReturn(0).kind() != ValueType::kI32) {
return false;
}
for (ValueType type : sig->parameters()) {
if (type.kind() != ValueType::kI32) {
return false;
}
}
return FLAG_wasm_generic_wrapper && sig->returns().empty();
return FLAG_wasm_generic_wrapper;
#else
return false;
#endif

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --wasm-generic-wrapper --expose-gc
// Flags: --wasm-generic-wrapper --expose-gc --allow-natives-syntax
load("test/mjsunit/wasm/wasm-module-builder.js");
@ -74,7 +74,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
}
let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(5);
assertEquals(undefined, instance.exports.main(5));
assertEquals(17, x);
})();
@ -97,7 +97,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let y = { valueOf: () => { print("Hello!"); gc(); return 24; } };
let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(y);
assertEquals(undefined, instance.exports.main(y));
assertEquals(36, x);
})();
@ -125,7 +125,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let param2 = { valueOf: () => { gc(); return 6; } };
let param3 = { valueOf: () => { gc(); return 3; } };
let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(9, param2, param3, 0);
assertEquals(undefined, instance.exports.main(9, param2, param3, 0));
assertEquals(60, x);
})();
@ -164,7 +164,7 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32,
let param6 = { valueOf: () => { gc(); return 10; } };
let param8 = { valueOf: () => { gc(); return 12; } };
let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(param1, 6, 7, param4, 9, param6, 11, param8);
assertEquals(undefined, instance.exports.main(param1, 6, 7, param4, 9, param6, 11, param8));
assertEquals(360, x);
})();
@ -192,7 +192,7 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32,
let param2 = { valueOf: () => { gc(); return 3; } };
let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(5, param2);
assertEquals(undefined, instance.exports.main(5, param2));
assertEquals(20, x);
})();
@ -221,6 +221,49 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32,
let param2 = { valueOf: () => { gc(); return 3; } };
let param3 = { valueOf: () => { gc(); return 6; } };
let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(5, param2, param3, 7, 200, 300, 400);
assertEquals(undefined, instance.exports.main(5, param2, param3, 7, 200, 300, 400));
assertEquals(33, x);
})();
(function testGenericWrapper1ReturnSmi() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_i_i);
let func_index = builder.addImport("mod", "func", sig_index);
builder.addFunction("main", sig_index)
.addBody([
kExprLocalGet, 0, kExprCallFunction, func_index
])
.exportFunc();
let x = 12;
function import_func(param) {
gc();
return x + param;
}
let instance = builder.instantiate({ mod: { func: import_func } });
assertEquals(17, instance.exports.main(5));
})();
(function testGenericWrapper1ReturnHeapNumber() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_i_i);
let func_index = builder.addImport("mod", "func", sig_index);
builder.addFunction("main", sig_index)
.addBody([
kExprLocalGet, 0, kExprCallFunction, func_index
])
.exportFunc();
let x = 2147483640;
function import_func(param) {
let result = x + param;
%SimulateNewspaceFull();
return result;
}
let instance = builder.instantiate({ mod: { func: import_func } });
assertEquals(2147483645, instance.exports.main(5));
})();