Index: test/cctest/wasm/test-run-wasm.cc |
diff --git a/test/cctest/wasm/test-run-wasm.cc b/test/cctest/wasm/test-run-wasm.cc |
index 445c3f0aedb73c2f9a806fcc21f95a964571a86a..c6f529e35a12d5addc0a56b94634ec9b14c4696e 100644 |
--- a/test/cctest/wasm/test-run-wasm.cc |
+++ b/test/cctest/wasm/test-run-wasm.cc |
@@ -6,17 +6,390 @@ |
#include <stdlib.h> |
#include <string.h> |
+#include "src/base/utils/random-number-generator.h" |
+ |
+#include "src/compiler/graph-visualizer.h" |
+#include "src/compiler/js-graph.h" |
+#include "src/compiler/wasm-compiler.h" |
+ |
+#include "src/wasm/ast-decoder.h" |
#include "src/wasm/wasm-macro-gen.h" |
+#include "src/wasm/wasm-module.h" |
+#include "src/wasm/wasm-opcodes.h" |
#include "test/cctest/cctest.h" |
+#include "test/cctest/compiler/codegen-tester.h" |
+#include "test/cctest/compiler/graph-builder-tester.h" |
#include "test/cctest/compiler/value-helper.h" |
+ |
#include "test/cctest/wasm/test-signatures.h" |
-#include "test/cctest/wasm/wasm-run-utils.h" |
+ |
+// TODO(titzer): pull WASM_64 up to a common header. |
+#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 |
+#define WASM_64 1 |
+#else |
+#define WASM_64 0 |
+#endif |
+ |
+// TODO(titzer): check traps more robustly in tests. |
+// Currently, in tests, we just return 0xdeadbeef from the function in which |
+// the trap occurs if the runtime context is not available to throw a JavaScript |
+// exception. |
+#define CHECK_TRAP32(x) \ |
+ CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF) |
+#define CHECK_TRAP64(x) \ |
+ CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF) |
+#define CHECK_TRAP(x) CHECK_TRAP32(x) |
+ |
using namespace v8::base; |
using namespace v8::internal; |
using namespace v8::internal::compiler; |
using namespace v8::internal::wasm; |
+ |
+static void init_env(FunctionEnv* env, FunctionSig* sig) { |
+ env->module = nullptr; |
+ env->sig = sig; |
+ env->local_int32_count = 0; |
+ env->local_int64_count = 0; |
+ env->local_float32_count = 0; |
+ env->local_float64_count = 0; |
+ env->SumLocals(); |
+} |
+ |
+const uint32_t kMaxGlobalsSize = 128; |
+ |
+// A helper for module environments that adds the ability to allocate memory |
+// and global variables. |
+class TestingModule : public ModuleEnv { |
+ public: |
+ TestingModule() : mem_size(0), global_offset(0) { |
+ globals_area = 0; |
+ mem_start = 0; |
+ mem_end = 0; |
+ module = nullptr; |
+ linker = nullptr; |
+ function_code = nullptr; |
+ asm_js = false; |
+ memset(global_data, 0, sizeof(global_data)); |
+ } |
+ |
+ ~TestingModule() { |
+ if (mem_start) { |
+ free(raw_mem_start<byte>()); |
+ } |
+ if (function_code) delete function_code; |
+ if (module) delete module; |
+ } |
+ |
+ byte* AddMemory(size_t size) { |
+ CHECK_EQ(0, mem_start); |
+ CHECK_EQ(0, mem_size); |
+ mem_start = reinterpret_cast<uintptr_t>(malloc(size)); |
+ CHECK(mem_start); |
+ byte* raw = raw_mem_start<byte>(); |
+ memset(raw, 0, size); |
+ mem_end = mem_start + size; |
+ mem_size = size; |
+ return raw_mem_start<byte>(); |
+ } |
+ |
+ template <typename T> |
+ T* AddMemoryElems(size_t count) { |
+ AddMemory(count * sizeof(T)); |
+ return raw_mem_start<T>(); |
+ } |
+ |
+ template <typename T> |
+ T* AddGlobal(MachineType mem_type) { |
+ WasmGlobal* global = AddGlobal(mem_type); |
+ return reinterpret_cast<T*>(globals_area + global->offset); |
+ } |
+ |
+ byte AddSignature(FunctionSig* sig) { |
+ AllocModule(); |
+ if (!module->signatures) { |
+ module->signatures = new std::vector<FunctionSig*>(); |
+ } |
+ module->signatures->push_back(sig); |
+ size_t size = module->signatures->size(); |
+ CHECK(size < 127); |
+ return static_cast<byte>(size - 1); |
+ } |
+ |
+ template <typename T> |
+ T* raw_mem_start() { |
+ DCHECK(mem_start); |
+ return reinterpret_cast<T*>(mem_start); |
+ } |
+ |
+ template <typename T> |
+ T* raw_mem_end() { |
+ DCHECK(mem_end); |
+ return reinterpret_cast<T*>(mem_end); |
+ } |
+ |
+ template <typename T> |
+ T raw_mem_at(int i) { |
+ DCHECK(mem_start); |
+ return reinterpret_cast<T*>(mem_start)[i]; |
+ } |
+ |
+ template <typename T> |
+ T raw_val_at(int i) { |
+ T val; |
+ memcpy(&val, reinterpret_cast<void*>(mem_start + i), sizeof(T)); |
+ return val; |
+ } |
+ |
+ // Zero-initialize the memory. |
+ void BlankMemory() { |
+ byte* raw = raw_mem_start<byte>(); |
+ memset(raw, 0, mem_size); |
+ } |
+ |
+ // Pseudo-randomly intialize the memory. |
+ void RandomizeMemory(unsigned int seed = 88) { |
+ byte* raw = raw_mem_start<byte>(); |
+ byte* end = raw_mem_end<byte>(); |
+ v8::base::RandomNumberGenerator rng; |
+ rng.SetSeed(seed); |
+ rng.NextBytes(raw, end - raw); |
+ } |
+ |
+ WasmFunction* AddFunction(FunctionSig* sig, Handle<Code> code) { |
+ AllocModule(); |
+ if (module->functions == nullptr) { |
+ module->functions = new std::vector<WasmFunction>(); |
+ function_code = new std::vector<Handle<Code>>(); |
+ } |
+ module->functions->push_back({sig, 0, 0, 0, 0, 0, 0, 0, false, false}); |
+ function_code->push_back(code); |
+ return &module->functions->back(); |
+ } |
+ |
+ private: |
+ size_t mem_size; |
+ uint32_t global_offset; |
+ byte global_data[kMaxGlobalsSize]; |
+ |
+ WasmGlobal* AddGlobal(MachineType mem_type) { |
+ AllocModule(); |
+ if (globals_area == 0) { |
+ globals_area = reinterpret_cast<uintptr_t>(global_data); |
+ module->globals = new std::vector<WasmGlobal>(); |
+ } |
+ byte size = WasmOpcodes::MemSize(mem_type); |
+ global_offset = (global_offset + size - 1) & ~(size - 1); // align |
+ module->globals->push_back({0, mem_type, global_offset, false}); |
+ global_offset += size; |
+ // limit number of globals. |
+ CHECK_LT(global_offset, kMaxGlobalsSize); |
+ return &module->globals->back(); |
+ } |
+ void AllocModule() { |
+ if (module == nullptr) { |
+ module = new WasmModule(); |
+ module->globals = nullptr; |
+ module->functions = nullptr; |
+ module->data_segments = nullptr; |
+ } |
+ } |
+}; |
+ |
+ |
+static void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, FunctionEnv* env, |
+ const byte* start, const byte* end) { |
+ compiler::WasmGraphBuilder builder(zone, jsgraph, env->sig); |
+ TreeResult result = BuildTFGraph(&builder, env, start, end); |
+ if (result.failed()) { |
+ ptrdiff_t pc = result.error_pc - result.start; |
+ ptrdiff_t pt = result.error_pt - result.start; |
+ std::ostringstream str; |
+ str << "Verification failed: " << result.error_code << " pc = +" << pc; |
+ if (result.error_pt) str << ", pt = +" << pt; |
+ str << ", msg = " << result.error_msg.get(); |
+ FATAL(str.str().c_str()); |
+ } |
+ if (FLAG_trace_turbo_graph) { |
+ OFStream os(stdout); |
+ os << AsRPO(*jsgraph->graph()); |
+ } |
+} |
+ |
+ |
+// A helper for compiling functions that are only internally callable WASM code. |
+class WasmFunctionCompiler : public HandleAndZoneScope, |
+ private GraphAndBuilders { |
+ public: |
+ explicit WasmFunctionCompiler(FunctionSig* sig) |
+ : GraphAndBuilders(main_zone()), |
+ jsgraph(this->isolate(), this->graph(), this->common(), nullptr, |
+ nullptr, this->machine()), |
+ descriptor_(nullptr) { |
+ init_env(&env, sig); |
+ } |
+ |
+ JSGraph jsgraph; |
+ FunctionEnv env; |
+ // The call descriptor is initialized when the function is compiled. |
+ CallDescriptor* descriptor_; |
+ |
+ Isolate* isolate() { return main_isolate(); } |
+ Graph* graph() const { return main_graph_; } |
+ Zone* zone() const { return graph()->zone(); } |
+ CommonOperatorBuilder* common() { return &main_common_; } |
+ MachineOperatorBuilder* machine() { return &main_machine_; } |
+ CallDescriptor* descriptor() { return descriptor_; } |
+ |
+ void Build(const byte* start, const byte* end) { |
+ TestBuildingGraph(main_zone(), &jsgraph, &env, start, end); |
+ } |
+ |
+ byte AllocateLocal(LocalType type) { |
+ int result = static_cast<int>(env.total_locals); |
+ env.AddLocals(type, 1); |
+ byte b = static_cast<byte>(result); |
+ CHECK_EQ(result, b); |
+ return b; |
+ } |
+ |
+ Handle<Code> Compile(ModuleEnv* module) { |
+ descriptor_ = module->GetWasmCallDescriptor(this->zone(), env.sig); |
+ CompilationInfo info("wasm compile", this->isolate(), this->zone()); |
+ Handle<Code> result = |
+ Pipeline::GenerateCodeForTesting(&info, descriptor_, this->graph()); |
+#ifdef ENABLE_DISASSEMBLER |
+ if (!result.is_null() && FLAG_print_opt_code) { |
+ OFStream os(stdout); |
+ result->Disassemble("wasm code", os); |
+ } |
+#endif |
+ |
+ return result; |
+ } |
+ |
+ uint32_t CompileAndAdd(TestingModule* module) { |
+ uint32_t index = 0; |
+ if (module->module && module->module->functions) { |
+ index = static_cast<uint32_t>(module->module->functions->size()); |
+ } |
+ module->AddFunction(env.sig, Compile(module)); |
+ return index; |
+ } |
+}; |
+ |
+ |
+// A helper class to build graphs from Wasm bytecode, generate machine |
+// code, and run that code. |
+template <typename ReturnType> |
+class WasmRunner { |
+ public: |
+ WasmRunner(MachineType p0 = MachineType::None(), |
+ MachineType p1 = MachineType::None(), |
+ MachineType p2 = MachineType::None(), |
+ MachineType p3 = MachineType::None()) |
+ : signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, |
+ GetParameterCount(p0, p1, p2, p3), storage_), |
+ compiler_(&signature_), |
+ call_wrapper_(p0, p1, p2, p3), |
+ compilation_done_(false) { |
+ int index = 0; |
+ MachineType ret = MachineTypeForC<ReturnType>(); |
+ if (ret != MachineType::None()) { |
+ storage_[index++] = WasmOpcodes::LocalTypeFor(ret); |
+ } |
+ if (p0 != MachineType::None()) |
+ storage_[index++] = WasmOpcodes::LocalTypeFor(p0); |
+ if (p1 != MachineType::None()) |
+ storage_[index++] = WasmOpcodes::LocalTypeFor(p1); |
+ if (p2 != MachineType::None()) |
+ storage_[index++] = WasmOpcodes::LocalTypeFor(p2); |
+ if (p3 != MachineType::None()) |
+ storage_[index++] = WasmOpcodes::LocalTypeFor(p3); |
+ } |
+ |
+ |
+ FunctionEnv* env() { return &compiler_.env; } |
+ |
+ |
+ // Builds a graph from the given Wasm code, and generates the machine |
+ // code and call wrapper for that graph. This method must not be called |
+ // more than once. |
+ void Build(const byte* start, const byte* end) { |
+ DCHECK(!compilation_done_); |
+ compilation_done_ = true; |
+ // Build the TF graph. |
+ compiler_.Build(start, end); |
+ // Generate code. |
+ Handle<Code> code = compiler_.Compile(env()->module); |
+ |
+ // Construct the call wrapper. |
+ Node* inputs[5]; |
+ int input_count = 0; |
+ inputs[input_count++] = call_wrapper_.HeapConstant(code); |
+ for (size_t i = 0; i < signature_.parameter_count(); i++) { |
+ inputs[input_count++] = call_wrapper_.Parameter(i); |
+ } |
+ |
+ call_wrapper_.Return(call_wrapper_.AddNode( |
+ call_wrapper_.common()->Call(compiler_.descriptor()), input_count, |
+ inputs)); |
+ } |
+ |
+ |
+ ReturnType Call() { return call_wrapper_.Call(); } |
+ |
+ |
+ template <typename P0> |
+ ReturnType Call(P0 p0) { |
+ return call_wrapper_.Call(p0); |
+ } |
+ |
+ |
+ template <typename P0, typename P1> |
+ ReturnType Call(P0 p0, P1 p1) { |
+ return call_wrapper_.Call(p0, p1); |
+ } |
+ |
+ |
+ template <typename P0, typename P1, typename P2> |
+ ReturnType Call(P0 p0, P1 p1, P2 p2) { |
+ return call_wrapper_.Call(p0, p1, p2); |
+ } |
+ |
+ |
+ template <typename P0, typename P1, typename P2, typename P3> |
+ ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { |
+ return call_wrapper_.Call(p0, p1, p2, p3); |
+ } |
+ |
+ |
+ byte AllocateLocal(LocalType type) { |
+ int result = static_cast<int>(env()->total_locals); |
+ env()->AddLocals(type, 1); |
+ byte b = static_cast<byte>(result); |
+ CHECK_EQ(result, b); |
+ return b; |
+ } |
+ |
+ |
+ private: |
+ LocalType storage_[5]; |
+ FunctionSig signature_; |
+ WasmFunctionCompiler compiler_; |
+ BufferedRawMachineAssemblerTester<ReturnType> call_wrapper_; |
+ bool compilation_done_; |
+ |
+ static size_t GetParameterCount(MachineType p0, MachineType p1, |
+ MachineType p2, MachineType p3) { |
+ if (p0 == MachineType::None()) return 0; |
+ if (p1 == MachineType::None()) return 1; |
+ if (p2 == MachineType::None()) return 2; |
+ if (p3 == MachineType::None()) return 3; |
+ return 4; |
+ } |
+}; |
#define BUILD(r, ...) \ |
do { \ |
@@ -2523,7 +2896,7 @@ |
} while (false) |
-static void Run_WasmMixedCall_N(int start) { |
+void Run_WasmMixedCall_N(int start) { |
const int kExpected = 6333; |
const int kElemSize = 8; |
TestSignatures sigs; |