Commit 99484e23 authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[wasm] Intrinsify math imports

This CL adds new Wasm import call kinds that correspond to various
math functions that can be imported from JavaScript, such as trigonometry.
Instead of calling a special import wrapper that converts arguments
to tagged values by boxing, we can now generate calls to little WASM
stubs that contain a single WASM bytecode each.

R=mstarzinger@chromium.org
BUG=v8:8423

Change-Id: I59b1be2dd36d190a8b6c98b88c86cecc0ca7f4a2
Reviewed-on: https://chromium-review.googlesource.com/c/1349279
Commit-Queue: Ben Titzer <titzer@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57835}
parent 5d6a735e
...@@ -2118,7 +2118,7 @@ wasm::WasmCode* Pipeline::GenerateCodeForWasmNativeStub( ...@@ -2118,7 +2118,7 @@ wasm::WasmCode* Pipeline::GenerateCodeForWasmNativeStub(
json_of << "{\"function\":\"" << info.GetDebugName().get() json_of << "{\"function\":\"" << info.GetDebugName().get()
<< "\", \"source\":\"\",\n\"phases\":["; << "\", \"source\":\"\",\n\"phases\":[";
} }
// TODO(rossberg): Should this really be untyped?
pipeline.RunPrintAndVerify("machine", true); pipeline.RunPrintAndVerify("machine", true);
pipeline.ComputeScheduledGraph(); pipeline.ComputeScheduledGraph();
...@@ -2207,7 +2207,7 @@ MaybeHandle<Code> Pipeline::GenerateCodeForWasmHeapStub( ...@@ -2207,7 +2207,7 @@ MaybeHandle<Code> Pipeline::GenerateCodeForWasmHeapStub(
json_of << "{\"function\":\"" << info.GetDebugName().get() json_of << "{\"function\":\"" << info.GetDebugName().get()
<< "\", \"source\":\"\",\n\"phases\":["; << "\", \"source\":\"\",\n\"phases\":[";
} }
// TODO(rossberg): Should this really be untyped?
pipeline.RunPrintAndVerify("machine", true); pipeline.RunPrintAndVerify("machine", true);
pipeline.ComputeScheduledGraph(); pipeline.ComputeScheduledGraph();
......
...@@ -5058,13 +5058,65 @@ WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target, ...@@ -5058,13 +5058,65 @@ WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target,
// and whether it has a sloppy receiver. // and whether it has a sloppy receiver.
if (target->IsJSFunction()) { if (target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(target); Handle<JSFunction> function = Handle<JSFunction>::cast(target);
if (IsClassConstructor(function->shared()->kind())) { SharedFunctionInfo* shared = function->shared();
// Check for math intrinsics.
#define COMPARE_SIG_FOR_BUILTIN(name) \
{ \
wasm::FunctionSig* sig = wasm::WasmOpcodes::Signature(wasm::kExpr##name); \
if (!sig) sig = wasm::WasmOpcodes::AsmjsSignature(wasm::kExpr##name); \
DCHECK_NOT_NULL(sig); \
if (*expected_sig == *sig) return WasmImportCallKind::k##name; \
}
#define COMPARE_SIG_FOR_BUILTIN_F64(name) \
case Builtins::kMath##name: \
COMPARE_SIG_FOR_BUILTIN(F64##name); \
break;
#define COMPARE_SIG_FOR_BUILTIN_F32_F64(name) \
case Builtins::kMath##name: \
COMPARE_SIG_FOR_BUILTIN(F64##name); \
COMPARE_SIG_FOR_BUILTIN(F32##name); \
break;
if (FLAG_wasm_math_intrinsics && shared->HasBuiltinId()) {
switch (shared->builtin_id()) {
COMPARE_SIG_FOR_BUILTIN_F64(Acos);
COMPARE_SIG_FOR_BUILTIN_F64(Asin);
COMPARE_SIG_FOR_BUILTIN_F64(Atan);
COMPARE_SIG_FOR_BUILTIN_F64(Cos);
COMPARE_SIG_FOR_BUILTIN_F64(Sin);
COMPARE_SIG_FOR_BUILTIN_F64(Tan);
COMPARE_SIG_FOR_BUILTIN_F64(Exp);
COMPARE_SIG_FOR_BUILTIN_F64(Log);
COMPARE_SIG_FOR_BUILTIN_F64(Atan2);
//===========================================================
// TODO(8505): Math.pow for wasm does not match JS.
// COMPARE_SIG_FOR_BUILTIN_F64(Pow);
//===========================================================
COMPARE_SIG_FOR_BUILTIN_F32_F64(Min);
COMPARE_SIG_FOR_BUILTIN_F32_F64(Max);
COMPARE_SIG_FOR_BUILTIN_F32_F64(Abs);
COMPARE_SIG_FOR_BUILTIN_F32_F64(Ceil);
COMPARE_SIG_FOR_BUILTIN_F32_F64(Floor);
COMPARE_SIG_FOR_BUILTIN_F32_F64(Sqrt);
case Builtins::kMathFround:
COMPARE_SIG_FOR_BUILTIN(F32ConvertF64);
break;
default:
break;
}
}
#undef COMPARE_SIG_FOR_BUILTIN
#undef COMPARE_SIG_FOR_BUILTIN_F64
#undef COMPARE_SIG_FOR_BUILTIN_F32_F64
if (IsClassConstructor(shared->kind())) {
// Class constructor will throw anyway. // Class constructor will throw anyway.
return WasmImportCallKind::kUseCallBuiltin; return WasmImportCallKind::kUseCallBuiltin;
} }
bool sloppy = is_sloppy(function->shared()->language_mode()) && bool sloppy = is_sloppy(shared->language_mode()) && !shared->native();
!function->shared()->native(); if (shared->internal_formal_parameter_count() ==
if (function->shared()->internal_formal_parameter_count() ==
expected_sig->parameter_count()) { expected_sig->parameter_count()) {
return sloppy ? WasmImportCallKind::kJSFunctionArityMatchSloppy return sloppy ? WasmImportCallKind::kJSFunctionArityMatchSloppy
: WasmImportCallKind::kJSFunctionArityMatch; : WasmImportCallKind::kJSFunctionArityMatch;
...@@ -5076,6 +5128,114 @@ WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target, ...@@ -5076,6 +5128,114 @@ WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target,
return WasmImportCallKind::kUseCallBuiltin; return WasmImportCallKind::kUseCallBuiltin;
} }
wasm::WasmOpcode GetMathIntrinsicOpcode(WasmImportCallKind kind,
const char** name_ptr) {
#define CASE(name) \
case WasmImportCallKind::k##name: \
*name_ptr = "WasmMathIntrinsic:" #name; \
return wasm::kExpr##name
switch (kind) {
CASE(F64Acos);
CASE(F64Asin);
CASE(F64Atan);
CASE(F64Cos);
CASE(F64Sin);
CASE(F64Tan);
CASE(F64Exp);
CASE(F64Log);
CASE(F64Atan2);
CASE(F64Pow);
CASE(F64Ceil);
CASE(F64Floor);
CASE(F64Sqrt);
CASE(F64Min);
CASE(F64Max);
CASE(F64Abs);
CASE(F32Min);
CASE(F32Max);
CASE(F32Abs);
CASE(F32Ceil);
CASE(F32Floor);
CASE(F32Sqrt);
CASE(F32ConvertF64);
default:
UNREACHABLE();
return wasm::kExprUnreachable;
}
#undef CASE
}
wasm::WasmCode* CompileWasmMathIntrinsic(Isolate* isolate,
wasm::NativeModule* native_module,
WasmImportCallKind kind,
wasm::FunctionSig* sig) {
DCHECK_EQ(1, sig->return_count());
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"CompileWasmMathIntrinsic");
Zone zone(isolate->allocator(), ZONE_NAME);
// Compile a WASM function with a single bytecode and let TurboFan
// generate either inlined machine code or a call to a helper.
SourcePositionTable* source_positions = nullptr;
MachineGraph* mcgraph = new (&zone) MachineGraph(
new (&zone) Graph(&zone), new (&zone) CommonOperatorBuilder(&zone),
new (&zone) MachineOperatorBuilder(
&zone, MachineType::PointerRepresentation(),
InstructionSelector::SupportedMachineOperatorFlags(),
InstructionSelector::AlignmentRequirements()));
wasm::CompilationEnv env(
native_module->module(), wasm::UseTrapHandler::kNoTrapHandler,
wasm::RuntimeExceptionSupport::kNoRuntimeExceptionSupport,
wasm::LowerSimd::kNoLowerSimd);
WasmGraphBuilder builder(&env, mcgraph->zone(), mcgraph, sig,
source_positions);
// Set up the graph start.
Node* start = builder.Start(static_cast<int>(sig->parameter_count() + 1 + 1));
Node* effect = start;
Node* control = start;
builder.set_effect_ptr(&effect);
builder.set_control_ptr(&control);
builder.set_instance_node(builder.Param(wasm::kWasmInstanceParameterIndex));
// Generate either a unop or a binop.
Node* result = nullptr;
const char* debug_name = "WasmMathIntrinsic";
auto opcode = GetMathIntrinsicOpcode(kind, &debug_name);
switch (sig->parameter_count()) {
case 1:
result = builder.Unop(opcode, builder.Param(1));
break;
case 2:
result = builder.Binop(opcode, builder.Param(1), builder.Param(2));
break;
default:
UNREACHABLE();
break;
}
builder.Return(result);
// Run the compiler pipeline to generate machine code.
auto call_descriptor = GetWasmCallDescriptor(&zone, sig);
if (mcgraph->machine()->Is32()) {
call_descriptor = GetI32WasmCallDescriptor(&zone, call_descriptor);
}
wasm::WasmCode* wasm_code = Pipeline::GenerateCodeForWasmNativeStub(
isolate->wasm_engine(), call_descriptor, mcgraph, Code::WASM_FUNCTION,
wasm::WasmCode::kFunction, debug_name, AssemblerOptions::Default(isolate),
native_module, source_positions);
CHECK_NOT_NULL(wasm_code);
// TODO(titzer): add counters for math intrinsic code size / allocation
return wasm_code;
}
wasm::WasmCode* CompileWasmImportCallWrapper(Isolate* isolate, wasm::WasmCode* CompileWasmImportCallWrapper(Isolate* isolate,
wasm::NativeModule* native_module, wasm::NativeModule* native_module,
WasmImportCallKind kind, WasmImportCallKind kind,
...@@ -5084,6 +5244,13 @@ wasm::WasmCode* CompileWasmImportCallWrapper(Isolate* isolate, ...@@ -5084,6 +5244,13 @@ wasm::WasmCode* CompileWasmImportCallWrapper(Isolate* isolate,
DCHECK_NE(WasmImportCallKind::kLinkError, kind); DCHECK_NE(WasmImportCallKind::kLinkError, kind);
DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind); DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind);
// Check for math intrinsics first.
if (FLAG_wasm_math_intrinsics &&
kind >= WasmImportCallKind::kFirstMathIntrinsic &&
kind <= WasmImportCallKind::kLastMathIntrinsic) {
return CompileWasmMathIntrinsic(isolate, native_module, kind, sig);
}
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"CompileWasmImportCallWrapper"); "CompileWasmImportCallWrapper");
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
......
...@@ -77,7 +77,34 @@ enum class WasmImportCallKind : uint8_t { ...@@ -77,7 +77,34 @@ enum class WasmImportCallKind : uint8_t {
kJSFunctionArityMatchSloppy, // fast WASM->JS call, sloppy receiver kJSFunctionArityMatchSloppy, // fast WASM->JS call, sloppy receiver
kJSFunctionArityMismatch, // WASM->JS, needs adapter frame kJSFunctionArityMismatch, // WASM->JS, needs adapter frame
kJSFunctionArityMismatchSloppy, // WASM->JS, needs adapter frame, sloppy kJSFunctionArityMismatchSloppy, // WASM->JS, needs adapter frame, sloppy
kUseCallBuiltin // everything else // Math functions imported from JavaScript that are intrinsified
kFirstMathIntrinsic,
kF64Acos = kFirstMathIntrinsic,
kF64Asin,
kF64Atan,
kF64Cos,
kF64Sin,
kF64Tan,
kF64Exp,
kF64Log,
kF64Atan2,
kF64Pow,
kF64Ceil,
kF64Floor,
kF64Sqrt,
kF64Min,
kF64Max,
kF64Abs,
kF32Min,
kF32Max,
kF32Abs,
kF32Ceil,
kF32Floor,
kF32Sqrt,
kF32ConvertF64,
kLastMathIntrinsic = kF32ConvertF64,
// For everything else, there's the call builtin.
kUseCallBuiltin
}; };
WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> callable, WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> callable,
......
...@@ -608,6 +608,8 @@ DEFINE_BOOL(wasm_no_bounds_checks, false, ...@@ -608,6 +608,8 @@ DEFINE_BOOL(wasm_no_bounds_checks, false,
"disable bounds checks (performance testing only)") "disable bounds checks (performance testing only)")
DEFINE_BOOL(wasm_no_stack_checks, false, DEFINE_BOOL(wasm_no_stack_checks, false,
"disable stack checks (performance testing only)") "disable stack checks (performance testing only)")
DEFINE_BOOL(wasm_math_intrinsics, false,
"intrinsify some Math imports into wasm")
DEFINE_BOOL(wasm_shared_engine, true, DEFINE_BOOL(wasm_shared_engine, true,
"shares one wasm engine between all isolates within a process") "shares one wasm engine between all isolates within a process")
......
...@@ -1607,7 +1607,16 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) { ...@@ -1607,7 +1607,16 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
native_module->import_wrapper_cache()->GetOrCompile( native_module->import_wrapper_cache()->GetOrCompile(
isolate_, kind, expected_sig); isolate_, kind, expected_sig);
ImportedFunctionEntry entry(instance, func_index); ImportedFunctionEntry entry(instance, func_index);
entry.SetWasmToJs(isolate_, js_receiver, wasm_code); if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) {
// Wasm to JS wrappers are treated specially in the import table.
entry.SetWasmToJs(isolate_, js_receiver, wasm_code);
} else {
// Wasm math intrinsics are compiled as regular Wasm functions.
DCHECK(kind >=
compiler::WasmImportCallKind::kFirstMathIntrinsic &&
kind <= compiler::WasmImportCallKind::kLastMathIntrinsic);
entry.SetWasmToWasm(*instance, wasm_code->instruction_start());
}
break; break;
} }
} }
......
...@@ -57,6 +57,9 @@ ...@@ -57,6 +57,9 @@
# Issue 5495: enable the test when the constant field tracking in enabled. # Issue 5495: enable the test when the constant field tracking in enabled.
'const-field-tracking': [SKIP], 'const-field-tracking': [SKIP],
# Issue 8505: Math.pow is incorrect for asm.js
'regress/wasm/regress-8505': [SKIP],
############################################################################## ##############################################################################
# Too slow in debug mode with --stress-opt mode. # Too slow in debug mode with --stress-opt mode.
'regress/regress-create-exception': [PASS, ['mode == debug', SKIP]], 'regress/regress-create-exception': [PASS, ['mode == debug', SKIP]],
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --wasm-math-intrinsics --validate-asm --allow-natives-syntax
load('test/mjsunit/wasm/wasm-constants.js');
load('test/mjsunit/wasm/wasm-module-builder.js');
function verbose(args) {
// print(...args);
}
//=============================================
// Global count of failures
//=============================================
let numFailures = 0;
function reportFailure(name, vals, m, w) {
print(" error: " + name + "(" + vals + ") == " + w + ", expected " + m);
numFailures++;
}
let global_imports = {Math: Math};
let inputs = [
1 / 0,
-1 / 0,
0 / 0,
-2.70497e+38,
-1.4698e+37,
-1.22813e+35,
-1.34584e+34,
-1.0079e+32,
-6.49364e+26,
-3.06077e+25,
-1.46821e+25,
-1.17658e+23,
-1.9617e+22,
-2.7357e+20,
-9223372036854775808.0, // INT64_MIN
-1.48708e+13,
-1.89633e+12,
-4.66622e+11,
-2.22581e+11,
-1.45381e+10,
-2147483904.0, // First float32 after INT32_MIN
-2147483648.0, // INT32_MIN
-2147483520.0, // Last float32 before INT32_MIN
-1.3956e+09,
-1.32951e+09,
-1.30721e+09,
-1.19756e+09,
-9.26822e+08,
-5.09256e+07,
-964300.0,
-192446.0,
-28455.0,
-27194.0,
-20575.0,
-17069.0,
-9167.0,
-960.178,
-113.0,
-62.0,
-15.0,
-7.0,
-1.0,
-0.0256635,
-4.60374e-07,
-3.63759e-10,
-4.30175e-14,
-5.27385e-15,
-1.5707963267948966,
-1.48084e-15,
-2.220446049250313e-16,
-1.05755e-19,
-3.2995e-21,
-1.67354e-23,
-1.11885e-23,
-1.78506e-30,
-1.43718e-34,
-1.27126e-38,
-0.0,
3e-88,
-2e66,
0.0,
2e66,
1.17549e-38,
1.56657e-37,
4.08512e-29,
6.25073e-22,
4.1723e-13,
1.44343e-09,
1.5707963267948966,
5.27004e-08,
9.48298e-08,
5.57888e-07,
4.89988e-05,
0.244326,
1.0,
12.4895,
19.0,
47.0,
106.0,
538.324,
564.536,
819.124,
7048.0,
12611.0,
19878.0,
20309.0,
797056.0,
1.77219e+09,
2147483648.0, // INT32_MAX + 1
4294967296.0, // UINT32_MAX + 1
1.51116e+11,
4.18193e+13,
3.59167e+16,
9223372036854775808.0, // INT64_MAX + 1
18446744073709551616.0, // UINT64_MAX + 1
3.38211e+19,
2.67488e+20,
1.78831e+21,
9.20914e+21,
8.35654e+23,
1.4495e+24,
5.94015e+25,
4.43608e+30,
2.44502e+33,
1.38178e+37,
1.71306e+37,
3.31899e+38,
3.40282e+38,
];
function assertBinop(name, math_func, wasm_func) {
let inputs2 = [ 1, 0.5, -1, -0.5, 0, -0, 1/0, -1/0, 0/0 ];
for (val of inputs) {
verbose(" ", val);
for (val2 of inputs2) {
verbose(" ", val2);
let m = math_func(val, val2);
let w = wasm_func(val, val2);
if (!deepEquals(m, w)) reportFailure(name, [val, val2], m, w);
m = math_func(val2, val);
w = wasm_func(val2, val);
if (!deepEquals(m, w)) reportFailure(name, [val2, val], m, w);
}
}
}
let stdlib = this;
function Module_exp(stdlib) {
"use asm";
var Stdlib = stdlib.Math.exp;
function NAME(a, b) {
a = +a;
b = +b;
return +Stdlib(a, b);
}
return {exp: exp};
}
function wasmBinop(name, sig) {
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(sig);
builder.addImport('Math', name, sig_index);
builder.addFunction('main', sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallFunction, 0
]) // --
.exportAs('main');
return builder.instantiate(global_imports).exports.main;
}
function asmBinop(name) {
let instance = Module_exp(stdlib);
assertTrue(%IsAsmWasmCode(Module_exp));
let asm_func = instance[name];
if (typeof asm_func != "function") throw "asm[" + full_name + "] not found";
return asm_func;
}
(function TestF64() {
let name = 'exp';
let math_func = Math[name];
let wasm_func = wasmBinop(name, kSig_d_dd);
assertBinop("(f64)" + name, math_func, wasm_func);
let asm_func = asmBinop(name);
assertBinop("(f64)" + name, math_func, asm_func);
})();
assertEquals(0, numFailures);
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --validate-asm --allow-natives-syntax
function verbose(args) {
// print(...args);
}
//=============================================
// Global count of failures
//=============================================
let numFailures = 0;
function reportFailure(name, vals, m, w) {
print(' error: ' + name + '(' + vals + ') == ' + w + ', expected ' + m);
numFailures++;
}
let inputs = [
1 / 0,
-1 / 0,
0 / 0,
-2.70497e+38,
-1.4698e+37,
-1.22813e+35,
-1.34584e+34,
-1.0079e+32,
-6.49364e+26,
-3.06077e+25,
-1.46821e+25,
-1.17658e+23,
-1.9617e+22,
-2.7357e+20,
-9223372036854775808.0, // INT64_MIN
-1.48708e+13,
-1.89633e+12,
-4.66622e+11,
-2.22581e+11,
-1.45381e+10,
-2147483904.0, // First float32 after INT32_MIN
-2147483648.0, // INT32_MIN
-2147483520.0, // Last float32 before INT32_MIN
-1.3956e+09,
-1.32951e+09,
-1.30721e+09,
-1.19756e+09,
-9.26822e+08,
-5.09256e+07,
-964300.0,
-192446.0,
-28455.0,
-27194.0,
-20575.0,
-17069.0,
-9167.0,
-960.178,
-113.0,
-62.0,
-15.0,
-7.0,
-1.0,
-0.0256635,
-4.60374e-07,
-3.63759e-10,
-4.30175e-14,
-5.27385e-15,
-1.5707963267948966,
-1.48084e-15,
-2.220446049250313e-16,
-1.05755e-19,
-3.2995e-21,
-1.67354e-23,
-1.11885e-23,
-1.78506e-30,
-1.43718e-34,
-1.27126e-38,
-0.0,
3e-88,
-2e66,
0.0,
2e66,
1.17549e-38,
1.56657e-37,
4.08512e-29,
6.25073e-22,
4.1723e-13,
1.44343e-09,
1.5707963267948966,
5.27004e-08,
9.48298e-08,
5.57888e-07,
4.89988e-05,
0.244326,
1.0,
12.4895,
19.0,
47.0,
106.0,
538.324,
564.536,
819.124,
7048.0,
12611.0,
19878.0,
20309.0,
797056.0,
1.77219e+09,
2147483648.0, // INT32_MAX + 1
4294967296.0, // UINT32_MAX + 1
1.51116e+11,
4.18193e+13,
3.59167e+16,
9223372036854775808.0, // INT64_MAX + 1
18446744073709551616.0, // UINT64_MAX + 1
3.38211e+19,
2.67488e+20,
1.78831e+21,
9.20914e+21,
8.35654e+23,
1.4495e+24,
5.94015e+25,
4.43608e+30,
2.44502e+33,
1.38178e+37,
1.71306e+37,
3.31899e+38,
3.40282e+38,
];
let stdlib = this;
// Module template for generating f64 unop functions.
function ModuleTemplate_f64_unop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
function NAME(a) {
a = +a;
return +Stdlib(a);
}
return {NAME: NAME};
}
// Module template for generating f64 binop functions.
function ModuleTemplate_f64_binop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
function NAME(a, b) {
a = +a;
b = +b;
return +Stdlib(a, b);
}
return {NAME: NAME};
}
// Module template for generating f64 unop functions.
function ModuleTemplate_f32_unop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
var fround = stdlib.Math.fround;
function NAME(a) {
a = fround(a);
return fround(Stdlib(a));
}
return {NAME: NAME};
}
// Module template for generating f64 binop functions.
function ModuleTemplate_f32_binop(stdlib) {
'use asm';
var Stdlib = stdlib.Math.NAME;
var fround = stdlib.Math.fround;
function NAME(a, b) {
a = fround(a);
b = fround(b);
return fround(Stdlib(a, b));
}
return {NAME: NAME};
}
function instantiateTemplate(func, name) {
let src = func.toString();
src = src.replace(/NAME/g, name);
let module = eval('(' + src + ')');
let instance = module(stdlib);
assertTrue(%IsAsmWasmCode(module));
let asm_func = instance[name];
if (typeof asm_func != 'function') throw 'asm[' + full_name + '] not found';
return asm_func;
}
function genUnop(name, f32) {
return instantiateTemplate(
f32 ? ModuleTemplate_f32_unop : ModuleTemplate_f64_unop, name);
}
function genBinop(name, f32) {
return instantiateTemplate(
f32 ? ModuleTemplate_f32_binop : ModuleTemplate_f64_binop, name);
}
function assertUnop(name, math_func, asm_func) {
for (val of inputs) {
verbose(' ', val);
let m = math_func(val);
let w = asm_func(val);
if (!deepEquals(m, w)) reportFailure(name, [val], m, w);
}
}
function assertBinop(name, math_func, asm_func) {
let inputs2 = [1, 0.5, -1, -0.5, 0, -0, 1 / 0, -1 / 0, 0 / 0];
for (val of inputs) {
verbose(' ', val);
for (val2 of inputs2) {
verbose(' ', val2);
let m = math_func(val, val2);
let w = asm_func(val, val2);
if (!deepEquals(m, w)) reportFailure(name, [val, val2], m, w);
m = math_func(val2, val);
w = asm_func(val2, val);
if (!deepEquals(m, w)) reportFailure(name, [val2, val], m, w);
}
}
}
(function TestF64() {
let f64_intrinsics = [
'acos', 'asin', 'atan', 'cos', 'sin', 'tan', 'exp', 'log',
'atan2', 'pow', 'ceil', 'floor', 'sqrt', 'min', 'max', 'abs',
'min', 'max', 'abs', 'ceil', 'floor', 'sqrt',
];
for (name of f64_intrinsics) {
if (name == 'pow') continue; // TODO(8505): asm.js correctness
let math_func = Math[name];
let f32 = false;
print('Testing (f64) Math.' + name);
switch (math_func.length) {
case 1: {
let asm_func = genUnop(name, false);
assertUnop('(f64)' + name, math_func, asm_func);
break;
}
case 2: {
let asm_func = genBinop(name, false);
assertBinop('(f64)' + name, math_func, asm_func);
break;
}
default:
throw 'Unexpected param count: ' + func.length;
}
}
})();
(function TestF32() {
let f32_intrinsics = ['min', 'max', 'abs', 'ceil', 'floor', 'sqrt'];
for (name of f32_intrinsics) {
let r = Math.fround, f = Math[name];
print('Testing (f32) Math.' + name);
switch (f.length) {
case 1: {
let asm_func = genUnop(name, true);
let math_func = (val) => r(f(r(val)));
assertUnop('(f32)' + name, math_func, asm_func);
break;
}
case 2: {
let asm_func = genBinop(name, true);
let math_func = (v1, v2) => r(f(r(v1), r(v2)));
assertBinop('(f32)' + name, math_func, asm_func);
break;
}
default:
throw 'Unexpected param count: ' + func.length;
}
}
})();
assertEquals(0, numFailures);
...@@ -116,6 +116,7 @@ let kSig_i_l = makeSig([kWasmI64], [kWasmI32]); ...@@ -116,6 +116,7 @@ let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]); let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]); let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []); let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]);
let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]); let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]); let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]); let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
...@@ -140,6 +141,7 @@ let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]); ...@@ -140,6 +141,7 @@ let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
let kSig_v_f = makeSig([kWasmF32], []); let kSig_v_f = makeSig([kWasmF32], []);
let kSig_f_f = makeSig([kWasmF32], [kWasmF32]); let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
let kSig_f_d = makeSig([kWasmF64], [kWasmF32]);
let kSig_d_d = makeSig([kWasmF64], [kWasmF64]); let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
let kSig_r_r = makeSig([kWasmAnyRef], [kWasmAnyRef]); let kSig_r_r = makeSig([kWasmAnyRef], [kWasmAnyRef]);
let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]); let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]);
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --wasm-math-intrinsics
load('test/mjsunit/wasm/wasm-constants.js');
load('test/mjsunit/wasm/wasm-module-builder.js');
function verbose(args) {
// print(...args);
}
//=============================================
// Global count of failures
//=============================================
let numFailures = 0;
function reportFailure(name, vals, m, w) {
print(' error: ' + name + '(' + vals + ') == ' + w + ', expected ' + m);
numFailures++;
}
let global_imports = {Math: Math};
let inputs = [
1 / 0,
-1 / 0,
0 / 0,
-2.70497e+38,
-1.4698e+37,
-1.22813e+35,
-1.34584e+34,
-1.0079e+32,
-6.49364e+26,
-3.06077e+25,
-1.46821e+25,
-1.17658e+23,
-1.9617e+22,
-2.7357e+20,
-9223372036854775808.0, // INT64_MIN
-1.48708e+13,
-1.89633e+12,
-4.66622e+11,
-2.22581e+11,
-1.45381e+10,
-2147483904.0, // First float32 after INT32_MIN
-2147483648.0, // INT32_MIN
-2147483520.0, // Last float32 before INT32_MIN
-1.3956e+09,
-1.32951e+09,
-1.30721e+09,
-1.19756e+09,
-9.26822e+08,
-5.09256e+07,
-964300.0,
-192446.0,
-28455.0,
-27194.0,
-20575.0,
-17069.0,
-9167.0,
-960.178,
-113.0,
-62.0,
-15.0,
-7.0,
-1.0,
-0.0256635,
-4.60374e-07,
-3.63759e-10,
-4.30175e-14,
-5.27385e-15,
-1.5707963267948966,
-1.48084e-15,
-2.220446049250313e-16,
-1.05755e-19,
-3.2995e-21,
-1.67354e-23,
-1.11885e-23,
-1.78506e-30,
-1.43718e-34,
-1.27126e-38,
-0.0,
3e-88,
-2e66,
0.0,
2e66,
1.17549e-38,
1.56657e-37,
4.08512e-29,
6.25073e-22,
4.1723e-13,
1.44343e-09,
1.5707963267948966,
5.27004e-08,
9.48298e-08,
5.57888e-07,
4.89988e-05,
0.244326,
1.0,
12.4895,
19.0,
47.0,
106.0,
538.324,
564.536,
819.124,
7048.0,
12611.0,
19878.0,
20309.0,
797056.0,
1.77219e+09,
2147483648.0, // INT32_MAX + 1
4294967296.0, // UINT32_MAX + 1
1.51116e+11,
4.18193e+13,
3.59167e+16,
9223372036854775808.0, // INT64_MAX + 1
18446744073709551616.0, // UINT64_MAX + 1
3.38211e+19,
2.67488e+20,
1.78831e+21,
9.20914e+21,
8.35654e+23,
1.4495e+24,
5.94015e+25,
4.43608e+30,
2.44502e+33,
1.38178e+37,
1.71306e+37,
3.31899e+38,
3.40282e+38,
];
function genUnop(name, sig) {
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(sig);
builder.addImport('Math', name, sig_index);
builder.addFunction('main', sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprCallFunction, 0
]) // --
.exportAs('main');
return builder.instantiate(global_imports).exports.main;
}
function genBinop(name, sig) {
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(sig);
builder.addImport('Math', name, sig_index);
builder.addFunction('main', sig_index)
.addBody([
kExprGetLocal, 0, // --
kExprGetLocal, 1, // --
kExprCallFunction, 0
]) // --
.exportAs('main');
return builder.instantiate(global_imports).exports.main;
}
function assertUnop(name, math_func, wasm_func) {
for (val of inputs) {
verbose(' ', val);
let m = math_func(val);
let w = wasm_func(val);
if (!deepEquals(m, w)) reportFailure(name, [val], m, w);
}
}
function assertBinop(name, math_func, wasm_func) {
let inputs2 = [1, 0.5, -1, -0.5, 0, -0, 1 / 0, -1 / 0, 0 / 0];
for (val of inputs) {
verbose(' ', val);
for (val2 of inputs2) {
verbose(' ', val2);
let m = math_func(val, val2);
let w = wasm_func(val, val2);
if (!deepEquals(m, w)) reportFailure(name, [val, val2], m, w);
m = math_func(val2, val);
w = wasm_func(val2, val);
if (!deepEquals(m, w)) reportFailure(name, [val2, val], m, w);
}
}
}
(function TestF64() {
let f64_intrinsics = [
'acos', 'asin', 'atan', 'cos', 'sin', 'tan', 'exp', 'log',
'atan2', 'pow', 'ceil', 'floor', 'sqrt', 'min', 'max', 'abs',
'min', 'max', 'abs', 'ceil', 'floor', 'sqrt',
];
for (name of f64_intrinsics) {
let math_func = Math[name];
let f32 = false;
print('Testing (f64) Math.' + name);
switch (math_func.length) {
case 1: {
let wasm_func = genUnop(name, kSig_d_d);
assertUnop('(f64)' + name, math_func, wasm_func);
break;
}
case 2: {
let wasm_func = genBinop(name, kSig_d_dd);
assertBinop('(f64)' + name, math_func, wasm_func);
break;
}
default:
throw 'Unexpected param count: ' + func.length;
}
}
})();
(function TestF32() {
let f32_intrinsics = ['min', 'max', 'abs', 'ceil', 'floor', 'sqrt'];
for (name of f32_intrinsics) {
let r = Math.fround, f = Math[name];
print('Testing (f32) Math.' + name);
switch (f.length) {
case 1: {
let wasm_func = genUnop(name, kSig_f_f);
let math_func = (val) => r(f(r(val)));
assertUnop('(f32)' + name, math_func, wasm_func);
break;
}
case 2: {
let wasm_func = genBinop(name, kSig_f_ff);
let math_func = (v1, v2) => r(f(r(v1), r(v2)));
assertBinop('(f32)' + name, math_func, wasm_func);
break;
}
default:
throw 'Unexpected param count: ' + func.length;
}
}
})();
(function TestFround() {
let name = 'fround';
print('Testing (f32) Math.' + name);
let wasm_func = genUnop(name, kSig_f_d); // fround has a special signature.
let f = Math[name];
let r = Math.fround;
let math_func = (val) => r(f(r(val)));
assertUnop(name, math_func, wasm_func);
})();
assertEquals(0, numFailures);
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