Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef WASM_RUN_UTILS_H
6 : #define WASM_RUN_UTILS_H
7 :
8 : #include <setjmp.h>
9 : #include <stdint.h>
10 : #include <stdlib.h>
11 : #include <string.h>
12 : #include <array>
13 : #include <memory>
14 :
15 : #include "src/base/utils/random-number-generator.h"
16 : #include "src/compiler/compiler-source-position-table.h"
17 : #include "src/compiler/graph-visualizer.h"
18 : #include "src/compiler/int64-lowering.h"
19 : #include "src/compiler/js-graph.h"
20 : #include "src/compiler/node.h"
21 : #include "src/compiler/pipeline.h"
22 : #include "src/compiler/wasm-compiler.h"
23 : #include "src/compiler/zone-stats.h"
24 : #include "src/trap-handler/trap-handler.h"
25 : #include "src/wasm/function-body-decoder.h"
26 : #include "src/wasm/local-decl-encoder.h"
27 : #include "src/wasm/wasm-code-manager.h"
28 : #include "src/wasm/wasm-external-refs.h"
29 : #include "src/wasm/wasm-interpreter.h"
30 : #include "src/wasm/wasm-js.h"
31 : #include "src/wasm/wasm-module.h"
32 : #include "src/wasm/wasm-objects-inl.h"
33 : #include "src/wasm/wasm-objects.h"
34 : #include "src/wasm/wasm-opcodes.h"
35 : #include "src/wasm/wasm-tier.h"
36 : #include "src/zone/accounting-allocator.h"
37 : #include "src/zone/zone.h"
38 :
39 : #include "test/cctest/cctest.h"
40 : #include "test/cctest/compiler/call-tester.h"
41 : #include "test/cctest/compiler/graph-builder-tester.h"
42 : #include "test/common/wasm/flag-utils.h"
43 :
44 : namespace v8 {
45 : namespace internal {
46 : namespace wasm {
47 :
48 : constexpr uint32_t kMaxFunctions = 10;
49 : constexpr uint32_t kMaxGlobalsSize = 128;
50 :
51 : using compiler::CallDescriptor;
52 : using compiler::MachineTypeForC;
53 : using compiler::Node;
54 :
55 : // TODO(titzer): check traps more robustly in tests.
56 : // Currently, in tests, we just return 0xDEADBEEF from the function in which
57 : // the trap occurs if the runtime context is not available to throw a JavaScript
58 : // exception.
59 : #define CHECK_TRAP32(x) \
60 : CHECK_EQ(0xDEADBEEF, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF)
61 : #define CHECK_TRAP64(x) \
62 : CHECK_EQ(0xDEADBEEFDEADBEEF, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
63 : #define CHECK_TRAP(x) CHECK_TRAP32(x)
64 :
65 : #define WASM_WRAPPER_RETURN_VALUE 8754
66 :
67 : #define BUILD(r, ...) \
68 : do { \
69 : byte code[] = {__VA_ARGS__}; \
70 : r.Build(code, code + arraysize(code)); \
71 : } while (false)
72 :
73 : // For tests that must manually import a JSFunction with source code.
74 : struct ManuallyImportedJSFunction {
75 : FunctionSig* sig;
76 : Handle<JSFunction> js_function;
77 : };
78 :
79 : // A Wasm module builder. Globals are pre-set, however, memory and code may be
80 : // progressively added by a test. In turn, we piecemeal update the runtime
81 : // objects, i.e. {WasmInstanceObject}, {WasmModuleObject} and, if necessary,
82 : // the interpreter.
83 : class TestingModuleBuilder {
84 : public:
85 : TestingModuleBuilder(Zone*, ManuallyImportedJSFunction*, ExecutionTier,
86 : RuntimeExceptionSupport, LowerSimd);
87 :
88 480 : void ChangeOriginToAsmjs() { test_module_->origin = kAsmJsOrigin; }
89 :
90 : byte* AddMemory(uint32_t size);
91 :
92 : size_t CodeTableLength() const { return native_module_->num_functions(); }
93 :
94 : template <typename T>
95 44200 : T* AddMemoryElems(uint32_t count) {
96 49720 : AddMemory(count * sizeof(T));
97 : return raw_mem_start<T>();
98 : }
99 :
100 : template <typename T>
101 : T* AddGlobal(
102 270 : ValueType type = ValueTypes::ValueTypeFor(MachineTypeForC<T>())) {
103 23740 : const WasmGlobal* global = AddGlobal(type);
104 23725 : return reinterpret_cast<T*>(globals_data_ + global->offset);
105 : }
106 :
107 3885 : byte AddSignature(FunctionSig* sig) {
108 : DCHECK_EQ(test_module_->signatures.size(),
109 : test_module_->signature_ids.size());
110 15540 : test_module_->signatures.push_back(sig);
111 7770 : auto canonical_sig_num = test_module_->signature_map.FindOrInsert(*sig);
112 3885 : test_module_->signature_ids.push_back(canonical_sig_num);
113 3885 : size_t size = test_module_->signatures.size();
114 3885 : CHECK_GT(127, size);
115 3885 : return static_cast<byte>(size - 1);
116 : }
117 :
118 : template <typename T>
119 : T* raw_mem_start() {
120 : DCHECK(mem_start_);
121 : return reinterpret_cast<T*>(mem_start_);
122 : }
123 :
124 : template <typename T>
125 : T* raw_mem_end() {
126 : DCHECK(mem_start_);
127 19455 : return reinterpret_cast<T*>(mem_start_ + mem_size_);
128 : }
129 :
130 : template <typename T>
131 : T raw_mem_at(int i) {
132 : DCHECK(mem_start_);
133 73200 : return ReadMemory(&(reinterpret_cast<T*>(mem_start_)[i]));
134 : }
135 :
136 : template <typename T>
137 : T raw_val_at(int i) {
138 990 : return ReadMemory(reinterpret_cast<T*>(mem_start_ + i));
139 : }
140 :
141 : template <typename T>
142 : void WriteMemory(T* p, T val) {
143 : WriteLittleEndianValue<T>(reinterpret_cast<Address>(p), val);
144 : }
145 :
146 : template <typename T>
147 : T ReadMemory(T* p) {
148 : return ReadLittleEndianValue<T>(reinterpret_cast<Address>(p));
149 : }
150 :
151 : // Zero-initialize the memory.
152 735 : void BlankMemory() {
153 : byte* raw = raw_mem_start<byte>();
154 735 : memset(raw, 0, mem_size_);
155 : }
156 :
157 : // Pseudo-randomly intialize the memory.
158 19455 : void RandomizeMemory(unsigned int seed = 88) {
159 : byte* raw = raw_mem_start<byte>();
160 : byte* end = raw_mem_end<byte>();
161 19455 : v8::base::RandomNumberGenerator rng;
162 19455 : rng.SetSeed(seed);
163 19455 : rng.NextBytes(raw, end - raw);
164 19455 : }
165 :
166 10 : void SetMaxMemPages(uint32_t maximum_pages) {
167 10 : test_module_->maximum_pages = maximum_pages;
168 20 : if (instance_object()->has_memory_object()) {
169 30 : instance_object()->memory_object()->set_maximum_pages(maximum_pages);
170 : }
171 10 : }
172 :
173 1230 : void SetHasSharedMemory() { test_module_->has_shared_memory = true; }
174 :
175 : enum FunctionType { kImport, kWasm };
176 : uint32_t AddFunction(FunctionSig* sig, const char* name, FunctionType type);
177 :
178 : Handle<JSFunction> WrapCode(uint32_t index);
179 :
180 : void AddIndirectFunctionTable(const uint16_t* function_indexes,
181 : uint32_t table_size);
182 :
183 : void PopulateIndirectFunctionTable();
184 :
185 : uint32_t AddBytes(Vector<const byte> bytes);
186 :
187 : WasmFunction* GetFunctionAt(int index) {
188 1377410 : return &test_module_->functions[index];
189 : }
190 :
191 : WasmInterpreter* interpreter() const { return interpreter_; }
192 : bool interpret() const { return interpreter_ != nullptr; }
193 : LowerSimd lower_simd() const { return lower_simd_; }
194 : Isolate* isolate() const { return isolate_; }
195 : Handle<WasmInstanceObject> instance_object() const {
196 : return instance_object_;
197 : }
198 : WasmCode* GetFunctionCode(uint32_t index) const {
199 : return native_module_->code(index);
200 : }
201 : Address globals_start() const {
202 : return reinterpret_cast<Address>(globals_data_);
203 : }
204 : void Link() {
205 11542240 : if (linked_) return;
206 910935 : linked_ = true;
207 910935 : native_module_->SetExecutable(true);
208 : }
209 :
210 : CompilationEnv CreateCompilationEnv();
211 :
212 : ExecutionTier execution_tier() const { return execution_tier_; }
213 :
214 : RuntimeExceptionSupport runtime_exception_support() const {
215 : return runtime_exception_support_;
216 : }
217 :
218 : private:
219 : std::shared_ptr<WasmModule> test_module_;
220 : WasmModule* test_module_ptr_;
221 : Isolate* isolate_;
222 : WasmFeatures enabled_features_;
223 : uint32_t global_offset = 0;
224 : byte* mem_start_ = nullptr;
225 : uint32_t mem_size_ = 0;
226 : alignas(16) byte globals_data_[kMaxGlobalsSize];
227 : WasmInterpreter* interpreter_ = nullptr;
228 : ExecutionTier execution_tier_;
229 : Handle<WasmInstanceObject> instance_object_;
230 : NativeModule* native_module_ = nullptr;
231 : bool linked_ = false;
232 : RuntimeExceptionSupport runtime_exception_support_;
233 : LowerSimd lower_simd_;
234 :
235 : const WasmGlobal* AddGlobal(ValueType type);
236 :
237 : Handle<WasmInstanceObject> InitInstanceObject();
238 : };
239 :
240 : void TestBuildingGraph(Zone* zone, compiler::JSGraph* jsgraph,
241 : CompilationEnv* module, FunctionSig* sig,
242 : compiler::SourcePositionTable* source_position_table,
243 : const byte* start, const byte* end);
244 :
245 : class WasmFunctionWrapper : private compiler::GraphAndBuilders {
246 : public:
247 : WasmFunctionWrapper(Zone* zone, int num_params);
248 :
249 : void Init(CallDescriptor* call_descriptor, MachineType return_type,
250 : Vector<MachineType> param_types);
251 :
252 : template <typename ReturnType, typename... ParamTypes>
253 910990 : void Init(CallDescriptor* call_descriptor) {
254 : std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
255 20130 : {MachineTypeForC<ParamTypes>()...}};
256 : Vector<MachineType> param_vec(param_machine_types.data(),
257 : param_machine_types.size());
258 910990 : Init(call_descriptor, MachineTypeForC<ReturnType>(), param_vec);
259 910990 : }
260 :
261 11537070 : void SetInnerCode(WasmCode* code) {
262 : intptr_t address = static_cast<intptr_t>(code->instruction_start());
263 : compiler::NodeProperties::ChangeOp(
264 : inner_code_node_,
265 11537070 : common()->ExternalConstant(ExternalReference::FromRawAddress(address)));
266 11537070 : }
267 :
268 910990 : const compiler::Operator* IntPtrConstant(intptr_t value) {
269 : return machine()->Is32()
270 0 : ? common()->Int32Constant(static_cast<int32_t>(value))
271 910990 : : common()->Int64Constant(static_cast<int64_t>(value));
272 : }
273 :
274 11537070 : void SetInstance(Handle<WasmInstanceObject> instance) {
275 : compiler::NodeProperties::ChangeOp(context_address_,
276 11537070 : common()->HeapConstant(instance));
277 11537070 : }
278 :
279 : Handle<Code> GetWrapperCode();
280 :
281 : Signature<MachineType>* signature() const { return signature_; }
282 :
283 : private:
284 : Node* inner_code_node_;
285 : Node* context_address_;
286 : MaybeHandle<Code> code_;
287 : Signature<MachineType>* signature_;
288 : };
289 :
290 : // A helper for compiling wasm functions for testing.
291 : // It contains the internal state for compilation (i.e. TurboFan graph) and
292 : // interpretation (by adding to the interpreter manually).
293 1377370 : class WasmFunctionCompiler : public compiler::GraphAndBuilders {
294 : public:
295 : ~WasmFunctionCompiler();
296 :
297 4111950 : Isolate* isolate() { return builder_->isolate(); }
298 910990 : CallDescriptor* descriptor() {
299 910990 : if (descriptor_ == nullptr) {
300 1821980 : descriptor_ = compiler::GetWasmCallDescriptor(zone(), sig);
301 : }
302 910990 : return descriptor_;
303 : }
304 8990 : uint32_t function_index() { return function_->func_index; }
305 :
306 : void Build(const byte* start, const byte* end);
307 :
308 : byte AllocateLocal(ValueType type) {
309 242250 : uint32_t index = local_decls.AddLocals(1, type);
310 6815 : byte result = static_cast<byte>(index);
311 : DCHECK_EQ(index, result);
312 : return result;
313 : }
314 :
315 120 : void SetSigIndex(int sig_index) { function_->sig_index = sig_index; }
316 :
317 : private:
318 : friend class WasmRunnerBase;
319 :
320 : WasmFunctionCompiler(Zone* zone, FunctionSig* sig,
321 : TestingModuleBuilder* builder, const char* name);
322 :
323 : compiler::JSGraph jsgraph;
324 : FunctionSig* sig;
325 : // The call descriptor is initialized when the function is compiled.
326 : CallDescriptor* descriptor_;
327 : TestingModuleBuilder* builder_;
328 : WasmFunction* function_;
329 : LocalDeclEncoder local_decls;
330 : compiler::SourcePositionTable source_position_table_;
331 : WasmInterpreter* interpreter_;
332 : };
333 :
334 : // A helper class to build a module around Wasm bytecode, generate machine
335 : // code, and run that code.
336 2733460 : class WasmRunnerBase : public HandleAndZoneScope {
337 : public:
338 1366730 : WasmRunnerBase(ManuallyImportedJSFunction* maybe_import,
339 : ExecutionTier execution_tier, int num_params,
340 : RuntimeExceptionSupport runtime_exception_support,
341 : LowerSimd lower_simd)
342 : : zone_(&allocator_, ZONE_NAME),
343 : builder_(&zone_, maybe_import, execution_tier,
344 : runtime_exception_support, lower_simd),
345 2733460 : wrapper_(&zone_, num_params) {}
346 :
347 : // Builds a graph from the given Wasm code and generates the machine
348 : // code and call wrapper for that graph. This method must not be called
349 : // more than once.
350 1360010 : void Build(const byte* start, const byte* end) {
351 1360010 : CHECK(!compiled_);
352 1360010 : compiled_ = true;
353 2720020 : functions_[0]->Build(start, end);
354 1360010 : }
355 :
356 : // Resets the state for building the next function.
357 : // The main function called will always be the first function.
358 : template <typename ReturnType, typename... ParamTypes>
359 1366945 : WasmFunctionCompiler& NewFunction(const char* name = nullptr) {
360 1366945 : return NewFunction(CreateSig<ReturnType, ParamTypes...>(), name);
361 : }
362 :
363 : // Resets the state for building the next function.
364 : // The main function called will be the last generated function.
365 : // Returns the index of the previously built function.
366 1377370 : WasmFunctionCompiler& NewFunction(FunctionSig* sig,
367 : const char* name = nullptr) {
368 : functions_.emplace_back(
369 1377370 : new WasmFunctionCompiler(&zone_, sig, &builder_, name));
370 1377370 : return *functions_.back();
371 : }
372 :
373 : byte AllocateLocal(ValueType type) {
374 242240 : return functions_[0]->AllocateLocal(type);
375 : }
376 :
377 200 : uint32_t function_index() { return functions_[0]->function_index(); }
378 11548730 : WasmFunction* function() { return functions_[0]->function_; }
379 : WasmInterpreter* interpreter() {
380 : DCHECK(interpret());
381 11537980 : return functions_[0]->interpreter_;
382 : }
383 : bool possible_nondeterminism() { return possible_nondeterminism_; }
384 : TestingModuleBuilder& builder() { return builder_; }
385 : Zone* zone() { return &zone_; }
386 :
387 18672770 : bool interpret() { return builder_.interpret(); }
388 :
389 : template <typename ReturnType, typename... ParamTypes>
390 1366975 : FunctionSig* CreateSig() {
391 : std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
392 30320 : {MachineTypeForC<ParamTypes>()...}};
393 : Vector<MachineType> param_vec(param_machine_types.data(),
394 : param_machine_types.size());
395 1366975 : return CreateSig(MachineTypeForC<ReturnType>(), param_vec);
396 : }
397 :
398 : private:
399 : FunctionSig* CreateSig(MachineType return_type,
400 : Vector<MachineType> param_types);
401 :
402 : protected:
403 : v8::internal::AccountingAllocator allocator_;
404 : Zone zone_;
405 : TestingModuleBuilder builder_;
406 : std::vector<std::unique_ptr<WasmFunctionCompiler>> functions_;
407 : WasmFunctionWrapper wrapper_;
408 : bool compiled_ = false;
409 : bool possible_nondeterminism_ = false;
410 :
411 : public:
412 : // This field has to be static. Otherwise, gcc complains about the use in
413 : // the lambda context below.
414 : static bool trap_happened;
415 : };
416 :
417 : template <typename ReturnType, typename... ParamTypes>
418 1366730 : class WasmRunner : public WasmRunnerBase {
419 : public:
420 1366730 : WasmRunner(ExecutionTier execution_tier,
421 : ManuallyImportedJSFunction* maybe_import = nullptr,
422 : const char* main_fn_name = "main",
423 : RuntimeExceptionSupport runtime_exception_support =
424 : kNoRuntimeExceptionSupport,
425 : LowerSimd lower_simd = kNoLowerSimd)
426 : : WasmRunnerBase(maybe_import, execution_tier, sizeof...(ParamTypes),
427 1366730 : runtime_exception_support, lower_simd) {
428 1366730 : NewFunction<ReturnType, ParamTypes...>(main_fn_name);
429 1366730 : if (!interpret()) {
430 1821980 : wrapper_.Init<ReturnType, ParamTypes...>(functions_[0]->descriptor());
431 : }
432 1366730 : }
433 :
434 : WasmRunner(ExecutionTier execution_tier, LowerSimd lower_simd)
435 : : WasmRunner(execution_tier, nullptr, "main", kNoRuntimeExceptionSupport,
436 15810 : lower_simd) {}
437 :
438 17306040 : ReturnType Call(ParamTypes... p) {
439 : DCHECK(compiled_);
440 17306040 : if (interpret()) return CallInterpreter(p...);
441 :
442 11537070 : ReturnType return_value = static_cast<ReturnType>(0xDEADBEEFDEADBEEF);
443 11537070 : WasmRunnerBase::trap_happened = false;
444 :
445 180780 : auto trap_callback = []() -> void {
446 90390 : WasmRunnerBase::trap_happened = true;
447 90390 : set_trap_callback_for_testing(nullptr);
448 0 : };
449 11537070 : set_trap_callback_for_testing(trap_callback);
450 :
451 46148280 : wrapper_.SetInnerCode(builder_.GetFunctionCode(0));
452 11537070 : wrapper_.SetInstance(builder_.instance_object());
453 : builder_.Link();
454 11537070 : Handle<Code> wrapper_code = wrapper_.GetWrapperCode();
455 : compiler::CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
456 11537070 : wrapper_code, wrapper_.signature());
457 : int32_t result;
458 : {
459 : trap_handler::SetThreadInWasm();
460 :
461 11537070 : result = runner.Call(static_cast<void*>(&p)...,
462 : static_cast<void*>(&return_value));
463 :
464 : trap_handler::ClearThreadInWasm();
465 : }
466 11537070 : CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
467 : return WasmRunnerBase::trap_happened
468 : ? static_cast<ReturnType>(0xDEADBEEFDEADBEEF)
469 11537070 : : return_value;
470 : }
471 :
472 5768970 : ReturnType CallInterpreter(ParamTypes... p) {
473 5768970 : WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
474 5768970 : thread->Reset();
475 : std::array<WasmValue, sizeof...(p)> args{{WasmValue(p)...}};
476 5768970 : thread->InitFrame(function(), args.data());
477 5768970 : if (thread->Run() == WasmInterpreter::FINISHED) {
478 5723770 : WasmValue val = thread->GetReturnValue();
479 5723770 : possible_nondeterminism_ |= thread->PossibleNondeterminism();
480 : return val.to<ReturnType>();
481 45200 : } else if (thread->state() == WasmInterpreter::TRAPPED) {
482 : // TODO(titzer): return the correct trap code
483 : int64_t result = 0xDEADBEEFDEADBEEF;
484 : return static_cast<ReturnType>(result);
485 : } else {
486 : // TODO(titzer): falling off end
487 0 : return ReturnType{0};
488 : }
489 : }
490 :
491 : Handle<Code> GetWrapperCode() { return wrapper_.GetWrapperCode(); }
492 : };
493 :
494 : // A macro to define tests that run in different engine configurations.
495 : #define WASM_EXEC_TEST(name) \
496 : void RunWasm_##name(ExecutionTier execution_tier); \
497 : TEST(RunWasmTurbofan_##name) { RunWasm_##name(ExecutionTier::kOptimized); } \
498 : TEST(RunWasmLiftoff_##name) { RunWasm_##name(ExecutionTier::kBaseline); } \
499 : TEST(RunWasmInterpreter_##name) { \
500 : RunWasm_##name(ExecutionTier::kInterpreter); \
501 : } \
502 : void RunWasm_##name(ExecutionTier execution_tier)
503 :
504 : #define WASM_COMPILED_EXEC_TEST(name) \
505 : void RunWasm_##name(ExecutionTier execution_tier); \
506 : TEST(RunWasmTurbofan_##name) { RunWasm_##name(ExecutionTier::kOptimized); } \
507 : TEST(RunWasmLiftoff_##name) { RunWasm_##name(ExecutionTier::kBaseline); } \
508 : void RunWasm_##name(ExecutionTier execution_tier)
509 :
510 : } // namespace wasm
511 : } // namespace internal
512 : } // namespace v8
513 :
514 : #endif
|