Line data Source code
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/compiler/wasm-compiler.h"
8 : #include "src/counters.h"
9 : #include "src/macro-assembler-inl.h"
10 : #include "src/wasm/baseline/liftoff-compiler.h"
11 : #include "src/wasm/wasm-code-manager.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 : namespace wasm {
16 :
17 : namespace {
18 :
19 2912435 : class WasmInstructionBufferImpl {
20 : public:
21 : class View : public AssemblerBuffer {
22 : public:
23 : View(Vector<uint8_t> buffer, WasmInstructionBufferImpl* holder)
24 1459020 : : buffer_(buffer), holder_(holder) {}
25 :
26 4371551 : ~View() override {
27 2913712 : if (buffer_.start() == holder_->old_buffer_.start()) {
28 : DCHECK_EQ(buffer_.size(), holder_->old_buffer_.size());
29 : holder_->old_buffer_ = {};
30 : }
31 2914695 : }
32 :
33 4323525 : byte* start() const override { return buffer_.start(); }
34 :
35 2894725 : int size() const override { return static_cast<int>(buffer_.size()); }
36 :
37 1248 : std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
38 : // If we grow, we must be the current buffer of {holder_}.
39 : DCHECK_EQ(buffer_.start(), holder_->buffer_.start());
40 : DCHECK_EQ(buffer_.size(), holder_->buffer_.size());
41 : DCHECK_NULL(holder_->old_buffer_);
42 :
43 : DCHECK_LT(size(), new_size);
44 :
45 1248 : holder_->old_buffer_ = std::move(holder_->buffer_);
46 2496 : holder_->buffer_ = OwnedVector<uint8_t>::New(new_size);
47 2496 : return base::make_unique<View>(holder_->buffer_.as_vector(), holder_);
48 : }
49 :
50 : private:
51 : const Vector<uint8_t> buffer_;
52 : WasmInstructionBufferImpl* const holder_;
53 : };
54 :
55 1454711 : std::unique_ptr<AssemblerBuffer> CreateView() {
56 : DCHECK_NOT_NULL(buffer_);
57 1457772 : return base::make_unique<View>(buffer_.as_vector(), this);
58 : }
59 :
60 : std::unique_ptr<uint8_t[]> ReleaseBuffer() {
61 : DCHECK_NULL(old_buffer_);
62 : DCHECK_NOT_NULL(buffer_);
63 : return buffer_.ReleaseData();
64 : }
65 :
66 : bool released() const { return buffer_ == nullptr; }
67 :
68 : private:
69 : // The current buffer used to emit code.
70 : OwnedVector<uint8_t> buffer_ =
71 : OwnedVector<uint8_t>::New(AssemblerBase::kMinimalBufferSize);
72 :
73 : // While the buffer is grown, we need to temporarily also keep the old buffer
74 : // alive.
75 : OwnedVector<uint8_t> old_buffer_;
76 : };
77 :
78 : WasmInstructionBufferImpl* Impl(WasmInstructionBuffer* buf) {
79 : return reinterpret_cast<WasmInstructionBufferImpl*>(buf);
80 : }
81 :
82 : } // namespace
83 :
84 : // PIMPL interface WasmInstructionBuffer for WasmInstBufferImpl
85 1456582 : WasmInstructionBuffer::~WasmInstructionBuffer() {
86 1456582 : Impl(this)->~WasmInstructionBufferImpl();
87 1456445 : }
88 :
89 1454747 : std::unique_ptr<AssemblerBuffer> WasmInstructionBuffer::CreateView() {
90 1454747 : return Impl(this)->CreateView();
91 : }
92 :
93 1435706 : std::unique_ptr<uint8_t[]> WasmInstructionBuffer::ReleaseBuffer() {
94 1435706 : return Impl(this)->ReleaseBuffer();
95 : }
96 :
97 : // static
98 1454079 : std::unique_ptr<WasmInstructionBuffer> WasmInstructionBuffer::New() {
99 : return std::unique_ptr<WasmInstructionBuffer>{
100 : reinterpret_cast<WasmInstructionBuffer*>(
101 2911333 : new WasmInstructionBufferImpl())};
102 : }
103 : // End of PIMPL interface WasmInstructionBuffer for WasmInstBufferImpl
104 :
105 : // static
106 420226 : ExecutionTier WasmCompilationUnit::GetDefaultExecutionTier(
107 : const WasmModule* module) {
108 : // Liftoff does not support the special asm.js opcodes, thus always compile
109 : // asm.js modules with TurboFan.
110 420226 : if (module->origin == kAsmJsOrigin) return ExecutionTier::kTurbofan;
111 412257 : if (FLAG_wasm_interpret_all) return ExecutionTier::kInterpreter;
112 408263 : return FLAG_liftoff ? ExecutionTier::kLiftoff : ExecutionTier::kTurbofan;
113 : }
114 :
115 1075327 : WasmCompilationUnit::WasmCompilationUnit(int index, ExecutionTier tier)
116 1075327 : : func_index_(index), tier_(tier) {
117 1075327 : if (V8_UNLIKELY(FLAG_wasm_tier_mask_for_testing) && index < 32 &&
118 : (FLAG_wasm_tier_mask_for_testing & (1 << index))) {
119 : tier = ExecutionTier::kTurbofan;
120 : }
121 1075327 : SwitchTier(tier);
122 1075326 : }
123 :
124 : // Declared here such that {LiftoffCompilationUnit} and
125 : // {TurbofanWasmCompilationUnit} can be opaque in the header file.
126 : WasmCompilationUnit::~WasmCompilationUnit() = default;
127 :
128 1073047 : WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
129 : WasmEngine* wasm_engine, CompilationEnv* env,
130 : const std::shared_ptr<WireBytesStorage>& wire_bytes_storage,
131 : Counters* counters, WasmFeatures* detected) {
132 1073047 : auto* func = &env->module->functions[func_index_];
133 1073047 : Vector<const uint8_t> code = wire_bytes_storage->GetCode(func->code);
134 1072998 : wasm::FunctionBody func_body{func->sig, func->code.offset(), code.start(),
135 : code.end()};
136 :
137 1072998 : auto size_histogram = SELECT_WASM_COUNTER(counters, env->module->origin, wasm,
138 : function_size_bytes);
139 1072998 : size_histogram->AddSample(static_cast<int>(func_body.end - func_body.start));
140 1072992 : auto timed_histogram = SELECT_WASM_COUNTER(counters, env->module->origin,
141 : wasm_compile, function_time);
142 : TimedHistogramScope wasm_compile_function_time_scope(timed_histogram);
143 :
144 : // Exactly one compiler-specific unit must be set.
145 : DCHECK_EQ(1, !!liftoff_unit_ + !!turbofan_unit_ + !!interpreter_unit_);
146 :
147 : if (FLAG_trace_wasm_compiler) {
148 : const char* tier =
149 : liftoff_unit_ ? "liftoff" : turbofan_unit_ ? "turbofan" : "interpreter";
150 : PrintF("Compiling wasm function %d with %s\n\n", func_index_, tier);
151 : }
152 :
153 : WasmCompilationResult result;
154 1072600 : if (liftoff_unit_) {
155 921288 : result = liftoff_unit_->ExecuteCompilation(wasm_engine->allocator(), env,
156 460404 : func_body, counters, detected);
157 460329 : if (!result.succeeded()) {
158 : // If Liftoff failed, fall back to turbofan.
159 : // TODO(wasm): We could actually stop or remove the tiering unit for this
160 : // function to avoid compiling it twice with TurboFan.
161 18806 : SwitchTier(ExecutionTier::kTurbofan);
162 : DCHECK_NOT_NULL(turbofan_unit_);
163 : }
164 : }
165 1072129 : if (turbofan_unit_) {
166 1255489 : result = turbofan_unit_->ExecuteCompilation(wasm_engine, env, func_body,
167 627011 : counters, detected);
168 : }
169 1069765 : if (interpreter_unit_) {
170 2862 : result = interpreter_unit_->ExecuteCompilation(wasm_engine, env, func_body,
171 1429 : counters, detected);
172 : }
173 1069756 : result.func_index = func_index_;
174 1069756 : result.requested_tier = tier_;
175 :
176 1069756 : if (result.succeeded()) {
177 1060643 : counters->wasm_generated_code_size()->Increment(
178 1060643 : result.code_desc.instr_size);
179 2121130 : counters->wasm_reloc_size()->Increment(result.code_desc.reloc_size);
180 : }
181 :
182 1069542 : return result;
183 : }
184 :
185 1094108 : void WasmCompilationUnit::SwitchTier(ExecutionTier new_tier) {
186 : // This method is being called in the constructor, where neither
187 : // {liftoff_unit_} nor {turbofan_unit_} nor {interpreter_unit_} are set, or to
188 : // switch tier from kLiftoff to kTurbofan, in which case {liftoff_unit_} is
189 : // already set.
190 1094108 : switch (new_tier) {
191 : case ExecutionTier::kLiftoff:
192 : DCHECK(!turbofan_unit_);
193 : DCHECK(!liftoff_unit_);
194 : DCHECK(!interpreter_unit_);
195 461561 : liftoff_unit_.reset(new LiftoffCompilationUnit());
196 : return;
197 : case ExecutionTier::kTurbofan:
198 : DCHECK(!turbofan_unit_);
199 : DCHECK(!interpreter_unit_);
200 : liftoff_unit_.reset();
201 631217 : turbofan_unit_.reset(new compiler::TurbofanWasmCompilationUnit(this));
202 631166 : return;
203 : case ExecutionTier::kInterpreter:
204 : DCHECK(!turbofan_unit_);
205 : DCHECK(!liftoff_unit_);
206 : DCHECK(!interpreter_unit_);
207 1439 : interpreter_unit_.reset(new compiler::InterpreterCompilationUnit(this));
208 : return;
209 : case ExecutionTier::kNone:
210 0 : UNREACHABLE();
211 : }
212 0 : UNREACHABLE();
213 : }
214 :
215 : // static
216 20 : void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
217 : NativeModule* native_module,
218 : WasmFeatures* detected,
219 : const WasmFunction* function,
220 : ExecutionTier tier) {
221 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
222 : FunctionBody function_body{function->sig, function->code.offset(),
223 : wire_bytes.start() + function->code.offset(),
224 : wire_bytes.start() + function->code.end_offset()};
225 :
226 40 : WasmCompilationUnit unit(function->func_index, tier);
227 20 : CompilationEnv env = native_module->CreateCompilationEnv();
228 : WasmCompilationResult result = unit.ExecuteCompilation(
229 : isolate->wasm_engine(), &env,
230 40 : native_module->compilation_state()->GetWireBytesStorage(),
231 40 : isolate->counters(), detected);
232 20 : if (result.succeeded()) {
233 40 : WasmCodeRefScope code_ref_scope;
234 20 : native_module->AddCompiledCode(std::move(result));
235 : } else {
236 0 : native_module->compilation_state()->SetError();
237 : }
238 20 : }
239 :
240 : } // namespace wasm
241 : } // namespace internal
242 122004 : } // namespace v8
|