Index: test/cctest/wasm/test-run-wasm-js.cc |
diff --git a/test/cctest/wasm/test-run-wasm-js.cc b/test/cctest/wasm/test-run-wasm-js.cc |
index 6fcde645cb43e25127e746f1d984453f4fe0c55e..bafeb25e3bf89865399b7d3e1703c16af4118503 100644 |
--- a/test/cctest/wasm/test-run-wasm-js.cc |
+++ b/test/cctest/wasm/test-run-wasm-js.cc |
@@ -3,6 +3,7 @@ |
// found in the LICENSE file. |
#include <stdint.h> |
+#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
@@ -24,8 +25,31 @@ using namespace v8::internal::wasm; |
} while (false) |
-static uint32_t AddJsFunction(TestingModule* module, FunctionSig* sig, |
- const char* source) { |
+#define ADD_CODE(vec, ...) \ |
+ do { \ |
+ byte __buf[] = {__VA_ARGS__}; \ |
+ for (size_t i = 0; i < sizeof(__buf); i++) vec.push_back(__buf[i]); \ |
+ } while (false) |
+ |
+ |
+namespace { |
+// A helper for generating predictable but unique argument values that |
+// are easy to debug (e.g. with misaligned stacks). |
+class PredictableInputValues { |
+ public: |
+ int base_; |
+ explicit PredictableInputValues(int base) : base_(base) {} |
+ double arg_d(int which) { return base_ * which + ((which & 1) * 0.5); } |
+ float arg_f(int which) { return base_ * which + ((which & 1) * 0.25); } |
+ int32_t arg_i(int which) { return base_ * which + ((which & 1) * kMinInt); } |
+ int64_t arg_l(int which) { |
+ return base_ * which + ((which & 1) * (0x04030201LL << 32)); |
+ } |
+}; |
+ |
+ |
+uint32_t AddJsFunction(TestingModule* module, FunctionSig* sig, |
+ const char* source) { |
Handle<JSFunction> jsfunc = Handle<JSFunction>::cast(v8::Utils::OpenHandle( |
*v8::Local<v8::Function>::Cast(CompileRun(source)))); |
module->AddFunction(sig, Handle<Code>::null()); |
@@ -37,7 +61,25 @@ static uint32_t AddJsFunction(TestingModule* module, FunctionSig* sig, |
} |
-static Handle<JSFunction> WrapCode(ModuleEnv* module, uint32_t index) { |
+uint32_t AddJSSelector(TestingModule* module, FunctionSig* sig, int which) { |
+ const int kMaxParams = 8; |
+ static const char* formals[kMaxParams] = { |
+ "", "a", "a,b", "a,b,c", |
+ "a,b,c,d", "a,b,c,d,e", "a,b,c,d,e,f", "a,b,c,d,e,f,g", |
+ }; |
+ CHECK_LT(which, static_cast<int>(sig->parameter_count())); |
+ CHECK_LT(static_cast<int>(sig->parameter_count()), kMaxParams); |
+ |
+ i::EmbeddedVector<char, 256> source; |
+ char param = 'a' + which; |
+ SNPrintF(source, "(function(%s) { return %c; })", |
+ formals[sig->parameter_count()], param); |
+ |
+ return AddJsFunction(module, sig, source.start()); |
+} |
+ |
+ |
+Handle<JSFunction> WrapCode(ModuleEnv* module, uint32_t index) { |
Isolate* isolate = module->module->shared_isolate; |
// Wrap the code so it can be called as a JS function. |
Handle<String> name = isolate->factory()->NewStringFromStaticChars("main"); |
@@ -49,14 +91,12 @@ static Handle<JSFunction> WrapCode(ModuleEnv* module, uint32_t index) { |
} |
-static void EXPECT_CALL(double expected, Handle<JSFunction> jsfunc, double a, |
- double b) { |
+void EXPECT_CALL(double expected, Handle<JSFunction> jsfunc, |
+ Handle<Object>* buffer, int count) { |
Isolate* isolate = jsfunc->GetIsolate(); |
- Handle<Object> buffer[] = {isolate->factory()->NewNumber(a), |
- isolate->factory()->NewNumber(b)}; |
Handle<Object> global(isolate->context()->global_object(), isolate); |
MaybeHandle<Object> retval = |
- Execution::Call(isolate, jsfunc, global, 2, buffer); |
+ Execution::Call(isolate, jsfunc, global, count, buffer); |
CHECK(!retval.is_null()); |
Handle<Object> result = retval.ToHandleChecked(); |
@@ -64,11 +104,20 @@ static void EXPECT_CALL(double expected, Handle<JSFunction> jsfunc, double a, |
CHECK_EQ(expected, Smi::cast(*result)->value()); |
} else { |
CHECK(result->IsHeapNumber()); |
- CHECK_EQ(expected, HeapNumber::cast(*result)->value()); |
+ CheckFloatEq(expected, HeapNumber::cast(*result)->value()); |
} |
} |
+void EXPECT_CALL(double expected, Handle<JSFunction> jsfunc, double a, |
+ double b) { |
+ Isolate* isolate = jsfunc->GetIsolate(); |
+ Handle<Object> buffer[] = {isolate->factory()->NewNumber(a), |
+ isolate->factory()->NewNumber(b)}; |
+ EXPECT_CALL(expected, jsfunc, buffer, 2); |
+} |
+} // namespace |
+ |
TEST(Run_Int32Sub_jswrapped) { |
TestSignatures sigs; |
TestingModule module; |
@@ -121,8 +170,7 @@ TEST(Run_I32Popcount_jswrapped) { |
#if !V8_TARGET_ARCH_ARM64 |
-// TODO(titzer): fix wasm->JS calls on arm64 (wrapper issues) |
- |
+// TODO(titzer): dynamic frame alignment on arm64 |
TEST(Run_CallJS_Add_jswrapped) { |
TestSignatures sigs; |
TestingModule module; |
@@ -137,5 +185,243 @@ TEST(Run_CallJS_Add_jswrapped) { |
EXPECT_CALL(199, jsfunc, 100, -1); |
EXPECT_CALL(-666666801, jsfunc, -666666900, -1); |
} |
+#endif |
+ |
+ |
+void RunJSSelectTest(int which) { |
+ const int kMaxParams = 8; |
+ PredictableInputValues inputs(0x100); |
+ LocalType type = kAstF64; |
+ LocalType types[kMaxParams + 1] = {type, type, type, type, type, |
+ type, type, type, type}; |
+ for (int num_params = which + 1; num_params < kMaxParams; num_params++) { |
+ HandleScope scope(CcTest::InitIsolateOnce()); |
+ FunctionSig sig(1, num_params, types); |
+ |
+ TestingModule module; |
+ uint32_t js_index = AddJSSelector(&module, &sig, which); |
+ WasmFunctionCompiler t(&sig, &module); |
+ |
+ { |
+ std::vector<byte> code; |
+ ADD_CODE(code, kExprCallFunction, static_cast<byte>(js_index)); |
+ |
+ for (int i = 0; i < num_params; i++) { |
+ ADD_CODE(code, WASM_F64(inputs.arg_d(i))); |
+ } |
+ |
+ size_t end = code.size(); |
+ code.push_back(0); |
+ t.Build(&code[0], &code[end]); |
+ } |
+ |
+ Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd(&module)); |
+ double expected = inputs.arg_d(which); |
+ EXPECT_CALL(expected, jsfunc, 0.0, 0.0); |
+ } |
+} |
+ |
+ |
+TEST(Run_JSSelect_0) { RunJSSelectTest(0); } |
+ |
+TEST(Run_JSSelect_1) { RunJSSelectTest(1); } |
+ |
+TEST(Run_JSSelect_2) { RunJSSelectTest(2); } |
+ |
+TEST(Run_JSSelect_3) { RunJSSelectTest(3); } |
+ |
+TEST(Run_JSSelect_4) { RunJSSelectTest(4); } |
+ |
+TEST(Run_JSSelect_5) { RunJSSelectTest(5); } |
+ |
+TEST(Run_JSSelect_6) { RunJSSelectTest(6); } |
+ |
+TEST(Run_JSSelect_7) { RunJSSelectTest(7); } |
+ |
+ |
+void RunWASMSelectTest(int which) { |
+ PredictableInputValues inputs(0x200); |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ const int kMaxParams = 8; |
+ for (int num_params = which + 1; num_params < kMaxParams; num_params++) { |
+ LocalType type = kAstF64; |
+ LocalType types[kMaxParams + 1] = {type, type, type, type, type, |
+ type, type, type, type}; |
+ FunctionSig sig(1, num_params, types); |
+ |
+ TestingModule module; |
+ WasmFunctionCompiler t(&sig, &module); |
+ BUILD(t, WASM_GET_LOCAL(which)); |
+ Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd(&module)); |
+ |
+ Handle<Object> args[] = { |
+ isolate->factory()->NewNumber(inputs.arg_d(0)), |
+ isolate->factory()->NewNumber(inputs.arg_d(1)), |
+ isolate->factory()->NewNumber(inputs.arg_d(2)), |
+ isolate->factory()->NewNumber(inputs.arg_d(3)), |
+ isolate->factory()->NewNumber(inputs.arg_d(4)), |
+ isolate->factory()->NewNumber(inputs.arg_d(5)), |
+ isolate->factory()->NewNumber(inputs.arg_d(6)), |
+ isolate->factory()->NewNumber(inputs.arg_d(7)), |
+ }; |
+ |
+ double expected = inputs.arg_d(which); |
+ EXPECT_CALL(expected, jsfunc, args, kMaxParams); |
+ } |
+} |
+ |
+ |
+TEST(Run_WASMSelect_0) { RunWASMSelectTest(0); } |
+ |
+TEST(Run_WASMSelect_1) { RunWASMSelectTest(1); } |
+ |
+TEST(Run_WASMSelect_2) { RunWASMSelectTest(2); } |
+ |
+TEST(Run_WASMSelect_3) { RunWASMSelectTest(3); } |
+ |
+TEST(Run_WASMSelect_4) { RunWASMSelectTest(4); } |
+ |
+TEST(Run_WASMSelect_5) { RunWASMSelectTest(5); } |
+ |
+TEST(Run_WASMSelect_6) { RunWASMSelectTest(6); } |
+ |
+TEST(Run_WASMSelect_7) { RunWASMSelectTest(7); } |
+ |
+ |
+void RunWASMSelectAlignTest(int num_args, int num_params) { |
+ PredictableInputValues inputs(0x300); |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ const int kMaxParams = 4; |
+ DCHECK_LE(num_args, kMaxParams); |
+ LocalType type = kAstF64; |
+ LocalType types[kMaxParams + 1] = {type, type, type, type, type}; |
+ FunctionSig sig(1, num_params, types); |
+ |
+ for (int which = 0; which < num_params; which++) { |
+ TestingModule module; |
+ WasmFunctionCompiler t(&sig, &module); |
+ BUILD(t, WASM_GET_LOCAL(which)); |
+ Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd(&module)); |
+ |
+ Handle<Object> args[] = { |
+ isolate->factory()->NewNumber(inputs.arg_d(0)), |
+ isolate->factory()->NewNumber(inputs.arg_d(1)), |
+ isolate->factory()->NewNumber(inputs.arg_d(2)), |
+ isolate->factory()->NewNumber(inputs.arg_d(3)), |
+ }; |
+ |
+ double nan = std::numeric_limits<double>::quiet_NaN(); |
+ double expected = which < num_args ? inputs.arg_d(which) : nan; |
+ EXPECT_CALL(expected, jsfunc, args, num_args); |
+ } |
+} |
+ |
+ |
+TEST(Run_WASMSelectAlign_0) { |
+ RunWASMSelectAlignTest(0, 1); |
+ RunWASMSelectAlignTest(0, 2); |
+} |
+ |
+TEST(Run_WASMSelectAlign_1) { |
+ RunWASMSelectAlignTest(1, 2); |
+ RunWASMSelectAlignTest(1, 3); |
+} |
+ |
+ |
+TEST(Run_WASMSelectAlign_2) { |
+ RunWASMSelectAlignTest(2, 3); |
+ RunWASMSelectAlignTest(2, 4); |
+} |
+ |
+ |
+TEST(Run_WASMSelectAlign_3) { |
+ RunWASMSelectAlignTest(3, 3); |
+ RunWASMSelectAlignTest(3, 4); |
+} |
+ |
+ |
+TEST(Run_WASMSelectAlign_4) { |
+ RunWASMSelectAlignTest(4, 3); |
+ RunWASMSelectAlignTest(4, 4); |
+} |
+ |
+ |
+void RunJSSelectAlignTest(int num_args, int num_params) { |
+ PredictableInputValues inputs(0x400); |
+ Isolate* isolate = CcTest::InitIsolateOnce(); |
+ Factory* factory = isolate->factory(); |
+ const int kMaxParams = 4; |
+ CHECK_LE(num_args, kMaxParams); |
+ CHECK_LE(num_params, kMaxParams); |
+ LocalType type = kAstF64; |
+ LocalType types[kMaxParams + 1] = {type, type, type, type, type}; |
+ FunctionSig sig(1, num_params, types); |
+ |
+ // Build the calling code. |
+ std::vector<byte> code; |
+ ADD_CODE(code, kExprCallFunction, 0); |
+ |
+ for (int i = 0; i < num_params; i++) { |
+ ADD_CODE(code, WASM_GET_LOCAL(i)); |
+ } |
+ |
+ size_t end = code.size(); |
+ code.push_back(0); |
+ |
+ // Call different select JS functions. |
+ for (int which = 0; which < num_params; which++) { |
+ HandleScope scope(isolate); |
+ TestingModule module; |
+ uint32_t js_index = AddJSSelector(&module, &sig, which); |
+ CHECK_EQ(0, js_index); |
+ WasmFunctionCompiler t(&sig, &module); |
+ t.Build(&code[0], &code[end]); |
+ |
+ Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd(&module)); |
+ |
+ Handle<Object> args[] = { |
+ factory->NewNumber(inputs.arg_d(0)), |
+ factory->NewNumber(inputs.arg_d(1)), |
+ factory->NewNumber(inputs.arg_d(2)), |
+ factory->NewNumber(inputs.arg_d(3)), |
+ }; |
+ |
+ double nan = std::numeric_limits<double>::quiet_NaN(); |
+ double expected = which < num_args ? inputs.arg_d(which) : nan; |
+ EXPECT_CALL(expected, jsfunc, args, num_args); |
+ } |
+} |
+ |
+ |
+TEST(Run_JSSelectAlign_0) { |
+ RunJSSelectAlignTest(0, 1); |
+ RunJSSelectAlignTest(0, 2); |
+} |
+ |
+ |
+TEST(Run_JSSelectAlign_2) { |
+ RunJSSelectAlignTest(2, 3); |
+ RunJSSelectAlignTest(2, 4); |
+} |
+ |
+ |
+TEST(Run_JSSelectAlign_4) { |
+ RunJSSelectAlignTest(4, 3); |
+ RunJSSelectAlignTest(4, 4); |
+} |
+ |
+ |
+#if !V8_TARGET_ARCH_ARM64 |
+// TODO(titzer): dynamic frame alignment on arm64 |
+TEST(Run_JSSelectAlign_1) { |
+ RunJSSelectAlignTest(1, 2); |
+ RunJSSelectAlignTest(1, 3); |
+} |
+ |
+ |
+TEST(Run_JSSelectAlign_3) { |
+ RunJSSelectAlignTest(3, 3); |
+ RunJSSelectAlignTest(3, 4); |
+} |
#endif |