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