Line data Source code
1 : // Copyright 2018 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 <memory>
6 :
7 : #include "src/microtask-queue.h"
8 : #include "src/objects-inl.h"
9 : #include "src/wasm/function-compiler.h"
10 : #include "src/wasm/wasm-engine.h"
11 : #include "src/wasm/wasm-module-builder.h"
12 : #include "src/wasm/wasm-module.h"
13 : #include "src/wasm/wasm-objects-inl.h"
14 :
15 : #include "test/cctest/cctest.h"
16 : #include "test/common/wasm/test-signatures.h"
17 : #include "test/common/wasm/wasm-macro-gen.h"
18 : #include "test/common/wasm/wasm-module-runner.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 : namespace wasm {
23 : namespace test_wasm_shared_engine {
24 :
25 : // Helper class representing a WebAssembly engine that is capable of being
26 : // shared between multiple Isolates, sharing the underlying generated code.
27 : class SharedEngine {
28 : public:
29 28 : explicit SharedEngine(size_t max_committed = kMaxWasmCodeMemory)
30 28 : : wasm_engine_(base::make_unique<WasmEngine>()) {}
31 56 : ~SharedEngine() {
32 : // Ensure no remaining uses exist.
33 28 : CHECK(wasm_engine_.unique());
34 28 : }
35 :
36 : WasmEngine* engine() const { return wasm_engine_.get(); }
37 : WasmCodeManager* code_manager() const { return engine()->code_manager(); }
38 :
39 : int NumberOfExportedEngineUses() const {
40 : // This class holds one implicit use itself, which we discount.
41 24 : return static_cast<int>(wasm_engine_.use_count()) - 1;
42 : }
43 :
44 : std::shared_ptr<WasmEngine> ExportEngineForSharing() { return wasm_engine_; }
45 :
46 : private:
47 : std::shared_ptr<WasmEngine> wasm_engine_;
48 : };
49 :
50 : // Helper type definition representing a WebAssembly module shared between
51 : // multiple Isolates with implicit reference counting.
52 : using SharedModule = std::shared_ptr<NativeModule>;
53 :
54 : // Helper class representing an Isolate based on a given shared WebAssembly
55 : // engine available at construction time.
56 : class SharedEngineIsolate {
57 : public:
58 84 : explicit SharedEngineIsolate(SharedEngine* engine)
59 84 : : isolate_(v8::Isolate::Allocate()) {
60 168 : isolate()->SetWasmEngine(engine->ExportEngineForSharing());
61 : v8::Isolate::CreateParams create_params;
62 84 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
63 84 : v8::Isolate::Initialize(isolate_, create_params);
64 168 : v8::HandleScope handle_scope(v8_isolate());
65 168 : v8::Context::New(v8_isolate())->Enter();
66 84 : testing::SetupIsolateForWasmModule(isolate());
67 84 : zone_.reset(new Zone(isolate()->allocator(), ZONE_NAME));
68 84 : }
69 166 : ~SharedEngineIsolate() {
70 83 : zone_.reset();
71 84 : isolate_->Dispose();
72 83 : }
73 :
74 : Zone* zone() const { return zone_.get(); }
75 : v8::Isolate* v8_isolate() { return isolate_; }
76 : Isolate* isolate() { return reinterpret_cast<Isolate*>(isolate_); }
77 :
78 28 : Handle<WasmInstanceObject> CompileAndInstantiate(ZoneBuffer* buffer) {
79 28 : ErrorThrower thrower(isolate(), "CompileAndInstantiate");
80 : MaybeHandle<WasmInstanceObject> instance =
81 : testing::CompileAndInstantiateForTesting(
82 : isolate(), &thrower,
83 28 : ModuleWireBytes(buffer->begin(), buffer->end()));
84 28 : return instance.ToHandleChecked();
85 : }
86 :
87 36 : Handle<WasmInstanceObject> ImportInstance(SharedModule shared_module) {
88 : Handle<WasmModuleObject> module_object =
89 72 : isolate()->wasm_engine()->ImportNativeModule(isolate(), shared_module);
90 36 : ErrorThrower thrower(isolate(), "ImportInstance");
91 : MaybeHandle<WasmInstanceObject> instance =
92 : isolate()->wasm_engine()->SyncInstantiate(isolate(), &thrower,
93 36 : module_object, {}, {});
94 36 : return instance.ToHandleChecked();
95 : }
96 :
97 12 : SharedModule ExportInstance(Handle<WasmInstanceObject> instance) {
98 12 : return instance->module_object()->shared_native_module();
99 : }
100 :
101 : int32_t Run(Handle<WasmInstanceObject> instance) {
102 2044 : return testing::RunWasmModuleForTesting(isolate(), instance, 0, nullptr);
103 : }
104 :
105 : private:
106 : v8::Isolate* isolate_;
107 : std::unique_ptr<Zone> zone_;
108 : };
109 :
110 : // Helper class representing a Thread running its own instance of an Isolate
111 : // with a shared WebAssembly engine available at construction time.
112 96 : class SharedEngineThread : public v8::base::Thread {
113 : public:
114 48 : SharedEngineThread(SharedEngine* engine,
115 : std::function<void(SharedEngineIsolate&)> callback)
116 : : Thread(Options("SharedEngineThread")),
117 : engine_(engine),
118 96 : callback_(callback) {}
119 :
120 48 : void Run() override {
121 95 : SharedEngineIsolate isolate(engine_);
122 : callback_(isolate);
123 47 : }
124 :
125 : private:
126 : SharedEngine* engine_;
127 : std::function<void(SharedEngineIsolate&)> callback_;
128 : };
129 :
130 : namespace {
131 :
132 36 : ZoneBuffer* BuildReturnConstantModule(Zone* zone, int constant) {
133 36 : TestSignatures sigs;
134 : ZoneBuffer* buffer = new (zone) ZoneBuffer(zone);
135 36 : WasmModuleBuilder* builder = new (zone) WasmModuleBuilder(zone);
136 36 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
137 36 : f->builder()->AddExport(CStrVector("main"), f);
138 36 : byte code[] = {WASM_I32V_2(constant)};
139 36 : f->EmitCode(code, sizeof(code));
140 36 : f->Emit(kExprEnd);
141 36 : builder->WriteTo(*buffer);
142 36 : return buffer;
143 : }
144 :
145 14 : class MockInstantiationResolver : public InstantiationResultResolver {
146 : public:
147 : explicit MockInstantiationResolver(Handle<Object>* out_instance)
148 8 : : out_instance_(out_instance) {}
149 8 : void OnInstantiationSucceeded(Handle<WasmInstanceObject> result) override {
150 16 : *out_instance_->location() = result->ptr();
151 8 : }
152 0 : void OnInstantiationFailed(Handle<Object> error_reason) override {
153 0 : UNREACHABLE();
154 : }
155 :
156 : private:
157 : Handle<Object>* out_instance_;
158 : };
159 :
160 14 : class MockCompilationResolver : public CompilationResultResolver {
161 : public:
162 : MockCompilationResolver(SharedEngineIsolate& isolate,
163 : Handle<Object>* out_instance)
164 8 : : isolate_(isolate), out_instance_(out_instance) {}
165 8 : void OnCompilationSucceeded(Handle<WasmModuleObject> result) override {
166 16 : isolate_.isolate()->wasm_engine()->AsyncInstantiate(
167 8 : isolate_.isolate(),
168 : base::make_unique<MockInstantiationResolver>(out_instance_), result,
169 8 : {});
170 8 : }
171 0 : void OnCompilationFailed(Handle<Object> error_reason) override {
172 0 : UNREACHABLE();
173 : }
174 :
175 : private:
176 : SharedEngineIsolate& isolate_;
177 : Handle<Object>* out_instance_;
178 : };
179 :
180 21 : void PumpMessageLoop(SharedEngineIsolate& isolate) {
181 21 : v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
182 : isolate.v8_isolate(),
183 21 : platform::MessageLoopBehavior::kWaitForWork);
184 : isolate.isolate()->default_microtask_queue()->RunMicrotasks(
185 20 : isolate.isolate());
186 21 : }
187 :
188 8 : Handle<WasmInstanceObject> CompileAndInstantiateAsync(
189 : SharedEngineIsolate& isolate, ZoneBuffer* buffer) {
190 8 : Handle<Object> maybe_instance = handle(Smi::kZero, isolate.isolate());
191 8 : auto enabled_features = WasmFeaturesFromIsolate(isolate.isolate());
192 16 : isolate.isolate()->wasm_engine()->AsyncCompile(
193 : isolate.isolate(), enabled_features,
194 8 : base::make_unique<MockCompilationResolver>(isolate, &maybe_instance),
195 8 : ModuleWireBytes(buffer->begin(), buffer->end()), true);
196 29 : while (!maybe_instance->IsWasmInstanceObject()) PumpMessageLoop(isolate);
197 : Handle<WasmInstanceObject> instance =
198 : Handle<WasmInstanceObject>::cast(maybe_instance);
199 8 : return instance;
200 : }
201 :
202 : } // namespace
203 :
204 26643 : TEST(SharedEngineUseCount) {
205 8 : SharedEngine engine;
206 4 : CHECK_EQ(0, engine.NumberOfExportedEngineUses());
207 : {
208 8 : SharedEngineIsolate isolate(&engine);
209 4 : CHECK_EQ(1, engine.NumberOfExportedEngineUses());
210 : }
211 4 : CHECK_EQ(0, engine.NumberOfExportedEngineUses());
212 : {
213 8 : SharedEngineIsolate isolate1(&engine);
214 4 : CHECK_EQ(1, engine.NumberOfExportedEngineUses());
215 8 : SharedEngineIsolate isolate2(&engine);
216 4 : CHECK_EQ(2, engine.NumberOfExportedEngineUses());
217 : }
218 4 : CHECK_EQ(0, engine.NumberOfExportedEngineUses());
219 4 : }
220 :
221 26643 : TEST(SharedEngineRunSeparated) {
222 8 : SharedEngine engine;
223 : {
224 8 : SharedEngineIsolate isolate(&engine);
225 : HandleScope scope(isolate.isolate());
226 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
227 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
228 4 : CHECK_EQ(23, isolate.Run(instance));
229 : }
230 : {
231 8 : SharedEngineIsolate isolate(&engine);
232 : HandleScope scope(isolate.isolate());
233 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
234 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
235 4 : CHECK_EQ(42, isolate.Run(instance));
236 : }
237 4 : }
238 :
239 26643 : TEST(SharedEngineRunImported) {
240 8 : SharedEngine engine;
241 4 : SharedModule module;
242 : {
243 8 : SharedEngineIsolate isolate(&engine);
244 : HandleScope scope(isolate.isolate());
245 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
246 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
247 8 : module = isolate.ExportInstance(instance);
248 4 : CHECK_EQ(23, isolate.Run(instance));
249 : }
250 : {
251 8 : SharedEngineIsolate isolate(&engine);
252 : HandleScope scope(isolate.isolate());
253 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
254 4 : CHECK_EQ(23, isolate.Run(instance));
255 : }
256 4 : }
257 :
258 26643 : TEST(SharedEngineRunThreadedBuildingSync) {
259 8 : SharedEngine engine;
260 4 : SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
261 : HandleScope scope(isolate.isolate());
262 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
263 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
264 4 : CHECK_EQ(23, isolate.Run(instance));
265 16 : });
266 4 : SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
267 : HandleScope scope(isolate.isolate());
268 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
269 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
270 4 : CHECK_EQ(42, isolate.Run(instance));
271 16 : });
272 4 : thread1.Start();
273 4 : thread2.Start();
274 4 : thread1.Join();
275 4 : thread2.Join();
276 4 : }
277 :
278 26643 : TEST(SharedEngineRunThreadedBuildingAsync) {
279 8 : SharedEngine engine;
280 4 : SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
281 : HandleScope scope(isolate.isolate());
282 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
283 : Handle<WasmInstanceObject> instance =
284 4 : CompileAndInstantiateAsync(isolate, buffer);
285 4 : CHECK_EQ(23, isolate.Run(instance));
286 16 : });
287 4 : SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
288 : HandleScope scope(isolate.isolate());
289 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
290 : Handle<WasmInstanceObject> instance =
291 4 : CompileAndInstantiateAsync(isolate, buffer);
292 4 : CHECK_EQ(42, isolate.Run(instance));
293 16 : });
294 4 : thread1.Start();
295 4 : thread2.Start();
296 4 : thread1.Join();
297 4 : thread2.Join();
298 4 : }
299 :
300 26643 : TEST(SharedEngineRunThreadedExecution) {
301 8 : SharedEngine engine;
302 4 : SharedModule module;
303 : {
304 8 : SharedEngineIsolate isolate(&engine);
305 : HandleScope scope(isolate.isolate());
306 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
307 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
308 8 : module = isolate.ExportInstance(instance);
309 : }
310 16 : SharedEngineThread thread1(&engine, [module](SharedEngineIsolate& isolate) {
311 : HandleScope scope(isolate.isolate());
312 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
313 4 : CHECK_EQ(23, isolate.Run(instance));
314 20 : });
315 16 : SharedEngineThread thread2(&engine, [module](SharedEngineIsolate& isolate) {
316 : HandleScope scope(isolate.isolate());
317 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
318 4 : CHECK_EQ(23, isolate.Run(instance));
319 20 : });
320 4 : thread1.Start();
321 4 : thread2.Start();
322 4 : thread1.Join();
323 4 : thread2.Join();
324 4 : }
325 :
326 26643 : TEST(SharedEngineRunThreadedTierUp) {
327 8 : SharedEngine engine;
328 4 : SharedModule module;
329 : {
330 8 : SharedEngineIsolate isolate(&engine);
331 : HandleScope scope(isolate.isolate());
332 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
333 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
334 8 : module = isolate.ExportInstance(instance);
335 : }
336 : constexpr int kNumberOfThreads = 5;
337 : std::list<SharedEngineThread> threads;
338 44 : for (int i = 0; i < kNumberOfThreads; ++i) {
339 120 : threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
340 : constexpr int kNumberOfIterations = 100;
341 : HandleScope scope(isolate.isolate());
342 40 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
343 4020 : for (int j = 0; j < kNumberOfIterations; ++j) {
344 2000 : CHECK_EQ(23, isolate.Run(instance));
345 : }
346 40 : });
347 : }
348 24 : threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
349 : HandleScope scope(isolate.isolate());
350 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
351 4 : WasmFeatures detected = kNoWasmFeatures;
352 : WasmCompilationUnit::CompileWasmFunction(
353 : isolate.isolate(), module.get(), &detected,
354 4 : &module->module()->functions[0], ExecutionTier::kTurbofan);
355 4 : CHECK_EQ(23, isolate.Run(instance));
356 8 : });
357 28 : for (auto& thread : threads) thread.Start();
358 28 : for (auto& thread : threads) thread.Join();
359 4 : }
360 :
361 : } // namespace test_wasm_shared_engine
362 : } // namespace wasm
363 : } // namespace internal
364 79917 : } // namespace v8
|