v8/test/cctest/wasm/test-c-wasm-entry.cc
Clemens Backes 8d1c5f3344 [wasm][cleanup] Avoid passing non-const FunctionSig*
Most function signatures are created once and never changed. Hence pass
them as const pointer. This makes it clear in function signatures that
these parameters will not be modified.

This also avoids a few ugly const_casts where we were passing pointers
to constexpr FunctionSigs via non-const pointers.

R=jkummerow@chromium.org

Bug: v8:10155
Change-Id: Ieb658ab5582bff276f76babdaf7ddb8f72bd4790
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2072739
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66478}
2020-02-27 09:44:42 +00:00

186 lines
6.6 KiB
C++

// Copyright 2017 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 <cstdint>
#include "src/base/overflowing-math.h"
#include "src/codegen/assembler-inl.h"
#include "src/objects/objects-inl.h"
#include "src/wasm/wasm-arguments.h"
#include "src/wasm/wasm-objects.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/value-helper.h"
#include "test/cctest/wasm/wasm-run-utils.h"
#include "test/common/wasm/wasm-macro-gen.h"
namespace v8 {
namespace internal {
namespace wasm {
/**
* We test the interface from C to compiled wasm code by generating a wasm
* function, creating a corresponding signature, compiling the c wasm entry for
* that signature, and then calling that entry using different test values.
* The result is compared against the expected result, computed from a lambda
* passed to the CWasmEntryArgTester.
*/
namespace {
template <typename ReturnType, typename... Args>
class CWasmEntryArgTester {
public:
CWasmEntryArgTester(std::initializer_list<uint8_t> wasm_function_bytes,
std::function<ReturnType(Args...)> expected_fn)
: runner_(ExecutionTier::kTurbofan),
isolate_(runner_.main_isolate()),
expected_fn_(expected_fn),
sig_(runner_.template CreateSig<ReturnType, Args...>()) {
std::vector<uint8_t> code{wasm_function_bytes};
runner_.Build(code.data(), code.data() + code.size());
wasm_code_ = runner_.builder().GetFunctionCode(0);
Handle<WasmInstanceObject> instance(runner_.builder().instance_object());
Handle<WasmDebugInfo> debug_info =
WasmInstanceObject::GetOrCreateDebugInfo(instance);
c_wasm_entry_ = WasmDebugInfo::GetCWasmEntry(debug_info, sig_);
}
template <typename... Rest>
void WriteToBuffer(CWasmArgumentsPacker* packer, Rest... rest) {
static_assert(sizeof...(rest) == 0, "this is the base case");
}
template <typename First, typename... Rest>
void WriteToBuffer(CWasmArgumentsPacker* packer, First first, Rest... rest) {
packer->Push(first);
WriteToBuffer(packer, rest...);
}
void CheckCall(Args... args) {
CWasmArgumentsPacker packer(CWasmArgumentsPacker::TotalSize(sig_));
WriteToBuffer(&packer, args...);
Address wasm_call_target = wasm_code_->instruction_start();
Handle<Object> object_ref = runner_.builder().instance_object();
wasm_code_->native_module()->SetExecutable(true);
Execution::CallWasm(isolate_, c_wasm_entry_, wasm_call_target, object_ref,
packer.argv());
CHECK(!isolate_->has_pending_exception());
packer.Reset();
// Check the result.
ReturnType result = packer.Pop<ReturnType>();
ReturnType expected = expected_fn_(args...);
if (std::is_floating_point<ReturnType>::value) {
CHECK_DOUBLE_EQ(expected, result);
} else {
CHECK_EQ(expected, result);
}
}
private:
WasmRunner<ReturnType, Args...> runner_;
Isolate* isolate_;
std::function<ReturnType(Args...)> expected_fn_;
const FunctionSig* sig_;
Handle<Code> c_wasm_entry_;
WasmCode* wasm_code_;
};
} // namespace
// Pass int32_t, return int32_t.
TEST(TestCWasmEntryArgPassing_int32) {
CWasmEntryArgTester<int32_t, int32_t> tester(
{// Return 2*<0> + 1.
WASM_I32_ADD(WASM_I32_MUL(WASM_I32V_1(2), WASM_GET_LOCAL(0)), WASM_ONE)},
[](int32_t a) {
return base::AddWithWraparound(base::MulWithWraparound(2, a), 1);
});
FOR_INT32_INPUTS(v) { tester.CheckCall(v); }
}
// Pass int64_t, return double.
TEST(TestCWasmEntryArgPassing_double_int64) {
CWasmEntryArgTester<double, int64_t> tester(
{// Return (double)<0>.
WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(0))},
[](int64_t a) { return static_cast<double>(a); });
FOR_INT64_INPUTS(v) { tester.CheckCall(v); }
}
// Pass double, return int64_t.
TEST(TestCWasmEntryArgPassing_int64_double) {
CWasmEntryArgTester<int64_t, double> tester(
{// Return (int64_t)<0>.
WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0))},
[](double d) { return static_cast<int64_t>(d); });
FOR_INT64_INPUTS(i) { tester.CheckCall(i); }
}
// Pass float, return double.
TEST(TestCWasmEntryArgPassing_float_double) {
CWasmEntryArgTester<double, float> tester(
{// Return 2*(double)<0> + 1.
WASM_F64_ADD(
WASM_F64_MUL(WASM_F64(2), WASM_F64_CONVERT_F32(WASM_GET_LOCAL(0))),
WASM_F64(1))},
[](float f) { return 2. * static_cast<double>(f) + 1.; });
FOR_FLOAT32_INPUTS(f) { tester.CheckCall(f); }
}
// Pass two doubles, return double.
TEST(TestCWasmEntryArgPassing_double_double) {
CWasmEntryArgTester<double, double, double> tester(
{// Return <0> + <1>.
WASM_F64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))},
[](double a, double b) { return a + b; });
FOR_FLOAT64_INPUTS(d1) {
FOR_FLOAT64_INPUTS(d2) { tester.CheckCall(d1, d2); }
}
}
// Pass int32_t, int64_t, float and double, return double.
TEST(TestCWasmEntryArgPassing_AllTypes) {
CWasmEntryArgTester<double, int32_t, int64_t, float, double> tester(
{
// Convert all arguments to double, add them and return the sum.
WASM_F64_ADD( // <0+1+2> + <3>
WASM_F64_ADD( // <0+1> + <2>
WASM_F64_ADD( // <0> + <1>
WASM_F64_SCONVERT_I32(
WASM_GET_LOCAL(0)), // <0> to double
WASM_F64_SCONVERT_I64(
WASM_GET_LOCAL(1))), // <1> to double
WASM_F64_CONVERT_F32(WASM_GET_LOCAL(2))), // <2> to double
WASM_GET_LOCAL(3)) // <3>
},
[](int32_t a, int64_t b, float c, double d) {
return 0. + a + b + c + d;
});
Vector<const int32_t> test_values_i32 = compiler::ValueHelper::int32_vector();
Vector<const int64_t> test_values_i64 = compiler::ValueHelper::int64_vector();
Vector<const float> test_values_f32 = compiler::ValueHelper::float32_vector();
Vector<const double> test_values_f64 =
compiler::ValueHelper::float64_vector();
size_t max_len =
std::max(std::max(test_values_i32.size(), test_values_i64.size()),
std::max(test_values_f32.size(), test_values_f64.size()));
for (size_t i = 0; i < max_len; ++i) {
int32_t i32 = test_values_i32[i % test_values_i32.size()];
int64_t i64 = test_values_i64[i % test_values_i64.size()];
float f32 = test_values_f32[i % test_values_f32.size()];
double f64 = test_values_f64[i % test_values_f64.size()];
tester.CheckCall(i32, i64, f32, f64);
}
}
} // namespace wasm
} // namespace internal
} // namespace v8