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 166 : v8::HandleScope handle_scope(v8_isolate());
65 166 : v8::Context::New(v8_isolate())->Enter();
66 83 : testing::SetupIsolateForWasmModule(isolate());
67 84 : zone_.reset(new Zone(isolate()->allocator(), ZONE_NAME));
68 84 : }
69 167 : ~SharedEngineIsolate() {
70 83 : zone_.reset();
71 84 : isolate_->Dispose();
72 84 : }
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 35 : 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 48 : }
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 16 : 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 16 : 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 21 : 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 26067 : 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 26067 : 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 26067 : 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 4 : CHECK_EQ(2, module.use_count());
250 : }
251 4 : CHECK_EQ(1, module.use_count());
252 : {
253 8 : SharedEngineIsolate isolate(&engine);
254 : HandleScope scope(isolate.isolate());
255 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
256 4 : CHECK_EQ(23, isolate.Run(instance));
257 4 : CHECK_EQ(2, module.use_count());
258 : }
259 4 : CHECK_EQ(1, module.use_count());
260 4 : }
261 :
262 26067 : TEST(SharedEngineRunThreadedBuildingSync) {
263 8 : SharedEngine engine;
264 4 : SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
265 : HandleScope scope(isolate.isolate());
266 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
267 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
268 4 : CHECK_EQ(23, isolate.Run(instance));
269 16 : });
270 4 : SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
271 : HandleScope scope(isolate.isolate());
272 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
273 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
274 4 : CHECK_EQ(42, isolate.Run(instance));
275 16 : });
276 4 : thread1.Start();
277 4 : thread2.Start();
278 4 : thread1.Join();
279 4 : thread2.Join();
280 4 : }
281 :
282 26067 : TEST(SharedEngineRunThreadedBuildingAsync) {
283 8 : SharedEngine engine;
284 4 : SharedEngineThread thread1(&engine, [](SharedEngineIsolate& isolate) {
285 : HandleScope scope(isolate.isolate());
286 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
287 : Handle<WasmInstanceObject> instance =
288 4 : CompileAndInstantiateAsync(isolate, buffer);
289 4 : CHECK_EQ(23, isolate.Run(instance));
290 16 : });
291 4 : SharedEngineThread thread2(&engine, [](SharedEngineIsolate& isolate) {
292 : HandleScope scope(isolate.isolate());
293 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
294 : Handle<WasmInstanceObject> instance =
295 4 : CompileAndInstantiateAsync(isolate, buffer);
296 4 : CHECK_EQ(42, isolate.Run(instance));
297 16 : });
298 4 : thread1.Start();
299 4 : thread2.Start();
300 4 : thread1.Join();
301 4 : thread2.Join();
302 4 : }
303 :
304 26067 : TEST(SharedEngineRunThreadedExecution) {
305 8 : SharedEngine engine;
306 4 : SharedModule module;
307 : {
308 8 : SharedEngineIsolate isolate(&engine);
309 : HandleScope scope(isolate.isolate());
310 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
311 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
312 8 : module = isolate.ExportInstance(instance);
313 : }
314 16 : SharedEngineThread thread1(&engine, [module](SharedEngineIsolate& isolate) {
315 : HandleScope scope(isolate.isolate());
316 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
317 4 : CHECK_EQ(23, isolate.Run(instance));
318 20 : });
319 16 : SharedEngineThread thread2(&engine, [module](SharedEngineIsolate& isolate) {
320 : HandleScope scope(isolate.isolate());
321 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
322 4 : CHECK_EQ(23, isolate.Run(instance));
323 20 : });
324 4 : thread1.Start();
325 4 : thread2.Start();
326 4 : thread1.Join();
327 4 : thread2.Join();
328 4 : }
329 :
330 26067 : TEST(SharedEngineRunThreadedTierUp) {
331 8 : SharedEngine engine;
332 4 : SharedModule module;
333 : {
334 8 : SharedEngineIsolate isolate(&engine);
335 : HandleScope scope(isolate.isolate());
336 4 : ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
337 4 : Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
338 8 : module = isolate.ExportInstance(instance);
339 : }
340 : constexpr int kNumberOfThreads = 5;
341 : std::list<SharedEngineThread> threads;
342 44 : for (int i = 0; i < kNumberOfThreads; ++i) {
343 120 : threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
344 : constexpr int kNumberOfIterations = 100;
345 : HandleScope scope(isolate.isolate());
346 40 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
347 4020 : for (int j = 0; j < kNumberOfIterations; ++j) {
348 2000 : CHECK_EQ(23, isolate.Run(instance));
349 : }
350 40 : });
351 : }
352 24 : threads.emplace_back(&engine, [module](SharedEngineIsolate& isolate) {
353 : HandleScope scope(isolate.isolate());
354 8 : Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
355 4 : WasmFeatures detected = kNoWasmFeatures;
356 : WasmCompilationUnit::CompileWasmFunction(
357 : isolate.isolate(), module.get(), &detected,
358 4 : &module->module()->functions[0], ExecutionTier::kOptimized);
359 4 : CHECK_EQ(23, isolate.Run(instance));
360 8 : });
361 28 : for (auto& thread : threads) thread.Start();
362 28 : for (auto& thread : threads) thread.Join();
363 4 : }
364 :
365 : } // namespace test_wasm_shared_engine
366 : } // namespace wasm
367 : } // namespace internal
368 78189 : } // namespace v8
|