• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/wasm/function-compiler.h"
6 
7 #include "src/base/platform/time.h"
8 #include "src/base/strings.h"
9 #include "src/codegen/compiler.h"
10 #include "src/codegen/macro-assembler-inl.h"
11 #include "src/codegen/optimized-compilation-info.h"
12 #include "src/compiler/wasm-compiler.h"
13 #include "src/diagnostics/code-tracer.h"
14 #include "src/logging/counters-scopes.h"
15 #include "src/logging/log.h"
16 #include "src/utils/ostreams.h"
17 #include "src/wasm/baseline/liftoff-compiler.h"
18 #include "src/wasm/wasm-code-manager.h"
19 #include "src/wasm/wasm-debug.h"
20 #include "src/wasm/wasm-engine.h"
21 
22 namespace v8 {
23 namespace internal {
24 namespace wasm {
25 
26 // static
GetBaselineExecutionTier(const WasmModule * module)27 ExecutionTier WasmCompilationUnit::GetBaselineExecutionTier(
28     const WasmModule* module) {
29   // Liftoff does not support the special asm.js opcodes, thus always compile
30   // asm.js modules with TurboFan.
31   if (is_asmjs_module(module)) return ExecutionTier::kTurbofan;
32   return FLAG_liftoff ? ExecutionTier::kLiftoff : ExecutionTier::kTurbofan;
33 }
34 
ExecuteCompilation(CompilationEnv * env,const WireBytesStorage * wire_bytes_storage,Counters * counters,WasmFeatures * detected)35 WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
36     CompilationEnv* env, const WireBytesStorage* wire_bytes_storage,
37     Counters* counters, WasmFeatures* detected) {
38   WasmCompilationResult result;
39   if (func_index_ < static_cast<int>(env->module->num_imported_functions)) {
40     result = ExecuteImportWrapperCompilation(env);
41   } else {
42     result =
43         ExecuteFunctionCompilation(env, wire_bytes_storage, counters, detected);
44   }
45 
46   if (result.succeeded() && counters) {
47     counters->wasm_generated_code_size()->Increment(
48         result.code_desc.instr_size);
49     counters->wasm_reloc_size()->Increment(result.code_desc.reloc_size);
50   }
51 
52   result.func_index = func_index_;
53   result.requested_tier = tier_;
54 
55   return result;
56 }
57 
ExecuteImportWrapperCompilation(CompilationEnv * env)58 WasmCompilationResult WasmCompilationUnit::ExecuteImportWrapperCompilation(
59     CompilationEnv* env) {
60   const FunctionSig* sig = env->module->functions[func_index_].sig;
61   // Assume the wrapper is going to be a JS function with matching arity at
62   // instantiation time.
63   auto kind = compiler::kDefaultImportCallKind;
64   bool source_positions = is_asmjs_module(env->module);
65   WasmCompilationResult result = compiler::CompileWasmImportCallWrapper(
66       env, kind, sig, source_positions,
67       static_cast<int>(sig->parameter_count()), wasm::kNoSuspend);
68   return result;
69 }
70 
ExecuteFunctionCompilation(CompilationEnv * env,const WireBytesStorage * wire_bytes_storage,Counters * counters,WasmFeatures * detected)71 WasmCompilationResult WasmCompilationUnit::ExecuteFunctionCompilation(
72     CompilationEnv* env, const WireBytesStorage* wire_bytes_storage,
73     Counters* counters, WasmFeatures* detected) {
74   auto* func = &env->module->functions[func_index_];
75   base::Vector<const uint8_t> code = wire_bytes_storage->GetCode(func->code);
76   wasm::FunctionBody func_body{func->sig, func->code.offset(), code.begin(),
77                                code.end()};
78 
79   base::Optional<TimedHistogramScope> wasm_compile_function_time_scope;
80   base::Optional<TimedHistogramScope> wasm_compile_huge_function_time_scope;
81   if (counters) {
82     if (func_body.end - func_body.start >= 100 * KB) {
83       auto huge_size_histogram = SELECT_WASM_COUNTER(
84           counters, env->module->origin, wasm, huge_function_size_bytes);
85       huge_size_histogram->AddSample(
86           static_cast<int>(func_body.end - func_body.start));
87       wasm_compile_huge_function_time_scope.emplace(
88           counters->wasm_compile_huge_function_time());
89     }
90     auto timed_histogram = SELECT_WASM_COUNTER(counters, env->module->origin,
91                                                wasm_compile, function_time);
92     wasm_compile_function_time_scope.emplace(timed_histogram);
93   }
94 
95   if (FLAG_trace_wasm_compiler) {
96     PrintF("Compiling wasm function %d with %s\n", func_index_,
97            ExecutionTierToString(tier_));
98   }
99 
100   WasmCompilationResult result;
101 
102   switch (tier_) {
103     case ExecutionTier::kNone:
104       UNREACHABLE();
105 
106     case ExecutionTier::kLiftoff:
107       // The --wasm-tier-mask-for-testing flag can force functions to be
108       // compiled with TurboFan, and the --wasm-debug-mask-for-testing can force
109       // them to be compiled for debugging, see documentation.
110       if (V8_LIKELY(FLAG_wasm_tier_mask_for_testing == 0) ||
111           func_index_ >= 32 ||
112           ((FLAG_wasm_tier_mask_for_testing & (1 << func_index_)) == 0) ||
113           FLAG_liftoff_only) {
114         // We do not use the debug side table, we only (optionally) pass it to
115         // cover different code paths in Liftoff for testing.
116         std::unique_ptr<DebugSideTable> unused_debug_sidetable;
117         std::unique_ptr<DebugSideTable>* debug_sidetable_ptr = nullptr;
118         if (V8_UNLIKELY(func_index_ < 32 && (FLAG_wasm_debug_mask_for_testing &
119                                              (1 << func_index_)) != 0)) {
120           debug_sidetable_ptr = &unused_debug_sidetable;
121         }
122         result = ExecuteLiftoffCompilation(
123             env, func_body, func_index_, for_debugging_,
124             LiftoffOptions{}
125                 .set_counters(counters)
126                 .set_detected_features(detected)
127                 .set_debug_sidetable(debug_sidetable_ptr));
128         if (result.succeeded()) break;
129       }
130 
131       // If --liftoff-only, do not fall back to turbofan, even if compilation
132       // failed.
133       if (FLAG_liftoff_only) break;
134 
135       // If Liftoff failed, fall back to turbofan.
136       // TODO(wasm): We could actually stop or remove the tiering unit for this
137       // function to avoid compiling it twice with TurboFan.
138       V8_FALLTHROUGH;
139 
140     case ExecutionTier::kTurbofan:
141       result = compiler::ExecuteTurbofanWasmCompilation(
142           env, wire_bytes_storage, func_body, func_index_, counters, detected);
143       result.for_debugging = for_debugging_;
144       break;
145   }
146 
147   return result;
148 }
149 
150 // static
CompileWasmFunction(Isolate * isolate,NativeModule * native_module,WasmFeatures * detected,const WasmFunction * function,ExecutionTier tier)151 void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
152                                               NativeModule* native_module,
153                                               WasmFeatures* detected,
154                                               const WasmFunction* function,
155                                               ExecutionTier tier) {
156   ModuleWireBytes wire_bytes(native_module->wire_bytes());
157   FunctionBody function_body{function->sig, function->code.offset(),
158                              wire_bytes.start() + function->code.offset(),
159                              wire_bytes.start() + function->code.end_offset()};
160 
161   DCHECK_LE(native_module->num_imported_functions(), function->func_index);
162   DCHECK_LT(function->func_index, native_module->num_functions());
163   WasmCompilationUnit unit(function->func_index, tier, kNoDebugging);
164   CompilationEnv env = native_module->CreateCompilationEnv();
165   WasmCompilationResult result = unit.ExecuteCompilation(
166       &env, native_module->compilation_state()->GetWireBytesStorage().get(),
167       isolate->counters(), detected);
168   if (result.succeeded()) {
169     WasmCodeRefScope code_ref_scope;
170     native_module->PublishCode(
171         native_module->AddCompiledCode(std::move(result)));
172   } else {
173     native_module->compilation_state()->SetError();
174   }
175 }
176 
177 namespace {
UseGenericWrapper(const FunctionSig * sig)178 bool UseGenericWrapper(const FunctionSig* sig) {
179 #if V8_TARGET_ARCH_X64
180   if (sig->returns().size() > 1) {
181     return false;
182   }
183   if (sig->returns().size() == 1) {
184     ValueType ret = sig->GetReturn(0);
185     if (ret.kind() == kS128) return false;
186     if (ret.is_reference()) {
187       if (ret.heap_representation() != wasm::HeapType::kAny &&
188           ret.heap_representation() != wasm::HeapType::kFunc) {
189         return false;
190       }
191     }
192   }
193   for (ValueType type : sig->parameters()) {
194     if (type.kind() != kI32 && type.kind() != kI64 && type.kind() != kF32 &&
195         type.kind() != kF64 &&
196         !(type.is_reference() &&
197           type.heap_representation() == wasm::HeapType::kAny)) {
198       return false;
199     }
200   }
201   return FLAG_wasm_generic_wrapper;
202 #else
203   return false;
204 #endif
205 }
206 }  // namespace
207 
JSToWasmWrapperCompilationUnit(Isolate * isolate,const FunctionSig * sig,const WasmModule * module,bool is_import,const WasmFeatures & enabled_features,AllowGeneric allow_generic)208 JSToWasmWrapperCompilationUnit::JSToWasmWrapperCompilationUnit(
209     Isolate* isolate, const FunctionSig* sig, const WasmModule* module,
210     bool is_import, const WasmFeatures& enabled_features,
211     AllowGeneric allow_generic)
212     : isolate_(isolate),
213       is_import_(is_import),
214       sig_(sig),
215       use_generic_wrapper_(allow_generic && UseGenericWrapper(sig) &&
216                            !is_import),
217       job_(use_generic_wrapper_
218                ? nullptr
219                : compiler::NewJSToWasmCompilationJob(
220                      isolate, sig, module, is_import, enabled_features)) {}
221 
222 JSToWasmWrapperCompilationUnit::~JSToWasmWrapperCompilationUnit() = default;
223 
Execute()224 void JSToWasmWrapperCompilationUnit::Execute() {
225   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
226                "wasm.CompileJSToWasmWrapper");
227   if (!use_generic_wrapper_) {
228     CompilationJob::Status status = job_->ExecuteJob(nullptr);
229     CHECK_EQ(status, CompilationJob::SUCCEEDED);
230   }
231 }
232 
Finalize()233 Handle<Code> JSToWasmWrapperCompilationUnit::Finalize() {
234   if (use_generic_wrapper_) {
235     return FromCodeT(
236         isolate_->builtins()->code_handle(Builtin::kGenericJSToWasmWrapper),
237         isolate_);
238   }
239 
240   CompilationJob::Status status = job_->FinalizeJob(isolate_);
241   CHECK_EQ(status, CompilationJob::SUCCEEDED);
242   Handle<Code> code = job_->compilation_info()->code();
243   if (isolate_->logger()->is_listening_to_code_events() ||
244       isolate_->is_profiling()) {
245     Handle<String> name = isolate_->factory()->NewStringFromAsciiChecked(
246         job_->compilation_info()->GetDebugName().get());
247     PROFILE(isolate_, CodeCreateEvent(CodeEventListener::STUB_TAG,
248                                       Handle<AbstractCode>::cast(code), name));
249   }
250   return code;
251 }
252 
253 // static
CompileJSToWasmWrapper(Isolate * isolate,const FunctionSig * sig,const WasmModule * module,bool is_import)254 Handle<Code> JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
255     Isolate* isolate, const FunctionSig* sig, const WasmModule* module,
256     bool is_import) {
257   // Run the compilation unit synchronously.
258   WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
259   JSToWasmWrapperCompilationUnit unit(isolate, sig, module, is_import,
260                                       enabled_features, kAllowGeneric);
261   unit.Execute();
262   return unit.Finalize();
263 }
264 
265 // static
CompileSpecificJSToWasmWrapper(Isolate * isolate,const FunctionSig * sig,const WasmModule * module)266 Handle<Code> JSToWasmWrapperCompilationUnit::CompileSpecificJSToWasmWrapper(
267     Isolate* isolate, const FunctionSig* sig, const WasmModule* module) {
268   // Run the compilation unit synchronously.
269   const bool is_import = false;
270   WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
271   JSToWasmWrapperCompilationUnit unit(isolate, sig, module, is_import,
272                                       enabled_features, kDontAllowGeneric);
273   unit.Execute();
274   return unit.Finalize();
275 }
276 
277 }  // namespace wasm
278 }  // namespace internal
279 }  // namespace v8
280