Line data Source code
1 : // Copyright 2017 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 : #include "test/cctest/wasm/wasm-run-utils.h"
6 :
7 : #include "src/assembler-inl.h"
8 : #include "src/code-tracer.h"
9 : #include "src/wasm/graph-builder-interface.h"
10 : #include "src/wasm/wasm-import-wrapper-cache-inl.h"
11 : #include "src/wasm/wasm-memory.h"
12 : #include "src/wasm/wasm-objects-inl.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 : namespace wasm {
17 :
18 1366730 : TestingModuleBuilder::TestingModuleBuilder(
19 : Zone* zone, ManuallyImportedJSFunction* maybe_import, ExecutionTier tier,
20 : RuntimeExceptionSupport exception_support, LowerSimd lower_simd)
21 : : test_module_(std::make_shared<WasmModule>()),
22 2733460 : test_module_ptr_(test_module_.get()),
23 1366730 : isolate_(CcTest::InitIsolateOnce()),
24 : enabled_features_(WasmFeaturesFromIsolate(isolate_)),
25 : execution_tier_(tier),
26 : runtime_exception_support_(exception_support),
27 4100190 : lower_simd_(lower_simd) {
28 1366730 : WasmJs::Install(isolate_, true);
29 1366730 : test_module_->untagged_globals_buffer_size = kMaxGlobalsSize;
30 1366730 : memset(globals_data_, 0, sizeof(globals_data_));
31 :
32 : uint32_t maybe_import_index = 0;
33 1366730 : if (maybe_import) {
34 : // Manually add an imported function before any other functions.
35 : // This must happen before the instance object is created, since the
36 : // instance object allocates import entries.
37 2460 : maybe_import_index = AddFunction(maybe_import->sig, nullptr, kImport);
38 : DCHECK_EQ(0, maybe_import_index);
39 : }
40 :
41 1366730 : instance_object_ = InitInstanceObject();
42 :
43 1366730 : if (maybe_import) {
44 : // Manually compile an import wrapper and insert it into the instance.
45 2460 : CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
46 : auto kind = compiler::GetWasmImportCallKind(maybe_import->js_function,
47 4920 : maybe_import->sig, false);
48 : auto import_wrapper = native_module_->import_wrapper_cache()->GetOrCompile(
49 7380 : isolate_->wasm_engine(), isolate_->counters(), kind, maybe_import->sig);
50 :
51 : ImportedFunctionEntry(instance_object_, maybe_import_index)
52 4920 : .SetWasmToJs(isolate_, maybe_import->js_function, import_wrapper);
53 : }
54 :
55 1366730 : if (tier == ExecutionTier::kInterpreter) {
56 455740 : interpreter_ = WasmDebugInfo::SetupForTesting(instance_object_);
57 : }
58 1366730 : }
59 :
60 51315 : byte* TestingModuleBuilder::AddMemory(uint32_t size) {
61 102630 : CHECK(!test_module_->has_memory);
62 51315 : CHECK_NULL(mem_start_);
63 51315 : CHECK_EQ(0, mem_size_);
64 : DCHECK(!instance_object_->has_memory_object());
65 : DCHECK_IMPLIES(test_module_->origin == kWasmOrigin,
66 : size % kWasmPageSize == 0);
67 51315 : test_module_->has_memory = true;
68 : uint32_t alloc_size = RoundUp(size, kWasmPageSize);
69 : Handle<JSArrayBuffer> new_buffer;
70 102630 : CHECK(NewArrayBuffer(isolate_, alloc_size).ToHandle(&new_buffer));
71 51315 : CHECK(!new_buffer.is_null());
72 51315 : mem_start_ = reinterpret_cast<byte*>(new_buffer->backing_store());
73 51315 : mem_size_ = size;
74 51315 : CHECK(size == 0 || mem_start_);
75 51315 : memset(mem_start_, 0, size);
76 :
77 : // Create the WasmMemoryObject.
78 : Handle<WasmMemoryObject> memory_object = WasmMemoryObject::New(
79 : isolate_, new_buffer,
80 102630 : (test_module_->maximum_pages != 0) ? test_module_->maximum_pages : -1);
81 51315 : instance_object_->set_memory_object(*memory_object);
82 51315 : WasmMemoryObject::AddInstance(isolate_, memory_object, instance_object_);
83 : // TODO(wasm): Delete the following two lines when test-run-wasm will use a
84 : // multiple of kPageSize as memory size. At the moment, the effect of these
85 : // two lines is used to shrink the memory for testing purposes.
86 102630 : instance_object_->SetRawMemory(mem_start_, mem_size_);
87 51315 : return mem_start_;
88 : }
89 :
90 1379830 : uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name,
91 : FunctionType type) {
92 7347465 : if (test_module_->functions.size() == 0) {
93 : // TODO(titzer): Reserving space here to avoid the underlying WasmFunction
94 : // structs from moving.
95 1366730 : test_module_->functions.reserve(kMaxFunctions);
96 : }
97 2759660 : uint32_t index = static_cast<uint32_t>(test_module_->functions.size());
98 4139490 : test_module_->functions.push_back({sig, index, 0, {0, 0}, false, false});
99 1379830 : if (type == kImport) {
100 : DCHECK_EQ(0, test_module_->num_declared_functions);
101 2460 : ++test_module_->num_imported_functions;
102 2460 : test_module_->functions.back().imported = true;
103 : } else {
104 1377370 : ++test_module_->num_declared_functions;
105 : }
106 : DCHECK_EQ(test_module_->functions.size(),
107 : test_module_->num_imported_functions +
108 : test_module_->num_declared_functions);
109 1379830 : if (name) {
110 1366815 : Vector<const byte> name_vec = Vector<const byte>::cast(CStrVector(name));
111 : test_module_->AddFunctionNameForTesting(
112 4100445 : index, {AddBytes(name_vec), static_cast<uint32_t>(name_vec.length())});
113 : }
114 1379830 : if (interpreter_) {
115 458870 : interpreter_->AddFunctionForTesting(&test_module_->functions.back());
116 : }
117 : DCHECK_LT(index, kMaxFunctions); // limited for testing.
118 1379830 : return index;
119 : }
120 :
121 5140 : Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
122 : // Wrap the code so it can be called as a JS function.
123 : Link();
124 15420 : FunctionSig* sig = test_module_->functions[index].sig;
125 : MaybeHandle<Code> maybe_ret_code =
126 5140 : compiler::CompileJSToWasmWrapper(isolate_, sig, false);
127 : Handle<Code> ret_code = maybe_ret_code.ToHandleChecked();
128 : Handle<JSFunction> ret = WasmExportedFunction::New(
129 : isolate_, instance_object(), MaybeHandle<String>(),
130 : static_cast<int>(index), static_cast<int>(sig->parameter_count()),
131 5140 : ret_code);
132 :
133 : // Add reference to the exported wrapper code.
134 : Handle<WasmModuleObject> module_object(instance_object()->module_object(),
135 15420 : isolate_);
136 15420 : Handle<FixedArray> old_arr(module_object->export_wrappers(), isolate_);
137 : Handle<FixedArray> new_arr =
138 5140 : isolate_->factory()->NewFixedArray(old_arr->length() + 1);
139 5140 : old_arr->CopyTo(0, *new_arr, 0, old_arr->length());
140 10280 : new_arr->set(old_arr->length(), *ret_code);
141 5140 : module_object->set_export_wrappers(*new_arr);
142 :
143 5140 : return ret;
144 : }
145 :
146 1860 : void TestingModuleBuilder::AddIndirectFunctionTable(
147 : const uint16_t* function_indexes, uint32_t table_size) {
148 1860 : test_module_->tables.emplace_back();
149 : WasmTable& table = test_module_->tables.back();
150 1860 : table.initial_size = table_size;
151 1860 : table.maximum_size = table_size;
152 1860 : table.has_maximum_size = true;
153 1995 : for (uint32_t i = 0; i < table_size; ++i) {
154 270 : table.values.push_back(function_indexes[i]);
155 : }
156 : WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
157 1860 : instance_object(), table_size);
158 1860 : }
159 :
160 45 : void TestingModuleBuilder::PopulateIndirectFunctionTable() {
161 90 : if (interpret()) return;
162 : auto instance = instance_object();
163 : uint32_t num_tables = 1; // TODO(titzer): multiple tables.
164 60 : for (uint32_t i = 0; i < num_tables; i++) {
165 120 : WasmTable& table = test_module_->tables[i];
166 30 : int table_size = static_cast<int>(instance->indirect_function_table_size());
167 120 : for (int j = 0; j < table_size; j++) {
168 270 : WasmFunction& function = test_module_->functions[table.values[j]];
169 90 : int sig_id = test_module_->signature_map.Find(*function.sig);
170 : IndirectFunctionTableEntry(instance, j)
171 180 : .Set(sig_id, instance, function.func_index);
172 : }
173 : }
174 : }
175 :
176 2737465 : uint32_t TestingModuleBuilder::AddBytes(Vector<const byte> bytes) {
177 2737465 : Vector<const uint8_t> old_bytes = native_module_->wire_bytes();
178 2737465 : uint32_t old_size = static_cast<uint32_t>(old_bytes.size());
179 : // Avoid placing strings at offset 0, this might be interpreted as "not
180 : // set", e.g. for function names.
181 2737465 : uint32_t bytes_offset = old_size ? old_size : 1;
182 2737465 : size_t new_size = bytes_offset + bytes.size();
183 : OwnedVector<uint8_t> new_bytes = OwnedVector<uint8_t>::New(new_size);
184 2737465 : memcpy(new_bytes.start(), old_bytes.start(), old_size);
185 2737465 : memcpy(new_bytes.start() + bytes_offset, bytes.start(), bytes.length());
186 5474930 : native_module_->SetWireBytes(std::move(new_bytes));
187 2737465 : return bytes_offset;
188 : }
189 :
190 1370650 : CompilationEnv TestingModuleBuilder::CreateCompilationEnv() {
191 : return {
192 : test_module_ptr_,
193 : trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler : kNoTrapHandler,
194 4111950 : runtime_exception_support_, enabled_features_, lower_simd()};
195 : }
196 :
197 23860 : const WasmGlobal* TestingModuleBuilder::AddGlobal(ValueType type) {
198 23860 : byte size = ValueTypes::MemSize(ValueTypes::MachineTypeFor(type));
199 23860 : global_offset = (global_offset + size - 1) & ~(size - 1); // align
200 47720 : test_module_->globals.push_back(
201 95440 : {type, true, WasmInitExpr(), {global_offset}, false, false});
202 23860 : global_offset += size;
203 : // limit number of globals.
204 23860 : CHECK_LT(global_offset, kMaxGlobalsSize);
205 23860 : return &test_module_->globals.back();
206 : }
207 :
208 1366730 : Handle<WasmInstanceObject> TestingModuleBuilder::InitInstanceObject() {
209 : Handle<Script> script =
210 2733460 : isolate_->factory()->NewScript(isolate_->factory()->empty_string());
211 : script->set_type(Script::TYPE_WASM);
212 : Handle<WasmModuleObject> module_object =
213 : WasmModuleObject::New(isolate_, enabled_features_, test_module_, {},
214 5466920 : script, Handle<ByteArray>::null());
215 : // This method is called when we initialize TestEnvironment. We don't
216 : // have a memory yet, so we won't create it here. We'll update the
217 : // interpreter when we get a memory. We do have globals, though.
218 1366730 : native_module_ = module_object->native_module();
219 1366730 : native_module_->ReserveCodeTableForTesting(kMaxFunctions);
220 :
221 1366730 : auto instance = WasmInstanceObject::New(isolate_, module_object);
222 1366730 : instance->set_globals_start(globals_data_);
223 1366730 : return instance;
224 : }
225 :
226 615 : void TestBuildingGraphWithBuilder(compiler::WasmGraphBuilder* builder,
227 615 : Zone* zone, FunctionSig* sig,
228 : const byte* start, const byte* end) {
229 615 : WasmFeatures unused_detected_features;
230 : FunctionBody body(sig, 0, start, end);
231 : DecodeResult result =
232 : BuildTFGraph(zone->allocator(), kAllWasmFeatures, nullptr, builder,
233 1230 : &unused_detected_features, body, nullptr);
234 615 : if (result.failed()) {
235 : #ifdef DEBUG
236 : if (!FLAG_trace_wasm_decoder) {
237 : // Retry the compilation with the tracing flag on, to help in debugging.
238 : FLAG_trace_wasm_decoder = true;
239 : result = BuildTFGraph(zone->allocator(), kAllWasmFeatures, nullptr,
240 : builder, &unused_detected_features, body, nullptr);
241 : }
242 : #endif
243 :
244 : FATAL("Verification failed; pc = +%x, msg = %s", result.error().offset(),
245 0 : result.error().message().c_str());
246 : }
247 615 : builder->LowerInt64();
248 615 : if (!CpuFeatures::SupportsWasmSimd128()) {
249 0 : builder->SimdScalarLoweringForTesting();
250 : }
251 615 : }
252 :
253 615 : void TestBuildingGraph(Zone* zone, compiler::JSGraph* jsgraph,
254 : CompilationEnv* module, FunctionSig* sig,
255 : compiler::SourcePositionTable* source_position_table,
256 : const byte* start, const byte* end) {
257 : compiler::WasmGraphBuilder builder(module, zone, jsgraph, sig,
258 615 : source_position_table);
259 615 : TestBuildingGraphWithBuilder(&builder, zone, sig, start, end);
260 615 : }
261 :
262 1366730 : WasmFunctionWrapper::WasmFunctionWrapper(Zone* zone, int num_params)
263 : : GraphAndBuilders(zone),
264 : inner_code_node_(nullptr),
265 : context_address_(nullptr),
266 1366730 : signature_(nullptr) {
267 : // One additional parameter for the pointer to the return value memory.
268 1366730 : Signature<MachineType>::Builder sig_builder(zone, 1, num_params + 1);
269 :
270 : sig_builder.AddReturn(MachineType::Int32());
271 2777380 : for (int i = 0; i < num_params + 1; i++) {
272 : sig_builder.AddParam(MachineType::Pointer());
273 : }
274 1366730 : signature_ = sig_builder.Build();
275 1366730 : }
276 :
277 910990 : void WasmFunctionWrapper::Init(CallDescriptor* call_descriptor,
278 : MachineType return_type,
279 : Vector<MachineType> param_types) {
280 : DCHECK_NOT_NULL(call_descriptor);
281 : DCHECK_EQ(signature_->parameter_count(), param_types.length() + 1);
282 :
283 : // Create the TF graph for the wrapper.
284 :
285 : // Function, context_address, effect, and control.
286 17430760 : Node** parameters = zone()->NewArray<Node*>(param_types.length() + 4);
287 : int start_value_output_count =
288 910990 : static_cast<int>(signature_->parameter_count()) + 1;
289 : graph()->SetStart(
290 910990 : graph()->NewNode(common()->Start(start_value_output_count)));
291 910990 : Node* effect = graph()->start();
292 : int parameter_count = 0;
293 :
294 : // Dummy node which gets replaced in SetInnerCode.
295 1821980 : inner_code_node_ = graph()->NewNode(common()->Int32Constant(0));
296 910990 : parameters[parameter_count++] = inner_code_node_;
297 :
298 : // Dummy node that gets replaced in SetContextAddress.
299 1821980 : context_address_ = graph()->NewNode(IntPtrConstant(0));
300 910990 : parameters[parameter_count++] = context_address_;
301 :
302 : int param_idx = 0;
303 940290 : for (MachineType t : param_types) {
304 : DCHECK_NE(MachineType::None(), t);
305 29300 : parameters[parameter_count] = graph()->NewNode(
306 : machine()->Load(t),
307 : graph()->NewNode(common()->Parameter(param_idx++), graph()->start()),
308 117200 : graph()->NewNode(common()->Int32Constant(0)), effect, graph()->start());
309 29300 : effect = parameters[parameter_count++];
310 : }
311 :
312 910990 : parameters[parameter_count++] = effect;
313 1821980 : parameters[parameter_count++] = graph()->start();
314 : Node* call = graph()->NewNode(common()->Call(call_descriptor),
315 1821980 : parameter_count, parameters);
316 :
317 910990 : if (!return_type.IsNone()) {
318 : effect = graph()->NewNode(
319 : machine()->Store(compiler::StoreRepresentation(
320 : return_type.representation(), WriteBarrierKind::kNoWriteBarrier)),
321 : graph()->NewNode(common()->Parameter(param_types.length()),
322 : graph()->start()),
323 : graph()->NewNode(common()->Int32Constant(0)), call, effect,
324 3624320 : graph()->start());
325 : }
326 910990 : Node* zero = graph()->NewNode(common()->Int32Constant(0));
327 : Node* r = graph()->NewNode(
328 : common()->Return(), zero,
329 : graph()->NewNode(common()->Int32Constant(WASM_WRAPPER_RETURN_VALUE)),
330 1821980 : effect, graph()->start());
331 910990 : graph()->SetEnd(graph()->NewNode(common()->End(1), r));
332 910990 : }
333 :
334 11537070 : Handle<Code> WasmFunctionWrapper::GetWrapperCode() {
335 : Handle<Code> code;
336 11537070 : if (!code_.ToHandle(&code)) {
337 905795 : Isolate* isolate = CcTest::InitIsolateOnce();
338 :
339 : auto call_descriptor =
340 2717385 : compiler::Linkage::GetSimplifiedCDescriptor(zone(), signature_, true);
341 :
342 : if (kSystemPointerSize == 4) {
343 : size_t num_params = signature_->parameter_count();
344 : // One additional parameter for the pointer of the return value.
345 : Signature<MachineRepresentation>::Builder rep_builder(zone(), 1,
346 : num_params + 1);
347 :
348 : rep_builder.AddReturn(MachineRepresentation::kWord32);
349 : for (size_t i = 0; i < num_params + 1; i++) {
350 : rep_builder.AddParam(MachineRepresentation::kWord32);
351 : }
352 : compiler::Int64Lowering r(graph(), machine(), common(), zone(),
353 : rep_builder.Build());
354 : r.LowerGraph();
355 : }
356 :
357 : OptimizedCompilationInfo info(ArrayVector("testing"), graph()->zone(),
358 905795 : Code::C_WASM_ENTRY);
359 : code_ = compiler::Pipeline::GenerateCodeForTesting(
360 : &info, isolate, call_descriptor, graph(),
361 1811590 : AssemblerOptions::Default(isolate));
362 905795 : code = code_.ToHandleChecked();
363 : #ifdef ENABLE_DISASSEMBLER
364 : if (FLAG_print_opt_code) {
365 : CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
366 : OFStream os(tracing_scope.file());
367 :
368 : code->Disassemble("wasm wrapper", os);
369 : }
370 : #endif
371 : }
372 :
373 11537070 : return code;
374 : }
375 :
376 5482600 : void WasmFunctionCompiler::Build(const byte* start, const byte* end) {
377 1370650 : size_t locals_size = local_decls.Size();
378 1370650 : size_t total_size = end - start + locals_size + 1;
379 1370650 : byte* buffer = static_cast<byte*>(zone()->New(total_size));
380 : // Prepend the local decls to the code.
381 1370650 : local_decls.Emit(buffer);
382 : // Emit the code.
383 1370650 : memcpy(buffer + locals_size, start, end - start);
384 : // Append an extra end opcode.
385 1370650 : buffer[total_size - 1] = kExprEnd;
386 :
387 : start = buffer;
388 1370650 : end = buffer + total_size;
389 :
390 1370650 : CHECK_GE(kMaxInt, end - start);
391 1370650 : int len = static_cast<int>(end - start);
392 : function_->code = {builder_->AddBytes(Vector<const byte>(start, len)),
393 4111950 : static_cast<uint32_t>(len)};
394 :
395 1370650 : if (interpreter_) {
396 : // Add the code to the interpreter.
397 457030 : interpreter_->SetFunctionCodeForTesting(function_, start, end);
398 : }
399 :
400 : // TODO(wasm): tests that go through JS depend on having a compiled version
401 : // of each function, even if the execution tier is the interpreter. Fix.
402 1370650 : auto tier = builder_->execution_tier();
403 1370650 : if (tier == ExecutionTier::kInterpreter) {
404 : tier = ExecutionTier::kOptimized;
405 : }
406 :
407 : Vector<const uint8_t> wire_bytes = builder_->instance_object()
408 2741300 : ->module_object()
409 : ->native_module()
410 2741300 : ->wire_bytes();
411 :
412 1370650 : CompilationEnv env = builder_->CreateCompilationEnv();
413 2741300 : ScopedVector<uint8_t> func_wire_bytes(function_->code.length());
414 2741300 : memcpy(func_wire_bytes.start(), wire_bytes.start() + function_->code.offset(),
415 1370650 : func_wire_bytes.length());
416 :
417 : FunctionBody func_body{function_->sig, function_->code.offset(),
418 : func_wire_bytes.start(), func_wire_bytes.end()};
419 : NativeModule* native_module =
420 2741300 : builder_->instance_object()->module_object()->native_module();
421 : WasmCompilationUnit unit(isolate()->wasm_engine(), function_->func_index,
422 4111950 : tier);
423 1370650 : WasmFeatures unused_detected_features;
424 : unit.ExecuteCompilation(
425 : &env, native_module,
426 : native_module->compilation_state()->GetWireBytesStorage(),
427 2741300 : isolate()->counters(), &unused_detected_features);
428 1370650 : WasmCode* result = unit.result();
429 : DCHECK_NOT_NULL(result);
430 1370650 : if (WasmCode::ShouldBeLogged(isolate())) result->LogCode(isolate());
431 1370650 : }
432 :
433 1377370 : WasmFunctionCompiler::WasmFunctionCompiler(Zone* zone, FunctionSig* sig,
434 2754740 : TestingModuleBuilder* builder,
435 : const char* name)
436 : : GraphAndBuilders(zone),
437 : jsgraph(builder->isolate(), this->graph(), this->common(), nullptr,
438 : nullptr, this->machine()),
439 : sig(sig),
440 : descriptor_(nullptr),
441 : builder_(builder),
442 : local_decls(zone, sig),
443 : source_position_table_(this->graph()),
444 5509480 : interpreter_(builder->interpreter()) {
445 : // Get a new function from the testing module.
446 1377370 : int index = builder->AddFunction(sig, name, TestingModuleBuilder::kWasm);
447 2754740 : function_ = builder_->GetFunctionAt(index);
448 1377370 : }
449 :
450 : WasmFunctionCompiler::~WasmFunctionCompiler() = default;
451 :
452 1366975 : FunctionSig* WasmRunnerBase::CreateSig(MachineType return_type,
453 : Vector<MachineType> param_types) {
454 1366975 : int return_count = return_type.IsNone() ? 0 : 1;
455 : int param_count = param_types.length();
456 :
457 : // Allocate storage array in zone.
458 1366975 : ValueType* sig_types = zone_.NewArray<ValueType>(return_count + param_count);
459 :
460 : // Convert machine types to local types, and check that there are no
461 : // MachineType::None()'s in the parameters.
462 : int idx = 0;
463 1366975 : if (return_count) sig_types[idx++] = ValueTypes::ValueTypeFor(return_type);
464 1411105 : for (MachineType param : param_types) {
465 88260 : CHECK_NE(MachineType::None(), param);
466 44130 : sig_types[idx++] = ValueTypes::ValueTypeFor(param);
467 : }
468 2733950 : return new (&zone_) FunctionSig(return_count, param_count, sig_types);
469 : }
470 :
471 : // static
472 : bool WasmRunnerBase::trap_happened;
473 :
474 : } // namespace wasm
475 : } // namespace internal
476 85011 : } // namespace v8
|