Commit 17cda1e6 authored by evih's avatar evih Committed by Commit Bot

[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: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarThibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69700}
parent c878e00d
...@@ -3295,6 +3295,10 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ...@@ -3295,6 +3295,10 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
Foreign::kForeignAddressOffset))); Foreign::kForeignAddressOffset)));
foreign_signature = no_reg; foreign_signature = no_reg;
Register return_count = r8;
__ movq(return_count,
MemOperand(signature, wasm::FunctionSig::kReturnCountOffset));
Register param_count = signature; Register param_count = signature;
__ movq(param_count, __ movq(param_count,
MemOperand(signature, wasm::FunctionSig::kParameterCountOffset)); MemOperand(signature, wasm::FunctionSig::kParameterCountOffset));
...@@ -3311,9 +3315,11 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ...@@ -3311,9 +3315,11 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
constexpr int kGCScanSlotCountOffset = constexpr int kGCScanSlotCountOffset =
kFrameMarkerOffset - kSystemPointerSize; kFrameMarkerOffset - kSystemPointerSize;
constexpr int kParamCountOffset = kGCScanSlotCountOffset - kSystemPointerSize; constexpr int kParamCountOffset = kGCScanSlotCountOffset - kSystemPointerSize;
constexpr int kNumSpillSlots = 2; constexpr int kReturnCountOffset = kParamCountOffset - kSystemPointerSize;
constexpr int kNumSpillSlots = 3;
__ subq(rsp, Immediate(kNumSpillSlots * kSystemPointerSize)); __ subq(rsp, Immediate(kNumSpillSlots * kSystemPointerSize));
__ movq(MemOperand(rbp, kParamCountOffset), param_count); __ movq(MemOperand(rbp, kParamCountOffset), param_count);
__ movq(MemOperand(rbp, kReturnCountOffset), return_count);
__ cmpl(param_count, Immediate(0)); __ cmpl(param_count, Immediate(0));
...@@ -3375,14 +3381,14 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ...@@ -3375,14 +3381,14 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
__ movq(param, MemOperand(rbp, current_param, times_1, 0)); __ movq(param, MemOperand(rbp, current_param, times_1, 0));
__ addq(current_param, Immediate(increment)); __ addq(current_param, Immediate(increment));
Label not_smi; Label param_not_smi;
__ JumpIfNotSmi(param, &not_smi); __ JumpIfNotSmi(param, &param_not_smi);
// Change from smi to int32. // Change from smi to int32.
__ SmiUntag(param); __ SmiUntag(param);
Label conversion_done; Label param_conversion_done;
__ bind(&conversion_done); __ bind(&param_conversion_done);
__ pushq(param); __ pushq(param);
...@@ -3436,7 +3442,8 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ...@@ -3436,7 +3442,8 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
function_entry = no_reg; function_entry = no_reg;
// Restore rsp to free the reserved stack slots for 5 param Registers. // 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)); __ leaq(rsp, MemOperand(rbp, kLastSpillOffset));
__ movq(param_count, MemOperand(rbp, kParamCountOffset)); __ movq(param_count, MemOperand(rbp, kParamCountOffset));
...@@ -3446,9 +3453,23 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ...@@ -3446,9 +3453,23 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
thread_in_wasm_flag_addr, thread_in_wasm_flag_addr,
MemOperand(kRootRegister, Isolate::thread_in_wasm_flag_address_offset())); MemOperand(kRootRegister, Isolate::thread_in_wasm_flag_address_offset()));
__ movl(MemOperand(thread_in_wasm_flag_addr, 0), Immediate(0)); __ 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; 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); __ LoadRoot(return_reg, RootIndex::kUndefinedValue);
Label return_done;
__ bind(&return_done);
// Deconstrunct the stack frame. // Deconstrunct the stack frame.
__ LeaveFrame(StackFrame::JS_TO_WASM); __ LeaveFrame(StackFrame::JS_TO_WASM);
...@@ -3469,7 +3490,7 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ...@@ -3469,7 +3490,7 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
__ ret(0); __ ret(0);
// Handle the conversion to int32 when the param is not a smi. // 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 // 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. // scanned by GC, to be on the top of the stack.
...@@ -3497,8 +3518,38 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) { ...@@ -3497,8 +3518,38 @@ void Builtins::Generate_GenericJSToWasmWrapper(MacroAssembler* masm) {
__ popq(param_limit); __ popq(param_limit);
__ popq(current_param); __ popq(current_param);
__ movq(param_count, MemOperand(rbp, kParamCountOffset)); __ movq(param_count, MemOperand(rbp, kParamCountOffset));
__ jmp(&param_conversion_done);
__ bind(&convert_return);
__ jmp(&conversion_done); 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 { namespace {
......
...@@ -267,14 +267,21 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate, ...@@ -267,14 +267,21 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
namespace { namespace {
bool UseGenericWrapper(const FunctionSig* sig) { 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 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()) { for (ValueType type : sig->parameters()) {
if (type.kind() != ValueType::kI32) { if (type.kind() != ValueType::kI32) {
return false; return false;
} }
} }
return FLAG_wasm_generic_wrapper && sig->returns().empty(); return FLAG_wasm_generic_wrapper;
#else #else
return false; return false;
#endif #endif
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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"); load("test/mjsunit/wasm/wasm-module-builder.js");
...@@ -74,7 +74,7 @@ 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 } }); let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(5); assertEquals(undefined, instance.exports.main(5));
assertEquals(17, x); assertEquals(17, x);
})(); })();
...@@ -97,7 +97,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -97,7 +97,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let y = { valueOf: () => { print("Hello!"); gc(); return 24; } }; let y = { valueOf: () => { print("Hello!"); gc(); return 24; } };
let instance = builder.instantiate({ mod: { func: import_func } }); let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(y); assertEquals(undefined, instance.exports.main(y));
assertEquals(36, x); assertEquals(36, x);
})(); })();
...@@ -125,7 +125,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -125,7 +125,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
let param2 = { valueOf: () => { gc(); return 6; } }; let param2 = { valueOf: () => { gc(); return 6; } };
let param3 = { valueOf: () => { gc(); return 3; } }; let param3 = { valueOf: () => { gc(); return 3; } };
let instance = builder.instantiate({ mod: { func: import_func } }); 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); assertEquals(60, x);
})(); })();
...@@ -164,7 +164,7 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32, ...@@ -164,7 +164,7 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32,
let param6 = { valueOf: () => { gc(); return 10; } }; let param6 = { valueOf: () => { gc(); return 10; } };
let param8 = { valueOf: () => { gc(); return 12; } }; let param8 = { valueOf: () => { gc(); return 12; } };
let instance = builder.instantiate({ mod: { func: import_func } }); 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); assertEquals(360, x);
})(); })();
...@@ -192,7 +192,7 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32, ...@@ -192,7 +192,7 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32,
let param2 = { valueOf: () => { gc(); return 3; } }; let param2 = { valueOf: () => { gc(); return 3; } };
let instance = builder.instantiate({ mod: { func: import_func } }); let instance = builder.instantiate({ mod: { func: import_func } });
instance.exports.main(5, param2); assertEquals(undefined, instance.exports.main(5, param2));
assertEquals(20, x); assertEquals(20, x);
})(); })();
...@@ -221,6 +221,49 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32, ...@@ -221,6 +221,49 @@ let kSig_v_iiiiiiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32,
let param2 = { valueOf: () => { gc(); return 3; } }; let param2 = { valueOf: () => { gc(); return 3; } };
let param3 = { valueOf: () => { gc(); return 6; } }; let param3 = { valueOf: () => { gc(); return 6; } };
let instance = builder.instantiate({ mod: { func: import_func } }); 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); 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));
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment