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