c9f59780f7
In JavaScript code and stubs, JSSP mirrors the CSP but may be unaligned. But in WASM code only CSP is used, like native code, and it must be aligned. Calls into WASM from JS need to carefully align the C stack pointer (csp) and restore the previous JSSP, while calls from WASM to JS need to compute a new JSSP and restore their CSP after the call. R=ahaas@chromium.org BUG= Review URL: https://codereview.chromium.org/1811283003 Cr-Commit-Position: refs/heads/master@{#35096}
495 lines
15 KiB
C++
495 lines
15 KiB
C++
// Copyright 2015 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.
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "src/wasm/wasm-macro-gen.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
|
#include "test/cctest/compiler/value-helper.h"
|
|
#include "test/cctest/wasm/test-signatures.h"
|
|
#include "test/cctest/wasm/wasm-run-utils.h"
|
|
|
|
using namespace v8::base;
|
|
using namespace v8::internal;
|
|
using namespace v8::internal::compiler;
|
|
using namespace v8::internal::wasm;
|
|
|
|
#define BUILD(r, ...) \
|
|
do { \
|
|
byte code[] = {__VA_ARGS__}; \
|
|
r.Build(code, code + arraysize(code)); \
|
|
} while (false)
|
|
|
|
|
|
#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());
|
|
uint32_t index = static_cast<uint32_t>(module->module->functions.size() - 1);
|
|
Isolate* isolate = CcTest::InitIsolateOnce();
|
|
WasmName module_name = {"test", 4};
|
|
WasmName function_name = {nullptr, 0};
|
|
Handle<Code> code = CompileWasmToJSWrapper(isolate, module, jsfunc, sig,
|
|
module_name, function_name);
|
|
module->instance->function_code[index] = code;
|
|
return index;
|
|
}
|
|
|
|
|
|
uint32_t AddJSSelector(TestingModule* module, FunctionSig* sig, int which) {
|
|
const int kMaxParams = 11;
|
|
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",
|
|
"a,b,c,d,e,f,g,h",
|
|
"a,b,c,d,e,f,g,h,i",
|
|
"a,b,c,d,e,f,g,h,i,j"};
|
|
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");
|
|
Handle<JSObject> module_object = Handle<JSObject>(0, isolate);
|
|
Handle<Code> code = module->instance->function_code[index];
|
|
WasmJs::InstallWasmFunctionMap(isolate, isolate->native_context());
|
|
return compiler::CompileJSToWasmWrapper(isolate, module, name, code,
|
|
module_object, index);
|
|
}
|
|
|
|
|
|
void EXPECT_CALL(double expected, Handle<JSFunction> jsfunc,
|
|
Handle<Object>* buffer, int count) {
|
|
Isolate* isolate = jsfunc->GetIsolate();
|
|
Handle<Object> global(isolate->context()->global_object(), isolate);
|
|
MaybeHandle<Object> retval =
|
|
Execution::Call(isolate, jsfunc, global, count, buffer);
|
|
|
|
CHECK(!retval.is_null());
|
|
Handle<Object> result = retval.ToHandleChecked();
|
|
if (result->IsSmi()) {
|
|
CHECK_EQ(expected, Smi::cast(*result)->value());
|
|
} else {
|
|
CHECK(result->IsHeapNumber());
|
|
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;
|
|
WasmFunctionCompiler t(sigs.i_ii(), &module);
|
|
BUILD(t, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
|
Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd());
|
|
|
|
EXPECT_CALL(33, jsfunc, 44, 11);
|
|
EXPECT_CALL(-8723487, jsfunc, -8000000, 723487);
|
|
}
|
|
|
|
|
|
TEST(Run_Float32Div_jswrapped) {
|
|
TestSignatures sigs;
|
|
TestingModule module;
|
|
WasmFunctionCompiler t(sigs.f_ff(), &module);
|
|
BUILD(t, WASM_F32_DIV(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
|
Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd());
|
|
|
|
EXPECT_CALL(92, jsfunc, 46, 0.5);
|
|
EXPECT_CALL(64, jsfunc, -16, -0.25);
|
|
}
|
|
|
|
|
|
TEST(Run_Float64Add_jswrapped) {
|
|
TestSignatures sigs;
|
|
TestingModule module;
|
|
WasmFunctionCompiler t(sigs.d_dd(), &module);
|
|
BUILD(t, WASM_F64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
|
Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd());
|
|
|
|
EXPECT_CALL(3, jsfunc, 2, 1);
|
|
EXPECT_CALL(-5.5, jsfunc, -5.25, -0.25);
|
|
}
|
|
|
|
|
|
TEST(Run_I32Popcount_jswrapped) {
|
|
TestSignatures sigs;
|
|
TestingModule module;
|
|
WasmFunctionCompiler t(sigs.i_i(), &module);
|
|
BUILD(t, WASM_I32_POPCNT(WASM_GET_LOCAL(0)));
|
|
Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd());
|
|
|
|
EXPECT_CALL(2, jsfunc, 9, 0);
|
|
EXPECT_CALL(3, jsfunc, 11, 0);
|
|
EXPECT_CALL(6, jsfunc, 0x3F, 0);
|
|
|
|
USE(AddJsFunction);
|
|
}
|
|
|
|
|
|
TEST(Run_CallJS_Add_jswrapped) {
|
|
TestSignatures sigs;
|
|
TestingModule module;
|
|
WasmFunctionCompiler t(sigs.i_i(), &module);
|
|
uint32_t js_index =
|
|
AddJsFunction(&module, sigs.i_i(), "(function(a) { return a + 99; })");
|
|
BUILD(t, WASM_CALL_FUNCTION(js_index, WASM_GET_LOCAL(0)));
|
|
|
|
Handle<JSFunction> jsfunc = WrapCode(&module, t.CompileAndAdd());
|
|
|
|
EXPECT_CALL(101, jsfunc, 2, -8);
|
|
EXPECT_CALL(199, jsfunc, 100, -1);
|
|
EXPECT_CALL(-666666801, jsfunc, -666666900, -1);
|
|
}
|
|
|
|
|
|
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());
|
|
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());
|
|
|
|
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 = 10;
|
|
DCHECK_LE(num_args, kMaxParams);
|
|
LocalType type = kAstF64;
|
|
LocalType types[kMaxParams + 1] = {type, type, type, type, type, type,
|
|
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());
|
|
|
|
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)),
|
|
isolate->factory()->NewNumber(inputs.arg_d(8)),
|
|
isolate->factory()->NewNumber(inputs.arg_d(9))};
|
|
|
|
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);
|
|
}
|
|
|
|
TEST(Run_WASMSelectAlign_7) {
|
|
RunWASMSelectAlignTest(7, 5);
|
|
RunWASMSelectAlignTest(7, 6);
|
|
RunWASMSelectAlignTest(7, 7);
|
|
}
|
|
|
|
TEST(Run_WASMSelectAlign_8) {
|
|
RunWASMSelectAlignTest(8, 5);
|
|
RunWASMSelectAlignTest(8, 6);
|
|
RunWASMSelectAlignTest(8, 7);
|
|
RunWASMSelectAlignTest(8, 8);
|
|
}
|
|
|
|
TEST(Run_WASMSelectAlign_9) {
|
|
RunWASMSelectAlignTest(9, 6);
|
|
RunWASMSelectAlignTest(9, 7);
|
|
RunWASMSelectAlignTest(9, 8);
|
|
RunWASMSelectAlignTest(9, 9);
|
|
}
|
|
|
|
TEST(Run_WASMSelectAlign_10) {
|
|
RunWASMSelectAlignTest(10, 7);
|
|
RunWASMSelectAlignTest(10, 8);
|
|
RunWASMSelectAlignTest(10, 9);
|
|
RunWASMSelectAlignTest(10, 10);
|
|
}
|
|
|
|
void RunJSSelectAlignTest(int num_args, int num_params) {
|
|
PredictableInputValues inputs(0x400);
|
|
Isolate* isolate = CcTest::InitIsolateOnce();
|
|
Factory* factory = isolate->factory();
|
|
const int kMaxParams = 10;
|
|
CHECK_LE(num_args, kMaxParams);
|
|
CHECK_LE(num_params, kMaxParams);
|
|
LocalType type = kAstF64;
|
|
LocalType types[kMaxParams + 1] = {type, type, type, type, type, type,
|
|
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());
|
|
|
|
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)),
|
|
factory->NewNumber(inputs.arg_d(4)),
|
|
factory->NewNumber(inputs.arg_d(5)),
|
|
factory->NewNumber(inputs.arg_d(6)),
|
|
factory->NewNumber(inputs.arg_d(7)),
|
|
factory->NewNumber(inputs.arg_d(8)),
|
|
factory->NewNumber(inputs.arg_d(9)),
|
|
};
|
|
|
|
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_1) {
|
|
RunJSSelectAlignTest(1, 2);
|
|
RunJSSelectAlignTest(1, 3);
|
|
}
|
|
|
|
TEST(Run_JSSelectAlign_2) {
|
|
RunJSSelectAlignTest(2, 3);
|
|
RunJSSelectAlignTest(2, 4);
|
|
}
|
|
|
|
TEST(Run_JSSelectAlign_3) {
|
|
RunJSSelectAlignTest(3, 3);
|
|
RunJSSelectAlignTest(3, 4);
|
|
}
|
|
|
|
TEST(Run_JSSelectAlign_4) {
|
|
RunJSSelectAlignTest(4, 3);
|
|
RunJSSelectAlignTest(4, 4);
|
|
}
|
|
|
|
TEST(Run_JSSelectAlign_7) {
|
|
RunJSSelectAlignTest(7, 3);
|
|
RunJSSelectAlignTest(7, 4);
|
|
RunJSSelectAlignTest(7, 4);
|
|
RunJSSelectAlignTest(7, 4);
|
|
}
|
|
|
|
TEST(Run_JSSelectAlign_8) {
|
|
RunJSSelectAlignTest(8, 5);
|
|
RunJSSelectAlignTest(8, 6);
|
|
RunJSSelectAlignTest(8, 7);
|
|
RunJSSelectAlignTest(8, 8);
|
|
}
|
|
|
|
TEST(Run_JSSelectAlign_9) {
|
|
RunJSSelectAlignTest(9, 6);
|
|
RunJSSelectAlignTest(9, 7);
|
|
RunJSSelectAlignTest(9, 8);
|
|
RunJSSelectAlignTest(9, 9);
|
|
}
|
|
|
|
TEST(Run_JSSelectAlign_10) {
|
|
RunJSSelectAlignTest(10, 7);
|
|
RunJSSelectAlignTest(10, 8);
|
|
RunJSSelectAlignTest(10, 9);
|
|
RunJSSelectAlignTest(10, 10);
|
|
}
|