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/cctest/compiler/value-helper.h"
43 : #include "test/common/wasm/flag-utils.h"
44 :
45 : namespace v8 {
46 : namespace internal {
47 : namespace wasm {
48 :
49 : constexpr uint32_t kMaxFunctions = 10;
50 : constexpr uint32_t kMaxGlobalsSize = 128;
51 :
52 : using compiler::CallDescriptor;
53 : using compiler::MachineTypeForC;
54 : using compiler::Node;
55 :
56 : // TODO(titzer): check traps more robustly in tests.
57 : // Currently, in tests, we just return 0xDEADBEEF from the function in which
58 : // the trap occurs if the runtime context is not available to throw a JavaScript
59 : // exception.
60 : #define CHECK_TRAP32(x) \
61 : CHECK_EQ(0xDEADBEEF, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF)
62 : #define CHECK_TRAP64(x) \
63 : CHECK_EQ(0xDEADBEEFDEADBEEF, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
64 : #define CHECK_TRAP(x) CHECK_TRAP32(x)
65 :
66 : #define WASM_WRAPPER_RETURN_VALUE 8754
67 :
68 : #define BUILD(r, ...) \
69 : do { \
70 : byte code[] = {__VA_ARGS__}; \
71 : r.Build(code, code + arraysize(code)); \
72 : } while (false)
73 :
74 : // For tests that must manually import a JSFunction with source code.
75 : struct ManuallyImportedJSFunction {
76 : FunctionSig* sig;
77 : Handle<JSFunction> js_function;
78 : };
79 :
80 : // A Wasm module builder. Globals are pre-set, however, memory and code may be
81 : // progressively added by a test. In turn, we piecemeal update the runtime
82 : // objects, i.e. {WasmInstanceObject}, {WasmModuleObject} and, if necessary,
83 : // the interpreter.
84 : class TestingModuleBuilder {
85 : public:
86 : TestingModuleBuilder(Zone*, ManuallyImportedJSFunction*, ExecutionTier,
87 : RuntimeExceptionSupport, LowerSimd);
88 :
89 384 : void ChangeOriginToAsmjs() { test_module_->origin = kAsmJsOrigin; }
90 :
91 : byte* AddMemory(uint32_t size);
92 :
93 : size_t CodeTableLength() const { return native_module_->num_functions(); }
94 :
95 : template <typename T>
96 35492 : T* AddMemoryElems(uint32_t count) {
97 39908 : AddMemory(count * sizeof(T));
98 : return raw_mem_start<T>();
99 : }
100 :
101 : template <typename T>
102 : T* AddGlobal(
103 216 : ValueType type = ValueTypes::ValueTypeFor(MachineTypeForC<T>())) {
104 18992 : const WasmGlobal* global = AddGlobal(type);
105 18980 : return reinterpret_cast<T*>(globals_data_ + global->offset);
106 : }
107 :
108 3120 : byte AddSignature(FunctionSig* sig) {
109 : DCHECK_EQ(test_module_->signatures.size(),
110 : test_module_->signature_ids.size());
111 12480 : test_module_->signatures.push_back(sig);
112 6240 : auto canonical_sig_num = test_module_->signature_map.FindOrInsert(*sig);
113 3120 : test_module_->signature_ids.push_back(canonical_sig_num);
114 3120 : size_t size = test_module_->signatures.size();
115 3120 : CHECK_GT(127, size);
116 3120 : return static_cast<byte>(size - 1);
117 : }
118 :
119 : template <typename T>
120 : T* raw_mem_start() {
121 : DCHECK(mem_start_);
122 : return reinterpret_cast<T*>(mem_start_);
123 : }
124 :
125 : template <typename T>
126 : T* raw_mem_end() {
127 : DCHECK(mem_start_);
128 15564 : return reinterpret_cast<T*>(mem_start_ + mem_size_);
129 : }
130 :
131 : template <typename T>
132 : T raw_mem_at(int i) {
133 : DCHECK(mem_start_);
134 58560 : return ReadMemory(&(reinterpret_cast<T*>(mem_start_)[i]));
135 : }
136 :
137 : template <typename T>
138 : T raw_val_at(int i) {
139 792 : return ReadMemory(reinterpret_cast<T*>(mem_start_ + i));
140 : }
141 :
142 : template <typename T>
143 : void WriteMemory(T* p, T val) {
144 : WriteLittleEndianValue<T>(reinterpret_cast<Address>(p), val);
145 : }
146 :
147 : template <typename T>
148 : T ReadMemory(T* p) {
149 : return ReadLittleEndianValue<T>(reinterpret_cast<Address>(p));
150 : }
151 :
152 : // Zero-initialize the memory.
153 588 : void BlankMemory() {
154 : byte* raw = raw_mem_start<byte>();
155 588 : memset(raw, 0, mem_size_);
156 : }
157 :
158 : // Pseudo-randomly intialize the memory.
159 15564 : void RandomizeMemory(unsigned int seed = 88) {
160 : byte* raw = raw_mem_start<byte>();
161 : byte* end = raw_mem_end<byte>();
162 15564 : v8::base::RandomNumberGenerator rng;
163 15564 : rng.SetSeed(seed);
164 15564 : rng.NextBytes(raw, end - raw);
165 15564 : }
166 :
167 8 : void SetMaxMemPages(uint32_t maximum_pages) {
168 8 : test_module_->maximum_pages = maximum_pages;
169 16 : if (instance_object()->has_memory_object()) {
170 24 : instance_object()->memory_object()->set_maximum_pages(maximum_pages);
171 : }
172 8 : }
173 :
174 1116 : void SetHasSharedMemory() { test_module_->has_shared_memory = true; }
175 :
176 : enum FunctionType { kImport, kWasm };
177 : uint32_t AddFunction(FunctionSig* sig, const char* name, FunctionType type);
178 :
179 : // Wrap the code so it can be called as a JS function.
180 : Handle<JSFunction> WrapCode(uint32_t index);
181 :
182 : void AddIndirectFunctionTable(const uint16_t* function_indexes,
183 : uint32_t table_size);
184 :
185 : void PopulateIndirectFunctionTable();
186 :
187 : uint32_t AddBytes(Vector<const byte> bytes);
188 :
189 : uint32_t AddException(FunctionSig* sig);
190 :
191 : WasmFunction* GetFunctionAt(int index) {
192 1102204 : return &test_module_->functions[index];
193 : }
194 :
195 : WasmInterpreter* interpreter() const { return interpreter_; }
196 : bool interpret() const { return interpreter_ != nullptr; }
197 : LowerSimd lower_simd() const { return lower_simd_; }
198 : Isolate* isolate() const { return isolate_; }
199 : Handle<WasmInstanceObject> instance_object() const {
200 : return instance_object_;
201 : }
202 : WasmCode* GetFunctionCode(uint32_t index) const {
203 : return native_module_->code(index);
204 : }
205 : Address globals_start() const {
206 : return reinterpret_cast<Address>(globals_data_);
207 : }
208 :
209 9259552 : void SetExecutable() { native_module_->SetExecutable(true); }
210 :
211 : CompilationEnv CreateCompilationEnv();
212 :
213 : ExecutionTier execution_tier() const { return execution_tier_; }
214 :
215 : RuntimeExceptionSupport runtime_exception_support() const {
216 : return runtime_exception_support_;
217 : }
218 :
219 : private:
220 : std::shared_ptr<WasmModule> test_module_;
221 : WasmModule* test_module_ptr_;
222 : Isolate* isolate_;
223 : WasmFeatures enabled_features_;
224 : uint32_t global_offset = 0;
225 : byte* mem_start_ = nullptr;
226 : uint32_t mem_size_ = 0;
227 : alignas(16) byte globals_data_[kMaxGlobalsSize];
228 : WasmInterpreter* interpreter_ = nullptr;
229 : ExecutionTier execution_tier_;
230 : Handle<WasmInstanceObject> instance_object_;
231 : NativeModule* native_module_ = nullptr;
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 728952 : void Init(CallDescriptor* call_descriptor) {
254 : std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
255 16256 : {MachineTypeForC<ParamTypes>()...}};
256 : Vector<MachineType> param_vec(param_machine_types.data(),
257 : param_machine_types.size());
258 728952 : Init(call_descriptor, MachineTypeForC<ReturnType>(), param_vec);
259 728952 : }
260 :
261 9255320 : void SetInnerCode(WasmCode* code) {
262 : intptr_t address = static_cast<intptr_t>(code->instruction_start());
263 : compiler::NodeProperties::ChangeOp(
264 : inner_code_node_,
265 9255320 : common()->ExternalConstant(ExternalReference::FromRawAddress(address)));
266 9255320 : }
267 :
268 728952 : const compiler::Operator* IntPtrConstant(intptr_t value) {
269 : return machine()->Is32()
270 0 : ? common()->Int32Constant(static_cast<int32_t>(value))
271 728952 : : common()->Int64Constant(static_cast<int64_t>(value));
272 : }
273 :
274 9255320 : void SetInstance(Handle<WasmInstanceObject> instance) {
275 : compiler::NodeProperties::ChangeOp(context_address_,
276 9255320 : common()->HeapConstant(instance));
277 9255320 : }
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 1102172 : class WasmFunctionCompiler : public compiler::GraphAndBuilders {
294 : public:
295 : ~WasmFunctionCompiler();
296 :
297 2193312 : Isolate* isolate() { return builder_->isolate(); }
298 728952 : CallDescriptor* descriptor() {
299 728952 : if (descriptor_ == nullptr) {
300 1457904 : descriptor_ = compiler::GetWasmCallDescriptor(zone(), sig);
301 : }
302 728952 : return descriptor_;
303 : }
304 7240 : 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 193800 : uint32_t index = local_decls.AddLocals(1, type);
310 5452 : byte result = static_cast<byte>(index);
311 : DCHECK_EQ(index, result);
312 : return result;
313 : }
314 :
315 108 : 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 2187224 : class WasmRunnerBase : public HandleAndZoneScope {
337 : public:
338 1093612 : 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 2187224 : 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 1088248 : void Build(const byte* start, const byte* end) {
351 1088248 : CHECK(!compiled_);
352 1088248 : compiled_ = true;
353 2176496 : functions_[0]->Build(start, end);
354 1088248 : }
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 1093772 : WasmFunctionCompiler& NewFunction(const char* name = nullptr) {
360 1093772 : 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 1102172 : WasmFunctionCompiler& NewFunction(FunctionSig* sig,
367 : const char* name = nullptr) {
368 : functions_.emplace_back(
369 1102172 : new WasmFunctionCompiler(&zone_, sig, &builder_, name));
370 1102172 : return *functions_.back();
371 : }
372 :
373 : byte AllocateLocal(ValueType type) {
374 193792 : return functions_[0]->AllocateLocal(type);
375 : }
376 :
377 160 : uint32_t function_index() { return functions_[0]->function_index(); }
378 9265248 : WasmFunction* function() { return functions_[0]->function_; }
379 : WasmInterpreter* interpreter() {
380 : DCHECK(interpret());
381 9256048 : return functions_[0]->interpreter_;
382 : }
383 : bool possible_nondeterminism() { return possible_nondeterminism_; }
384 : TestingModuleBuilder& builder() { return builder_; }
385 : Zone* zone() { return &zone_; }
386 :
387 14976940 : bool interpret() { return builder_.interpret(); }
388 :
389 : template <typename ReturnType, typename... ParamTypes>
390 1093796 : FunctionSig* CreateSig() {
391 : std::array<MachineType, sizeof...(ParamTypes)> param_machine_types{
392 24484 : {MachineTypeForC<ParamTypes>()...}};
393 : Vector<MachineType> param_vec(param_machine_types.data(),
394 : param_machine_types.size());
395 1093796 : 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 2187224 : class WasmRunner : public WasmRunnerBase {
419 : public:
420 1093612 : 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 1093612 : runtime_exception_support, lower_simd) {
428 1093612 : NewFunction<ReturnType, ParamTypes...>(main_fn_name);
429 1093612 : if (!interpret()) {
430 1457904 : wrapper_.Init<ReturnType, ParamTypes...>(functions_[0]->descriptor());
431 : }
432 1093612 : }
433 :
434 : WasmRunner(ExecutionTier execution_tier, LowerSimd lower_simd)
435 : : WasmRunner(execution_tier, nullptr, "main", kNoRuntimeExceptionSupport,
436 12648 : lower_simd) {}
437 :
438 13883328 : ReturnType Call(ParamTypes... p) {
439 : DCHECK(compiled_);
440 13883328 : if (interpret()) return CallInterpreter(p...);
441 :
442 9255320 : ReturnType return_value = static_cast<ReturnType>(0xDEADBEEFDEADBEEF);
443 9255320 : WasmRunnerBase::trap_happened = false;
444 :
445 144624 : auto trap_callback = []() -> void {
446 72312 : WasmRunnerBase::trap_happened = true;
447 72312 : set_trap_callback_for_testing(nullptr);
448 0 : };
449 9255320 : set_trap_callback_for_testing(trap_callback);
450 :
451 37021280 : wrapper_.SetInnerCode(builder_.GetFunctionCode(0));
452 9255320 : wrapper_.SetInstance(builder_.instance_object());
453 : builder_.SetExecutable();
454 9255320 : Handle<Code> wrapper_code = wrapper_.GetWrapperCode();
455 : compiler::CodeRunner<int32_t> runner(CcTest::InitIsolateOnce(),
456 9255320 : wrapper_code, wrapper_.signature());
457 : int32_t result;
458 : {
459 : trap_handler::SetThreadInWasm();
460 :
461 9255320 : result = runner.Call(static_cast<void*>(&p)...,
462 : static_cast<void*>(&return_value));
463 :
464 : trap_handler::ClearThreadInWasm();
465 : }
466 9255320 : CHECK_EQ(WASM_WRAPPER_RETURN_VALUE, result);
467 : return WasmRunnerBase::trap_happened
468 : ? static_cast<ReturnType>(0xDEADBEEFDEADBEEF)
469 9255320 : : return_value;
470 : }
471 :
472 4628008 : ReturnType CallInterpreter(ParamTypes... p) {
473 4628008 : WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
474 4628008 : thread->Reset();
475 3855196 : std::array<WasmValue, sizeof...(p)> args{{WasmValue(p)...}};
476 4628008 : thread->InitFrame(function(), args.data());
477 4628008 : thread->Run();
478 9256016 : CHECK_GT(thread->NumInterpretedCalls(), 0);
479 4628008 : if (thread->state() == WasmInterpreter::FINISHED) {
480 4591848 : WasmValue val = thread->GetReturnValue();
481 4591848 : possible_nondeterminism_ |= thread->PossibleNondeterminism();
482 : return val.to<ReturnType>();
483 36160 : } else if (thread->state() == WasmInterpreter::TRAPPED) {
484 : // TODO(titzer): return the correct trap code
485 : int64_t result = 0xDEADBEEFDEADBEEF;
486 : return static_cast<ReturnType>(result);
487 : } else {
488 : // TODO(titzer): falling off end
489 0 : return ReturnType{0};
490 : }
491 : }
492 :
493 4284 : void CheckCallViaJS(double expected, uint32_t function_index,
494 : Handle<Object>* buffer, int count) {
495 8568 : Isolate* isolate = builder_.isolate();
496 17136 : if (jsfuncs_.size() <= function_index) {
497 4092 : jsfuncs_.resize(function_index + 1);
498 : }
499 4284 : if (jsfuncs_[function_index].is_null()) {
500 4092 : jsfuncs_[function_index] = builder_.WrapCode(function_index);
501 : }
502 4284 : Handle<JSFunction> jsfunc = jsfuncs_[function_index];
503 8568 : Handle<Object> global(isolate->context()->global_object(), isolate);
504 : MaybeHandle<Object> retval =
505 4284 : Execution::Call(isolate, jsfunc, global, count, buffer);
506 :
507 4284 : CHECK(!retval.is_null());
508 : Handle<Object> result = retval.ToHandleChecked();
509 8568 : if (result->IsSmi()) {
510 4776 : CHECK_EQ(expected, Smi::ToInt(*result));
511 : } else {
512 3792 : CHECK(result->IsHeapNumber());
513 1896 : CHECK_DOUBLE_EQ(expected, HeapNumber::cast(*result)->value());
514 : }
515 :
516 4284 : if (builder_.interpret()) {
517 2856 : CHECK_GT(builder_.interpreter()->GetThread(0)->NumInterpretedCalls(), 0);
518 : }
519 4284 : }
520 :
521 360 : void CheckCallViaJS(double expected, ParamTypes... p) {
522 360 : Isolate* isolate = builder_.isolate();
523 360 : uint32_t function_index = function()->func_index;
524 360 : Handle<Object> buffer[] = {isolate->factory()->NewNumber(p)...};
525 360 : CheckCallViaJS(expected, function_index, buffer, sizeof...(p));
526 360 : }
527 :
528 : Handle<Code> GetWrapperCode() { return wrapper_.GetWrapperCode(); }
529 :
530 : private:
531 : std::vector<Handle<JSFunction>> jsfuncs_;
532 : };
533 :
534 : // A macro to define tests that run in different engine configurations.
535 : #define WASM_EXEC_TEST(name) \
536 : void RunWasm_##name(ExecutionTier execution_tier); \
537 : TEST(RunWasmTurbofan_##name) { RunWasm_##name(ExecutionTier::kOptimized); } \
538 : TEST(RunWasmLiftoff_##name) { RunWasm_##name(ExecutionTier::kBaseline); } \
539 : TEST(RunWasmInterpreter_##name) { \
540 : RunWasm_##name(ExecutionTier::kInterpreter); \
541 : } \
542 : void RunWasm_##name(ExecutionTier execution_tier)
543 :
544 : #define WASM_COMPILED_EXEC_TEST(name) \
545 : void RunWasm_##name(ExecutionTier execution_tier); \
546 : TEST(RunWasmTurbofan_##name) { RunWasm_##name(ExecutionTier::kOptimized); } \
547 : TEST(RunWasmLiftoff_##name) { RunWasm_##name(ExecutionTier::kBaseline); } \
548 : void RunWasm_##name(ExecutionTier execution_tier)
549 :
550 : } // namespace wasm
551 : } // namespace internal
552 : } // namespace v8
553 :
554 : #endif
|