// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/wasm/function-compiler.h" #include "src/compiler/wasm-compiler.h" #include "src/counters.h" #include "src/macro-assembler-inl.h" #include "src/wasm/baseline/liftoff-compiler.h" #include "src/wasm/wasm-code-manager.h" namespace v8 { namespace internal { namespace wasm { namespace { const char* GetExecutionTierAsString(ExecutionTier mode) { switch (mode) { case ExecutionTier::kBaseline: return "liftoff"; case ExecutionTier::kOptimized: return "turbofan"; case ExecutionTier::kInterpreter: return "interpreter"; } UNREACHABLE(); } void RecordStats(const WasmCode* code, Counters* counters) { counters->wasm_generated_code_size()->Increment( static_cast(code->instructions().size())); counters->wasm_reloc_size()->Increment( static_cast(code->reloc_info().size())); } } // namespace // static ExecutionTier WasmCompilationUnit::GetDefaultExecutionTier() { return FLAG_liftoff ? ExecutionTier::kBaseline : ExecutionTier::kOptimized; } WasmCompilationUnit::WasmCompilationUnit(WasmEngine* wasm_engine, ModuleEnv* env, NativeModule* native_module, FunctionBody body, WasmName name, int index, Counters* counters, ExecutionTier mode) : env_(env), wasm_engine_(wasm_engine), func_body_(body), func_name_(name), counters_(counters), func_index_(index), native_module_(native_module), mode_(mode) { DCHECK_GE(index, env->module->num_imported_functions); DCHECK_LT(index, env->module->functions.size()); // Always disable Liftoff for asm.js, for two reasons: // 1) asm-specific opcodes are not implemented, and // 2) tier-up does not work with lazy compilation. if (env->module->origin == kAsmJsOrigin) mode = ExecutionTier::kOptimized; if (V8_UNLIKELY(FLAG_wasm_tier_mask_for_testing) && index < 32 && (FLAG_wasm_tier_mask_for_testing & (1 << index))) { mode = ExecutionTier::kOptimized; } SwitchMode(mode); } // Declared here such that {LiftoffCompilationUnit} and // {TurbofanWasmCompilationUnit} can be opaque in the header file. WasmCompilationUnit::~WasmCompilationUnit() {} void WasmCompilationUnit::ExecuteCompilation(WasmFeatures* detected) { auto size_histogram = SELECT_WASM_COUNTER(counters_, env_->module->origin, wasm, function_size_bytes); size_histogram->AddSample( static_cast(func_body_.end - func_body_.start)); auto timed_histogram = SELECT_WASM_COUNTER(counters_, env_->module->origin, wasm_compile, function_time); TimedHistogramScope wasm_compile_function_time_scope(timed_histogram); if (FLAG_trace_wasm_compiler) { PrintF("Compiling wasm function %d with %s\n\n", func_index_, GetExecutionTierAsString(mode_)); } switch (mode_) { case ExecutionTier::kBaseline: if (liftoff_unit_->ExecuteCompilation(detected)) break; // Otherwise, fall back to turbofan. SwitchMode(ExecutionTier::kOptimized); V8_FALLTHROUGH; case ExecutionTier::kOptimized: turbofan_unit_->ExecuteCompilation(detected); break; case ExecutionTier::kInterpreter: UNREACHABLE(); // TODO(titzer): compile interpreter entry stub. } } WasmCode* WasmCompilationUnit::FinishCompilation(ErrorThrower* thrower) { WasmCode* ret; switch (mode_) { case ExecutionTier::kBaseline: ret = liftoff_unit_->FinishCompilation(thrower); break; case ExecutionTier::kOptimized: ret = turbofan_unit_->FinishCompilation(thrower); break; case ExecutionTier::kInterpreter: UNREACHABLE(); // TODO(titzer): finish interpreter entry stub. } if (ret == nullptr) { thrower->RuntimeError("Error finalizing code."); } else { RecordStats(ret, counters_); } return ret; } void WasmCompilationUnit::SwitchMode(ExecutionTier new_mode) { // This method is being called in the constructor, where neither // {liftoff_unit_} nor {turbofan_unit_} are set, or to switch mode from // kLiftoff to kTurbofan, in which case {liftoff_unit_} is already set. mode_ = new_mode; switch (new_mode) { case ExecutionTier::kBaseline: DCHECK(!turbofan_unit_); DCHECK(!liftoff_unit_); liftoff_unit_.reset(new LiftoffCompilationUnit(this)); return; case ExecutionTier::kOptimized: DCHECK(!turbofan_unit_); liftoff_unit_.reset(); turbofan_unit_.reset(new compiler::TurbofanWasmCompilationUnit(this)); return; case ExecutionTier::kInterpreter: UNREACHABLE(); // TODO(titzer): allow compiling interpreter entry stub. } UNREACHABLE(); } // static WasmCode* WasmCompilationUnit::CompileWasmFunction( Isolate* isolate, NativeModule* native_module, WasmFeatures* detected, ErrorThrower* thrower, ModuleEnv* env, const WasmFunction* function, ExecutionTier mode) { ModuleWireBytes wire_bytes(native_module->wire_bytes()); FunctionBody function_body{function->sig, function->code.offset(), wire_bytes.start() + function->code.offset(), wire_bytes.start() + function->code.end_offset()}; WasmCompilationUnit unit(isolate->wasm_engine(), env, native_module, function_body, wire_bytes.GetNameOrNull(function, env->module), function->func_index, isolate->counters(), mode); unit.ExecuteCompilation(detected); return unit.FinishCompilation(thrower); } } // namespace wasm } // namespace internal } // namespace v8