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
22namespace v8 {
23namespace internal {
24namespace wasm {
25
26// static
27ExecutionTier 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
35WasmCompilationResult 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
58WasmCompilationResult 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
71WasmCompilationResult 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
151void 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
177namespace {
178bool 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
208JSToWasmWrapperCompilationUnit::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
222JSToWasmWrapperCompilationUnit::~JSToWasmWrapperCompilationUnit() = default;
223
224void 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
233Handle<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
254Handle<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
266Handle<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