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 35 : explicit SharedEngine(size_t max_committed = kMaxWasmCodeMemory)
30 70 : : wasm_engine_(base::make_unique<WasmEngine>()) {}
31 35 : ~SharedEngine() {
32 : // Ensure no remaining uses exist.
33 35 : CHECK(wasm_engine_.unique());
34 35 : }
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 30 : 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 630 : explicit SharedEngineIsolate(SharedEngine* engine)
59 105 : : isolate_(v8::Isolate::Allocate()) {
60 210 : isolate()->SetWasmEngine(engine->ExportEngineForSharing());
61 : v8::Isolate::CreateParams create_params;
62 105 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
63 105 : v8::Isolate::Initialize(isolate_, create_params);
64 105 : v8::HandleScope handle_scope(v8_isolate());
65 210 : v8::Context::New(v8_isolate())->Enter();
66 105 : testing::SetupIsolateForWasmModule(isolate());
67 210 : zone_.reset(new Zone(isolate()->allocator(), ZONE_NAME));
68 105 : }
69 105 : ~SharedEngineIsolate() {
70 : zone_.reset();
71 105 : isolate_->Dispose();
72 105 : }
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 70 : Handle<WasmInstanceObject> CompileAndInstantiate(ZoneBuffer* buffer) {
79 : ErrorThrower thrower(isolate(), "CompileAndInstantiate");
80 : MaybeHandle<WasmInstanceObject> instance =
81 : testing::CompileAndInstantiateForTesting(
82 : isolate(), &thrower,
83 35 : ModuleWireBytes(buffer->begin(), buffer->end()));
84 35 : return instance.ToHandleChecked();
85 : }
86 :
87 135 : Handle<WasmInstanceObject> ImportInstance(SharedModule shared_module) {
88 : Handle<WasmModuleObject> module_object =
89 90 : isolate()->wasm_engine()->ImportNativeModule(isolate(), shared_module);
90 : ErrorThrower thrower(isolate(), "ImportInstance");
91 : MaybeHandle<WasmInstanceObject> instance =
92 : isolate()->wasm_engine()->SyncInstantiate(isolate(), &thrower,
93 90 : module_object, {}, {});
94 45 : return instance.ToHandleChecked();
95 : }
96 :
97 15 : SharedModule ExportInstance(Handle<WasmInstanceObject> instance) {
98 15 : return instance->module_object()->shared_native_module();
99 : }
100 :
101 2555 : int32_t Run(Handle<WasmInstanceObject> instance) {
102 2555 : 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 120 : class SharedEngineThread : public v8::base::Thread {
113 : public:
114 60 : SharedEngineThread(SharedEngine* engine,
115 : std::function<void(SharedEngineIsolate&)> callback)
116 : : Thread(Options("SharedEngineThread")),
117 : engine_(engine),
118 120 : callback_(callback) {}
119 :
120 60 : void Run() override {
121 60 : SharedEngineIsolate isolate(engine_);
122 60 : callback_(isolate);
123 60 : }
124 :
125 : private:
126 : SharedEngine* engine_;
127 : std::function<void(SharedEngineIsolate&)> callback_;
128 : };
129 :
130 : namespace {
131 :
132 45 : ZoneBuffer* BuildReturnConstantModule(Zone* zone, int constant) {
133 45 : TestSignatures sigs;
134 : ZoneBuffer* buffer = new (zone) ZoneBuffer(zone);
135 45 : WasmModuleBuilder* builder = new (zone) WasmModuleBuilder(zone);
136 45 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
137 45 : f->builder()->AddExport(CStrVector("main"), f);
138 45 : byte code[] = {WASM_I32V_2(constant)};
139 45 : f->EmitCode(code, sizeof(code));
140 45 : f->Emit(kExprEnd);
141 45 : builder->WriteTo(*buffer);
142 45 : return buffer;
143 : }
144 :
145 20 : class MockInstantiationResolver : public InstantiationResultResolver {
146 : public:
147 : explicit MockInstantiationResolver(Handle<Object>* out_instance)
148 10 : : out_instance_(out_instance) {}
149 10 : void OnInstantiationSucceeded(Handle<WasmInstanceObject> result) override {
150 20 : *out_instance_->location() = result->ptr();
151 10 : }
152 0 : void OnInstantiationFailed(Handle<Object> error_reason) override {
153 0 : UNREACHABLE();
154 : }
155 :
156 : private:
157 : Handle<Object>* out_instance_;
158 : };
159 :
160 20 : class MockCompilationResolver : public CompilationResultResolver {
161 : public:
162 : MockCompilationResolver(SharedEngineIsolate& isolate,
163 : Handle<Object>* out_instance)
164 10 : : isolate_(isolate), out_instance_(out_instance) {}
165 10 : void OnCompilationSucceeded(Handle<WasmModuleObject> result) override {
166 : isolate_.isolate()->wasm_engine()->AsyncInstantiate(
167 : isolate_.isolate(),
168 : base::make_unique<MockInstantiationResolver>(out_instance_), result,
169 40 : {});
170 10 : }
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 48 : void PumpMessageLoop(SharedEngineIsolate& isolate) {
181 : v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
182 : isolate.v8_isolate(),
183 24 : platform::MessageLoopBehavior::kWaitForWork);
184 : isolate.isolate()->default_microtask_queue()->RunMicrotasks(
185 24 : isolate.isolate());
186 24 : }
187 :
188 10 : Handle<WasmInstanceObject> CompileAndInstantiateAsync(
189 40 : SharedEngineIsolate& isolate, ZoneBuffer* buffer) {
190 10 : Handle<Object> maybe_instance = handle(Smi::kZero, isolate.isolate());
191 10 : auto enabled_features = WasmFeaturesFromIsolate(isolate.isolate());
192 : isolate.isolate()->wasm_engine()->AsyncCompile(
193 : isolate.isolate(), enabled_features,
194 : base::make_unique<MockCompilationResolver>(isolate, &maybe_instance),
195 30 : ModuleWireBytes(buffer->begin(), buffer->end()), true);
196 78 : while (!maybe_instance->IsWasmInstanceObject()) PumpMessageLoop(isolate);
197 : Handle<WasmInstanceObject> instance =
198 10 : Handle<WasmInstanceObject>::cast(maybe_instance);
199 10 : return instance;
200 : }
201 :
202 : } // namespace
203 :
204 28342 : TEST(SharedEngineUseCount) {
205 5 : SharedEngine engine;
206 5 : CHECK_EQ(0, engine.NumberOfExportedEngineUses());
207 : {
208 5 : SharedEngineIsolate isolate(&engine);
209 5 : CHECK_EQ(1, engine.NumberOfExportedEngineUses());
210 : }
211 5 : CHECK_EQ(0, engine.NumberOfExportedEngineUses());
212 : {
213 5 : SharedEngineIsolate isolate1(&engine);
214 5 : CHECK_EQ(1, engine.NumberOfExportedEngineUses());
215 10 : SharedEngineIsolate isolate2(&engine);
216 10 : CHECK_EQ(2, engine.NumberOfExportedEngineUses());
217 : }
218 5 : CHECK_EQ(0, engine.NumberOfExportedEngineUses());
219 5 : }
220 :
221 28342 : TEST(SharedEngineRunSeparated) {
222 5 : SharedEngine engine;
223 : {
224 5 : SharedEngineIsolate isolate(&engine);
225 5 : HandleScope scope(isolate.isolate());
226 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
227 5 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
228 10 : CHECK_EQ(23, isolate.Run(instance));
229 : }
230 : {
231 5 : SharedEngineIsolate isolate(&engine);
232 5 : HandleScope scope(isolate.isolate());
233 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
234 5 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
235 10 : CHECK_EQ(42, isolate.Run(instance));
236 5 : }
237 5 : }
238 :
239 28342 : TEST(SharedEngineRunImported) {
240 5 : SharedEngine engine;
241 : SharedModule module;
242 : {
243 5 : SharedEngineIsolate isolate(&engine);
244 5 : HandleScope scope(isolate.isolate());
245 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
246 5 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
247 10 : module = isolate.ExportInstance(instance);
248 5 : CHECK_EQ(23, isolate.Run(instance));
249 10 : CHECK_EQ(2, module.use_count());
250 : }
251 5 : CHECK_EQ(1, module.use_count());
252 : {
253 5 : SharedEngineIsolate isolate(&engine);
254 5 : HandleScope scope(isolate.isolate());
255 10 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
256 5 : CHECK_EQ(23, isolate.Run(instance));
257 10 : CHECK_EQ(2, module.use_count());
258 : }
259 10 : CHECK_EQ(1, module.use_count());
260 5 : }
261 :
262 28342 : TEST(SharedEngineRunThreadedBuildingSync) {
263 5 : SharedEngine engine;
264 5 : SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
265 : HandleScope scope(isolate.isolate());
266 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
267 5 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
268 5 : CHECK_EQ(23, isolate.Run(instance));
269 20 : });
270 5 : SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
271 : HandleScope scope(isolate.isolate());
272 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
273 5 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
274 5 : CHECK_EQ(42, isolate.Run(instance));
275 20 : });
276 5 : thread1.Start();
277 5 : thread2.Start();
278 5 : thread1.Join();
279 10 : thread2.Join();
280 5 : }
281 :
282 28342 : TEST(SharedEngineRunThreadedBuildingAsync) {
283 5 : SharedEngine engine;
284 5 : SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
285 : HandleScope scope(isolate.isolate());
286 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
287 : Handle<WasmInstanceObject> instance =
288 5 : CompileAndInstantiateAsync(isolate, buffer);
289 5 : CHECK_EQ(23, isolate.Run(instance));
290 20 : });
291 5 : SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
292 : HandleScope scope(isolate.isolate());
293 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
294 : Handle<WasmInstanceObject> instance =
295 5 : CompileAndInstantiateAsync(isolate, buffer);
296 5 : CHECK_EQ(42, isolate.Run(instance));
297 20 : });
298 5 : thread1.Start();
299 5 : thread2.Start();
300 5 : thread1.Join();
301 10 : thread2.Join();
302 5 : }
303 :
304 28342 : TEST(SharedEngineRunThreadedExecution) {
305 5 : SharedEngine engine;
306 : SharedModule module;
307 : {
308 5 : SharedEngineIsolate isolate(&engine);
309 5 : HandleScope scope(isolate.isolate());
310 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
311 5 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
312 15 : module = isolate.ExportInstance(instance);
313 : }
314 5 : SharedEngineThread thread1(&engine, [module](SharedEngineIsolate& isolate) {
315 : HandleScope scope(isolate.isolate());
316 10 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
317 5 : CHECK_EQ(23, isolate.Run(instance));
318 25 : });
319 5 : SharedEngineThread thread2(&engine, [module](SharedEngineIsolate& isolate) {
320 : HandleScope scope(isolate.isolate());
321 10 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
322 5 : CHECK_EQ(23, isolate.Run(instance));
323 25 : });
324 5 : thread1.Start();
325 5 : thread2.Start();
326 5 : thread1.Join();
327 10 : thread2.Join();
328 5 : }
329 :
330 28342 : TEST(SharedEngineRunThreadedTierUp) {
331 5 : SharedEngine engine;
332 : SharedModule module;
333 : {
334 5 : SharedEngineIsolate isolate(&engine);
335 5 : HandleScope scope(isolate.isolate());
336 5 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
337 5 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
338 15 : module = isolate.ExportInstance(instance);
339 : }
340 : constexpr int kNumberOfThreads = 5;
341 : std::list<SharedEngineThread> threads;
342 30 : for (int i = 0; i < kNumberOfThreads; ++i) {
343 25 : threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
344 : constexpr int kNumberOfIterations = 100;
345 : HandleScope scope(isolate.isolate());
346 50 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
347 2525 : for (int j = 0; j < kNumberOfIterations; ++j) {
348 2500 : CHECK_EQ(23, isolate.Run(instance));
349 : }
350 100 : });
351 : }
352 10 : threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
353 : HandleScope scope(isolate.isolate());
354 10 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
355 5 : WasmFeatures detected = kNoWasmFeatures;
356 : WasmCompilationUnit::CompileWasmFunction(
357 : isolate.isolate(), module.get(), &detected,
358 15 : &module->module()->functions[0], ExecutionTier::kOptimized);
359 5 : CHECK_EQ(23, isolate.Run(instance));
360 20 : });
361 40 : for (auto& thread : threads) thread.Start();
362 45 : for (auto& thread : threads) thread.Join();
363 5 : }
364 :
365 : } // namespace test_wasm_shared_engine
366 : } // namespace wasm
367 : } // namespace internal
368 85011 : } // namespace v8
|