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 : const char* GetExecutionTierAsString(ExecutionTier tier) {
20 : switch (tier) {
21 : case ExecutionTier::kBaseline:
22 : return "liftoff";
23 : case ExecutionTier::kOptimized:
24 : return "turbofan";
25 : case ExecutionTier::kInterpreter:
26 : return "interpreter";
27 : }
28 : UNREACHABLE();
29 : }
30 :
31 3271538 : class WasmInstructionBufferImpl {
32 : public:
33 : class View : public AssemblerBuffer {
34 : public:
35 : View(Vector<uint8_t> buffer, WasmInstructionBufferImpl* holder)
36 1091767 : : buffer_(buffer), holder_(holder) {}
37 :
38 2182918 : ~View() override {
39 2182840 : if (buffer_.start() == holder_->old_buffer_.start()) {
40 : DCHECK_EQ(buffer_.size(), holder_->old_buffer_.size());
41 2006 : holder_->old_buffer_ = {};
42 : }
43 2182918 : }
44 :
45 3224148 : byte* start() const override { return buffer_.start(); }
46 :
47 2158248 : int size() const override { return static_cast<int>(buffer_.size()); }
48 :
49 1003 : std::unique_ptr<AssemblerBuffer> Grow(int new_size) override {
50 : // If we grow, we must be the current buffer of {holder_}.
51 : DCHECK_EQ(buffer_.start(), holder_->buffer_.start());
52 : DCHECK_EQ(buffer_.size(), holder_->buffer_.size());
53 : DCHECK_NULL(holder_->old_buffer_);
54 :
55 : DCHECK_LT(size(), new_size);
56 :
57 1003 : holder_->old_buffer_ = std::move(holder_->buffer_);
58 3009 : holder_->buffer_ = OwnedVector<uint8_t>::New(new_size);
59 3009 : return base::make_unique<View>(holder_->buffer_.as_vector(), holder_);
60 : }
61 :
62 : private:
63 : const Vector<uint8_t> buffer_;
64 : WasmInstructionBufferImpl* const holder_;
65 : };
66 :
67 1090669 : std::unique_ptr<AssemblerBuffer> CreateView() {
68 : DCHECK_NOT_NULL(buffer_);
69 2181430 : return base::make_unique<View>(buffer_.as_vector(), this);
70 : }
71 :
72 : std::unique_ptr<uint8_t[]> ReleaseBuffer() {
73 : DCHECK_NULL(old_buffer_);
74 : DCHECK_NOT_NULL(buffer_);
75 : return buffer_.ReleaseData();
76 : }
77 :
78 : bool released() const { return buffer_ == nullptr; }
79 :
80 : private:
81 : // The current buffer used to emit code.
82 : OwnedVector<uint8_t> buffer_ =
83 : OwnedVector<uint8_t>::New(AssemblerBase::kMinimalBufferSize);
84 :
85 : // While the buffer is grown, we need to temporarily also keep the old
86 : // buffer alive.
87 : OwnedVector<uint8_t> old_buffer_;
88 : };
89 :
90 : WasmInstructionBufferImpl* Impl(WasmInstructionBuffer* buf) {
91 : return reinterpret_cast<WasmInstructionBufferImpl*>(buf);
92 : }
93 :
94 : } // namespace
95 :
96 : // PIMPL interface WasmInstructionBuffer for WasmInstBufferImpl
97 1090349 : WasmInstructionBuffer::~WasmInstructionBuffer() {
98 1090349 : Impl(this)->~WasmInstructionBufferImpl();
99 1090501 : }
100 :
101 1090678 : std::unique_ptr<AssemblerBuffer> WasmInstructionBuffer::CreateView() {
102 1090678 : return Impl(this)->CreateView();
103 : }
104 :
105 1066544 : std::unique_ptr<uint8_t[]> WasmInstructionBuffer::ReleaseBuffer() {
106 1066544 : return Impl(this)->ReleaseBuffer();
107 : }
108 :
109 : // static
110 1090500 : std::unique_ptr<WasmInstructionBuffer> WasmInstructionBuffer::New() {
111 : return std::unique_ptr<WasmInstructionBuffer>{
112 : reinterpret_cast<WasmInstructionBuffer*>(
113 2181242 : new WasmInstructionBufferImpl())};
114 : }
115 : // End of PIMPL interface WasmInstructionBuffer for WasmInstBufferImpl
116 :
117 : // static
118 150004 : ExecutionTier WasmCompilationUnit::GetDefaultExecutionTier(
119 : const WasmModule* module) {
120 149968 : return FLAG_liftoff && module->origin == kWasmOrigin
121 : ? ExecutionTier::kBaseline
122 300008 : : ExecutionTier::kOptimized;
123 : }
124 :
125 1074796 : WasmCompilationUnit::WasmCompilationUnit(WasmEngine* wasm_engine, int index,
126 : ExecutionTier tier)
127 1074796 : : wasm_engine_(wasm_engine), func_index_(index), requested_tier_(tier) {
128 1074796 : if (V8_UNLIKELY(FLAG_wasm_tier_mask_for_testing) && index < 32 &&
129 : (FLAG_wasm_tier_mask_for_testing & (1 << index))) {
130 : tier = ExecutionTier::kOptimized;
131 : }
132 1074796 : SwitchTier(tier);
133 1074795 : }
134 :
135 : // Declared here such that {LiftoffCompilationUnit} and
136 : // {TurbofanWasmCompilationUnit} can be opaque in the header file.
137 : WasmCompilationUnit::~WasmCompilationUnit() = default;
138 :
139 1073506 : WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
140 : CompilationEnv* env,
141 : const std::shared_ptr<WireBytesStorage>& wire_bytes_storage,
142 : Counters* counters, WasmFeatures* detected) {
143 1073506 : auto* func = &env->module->functions[func_index_];
144 1073506 : Vector<const uint8_t> code = wire_bytes_storage->GetCode(func->code);
145 : wasm::FunctionBody func_body{func->sig, func->code.offset(), code.start(),
146 1073495 : code.end()};
147 :
148 1073495 : auto size_histogram = SELECT_WASM_COUNTER(counters, env->module->origin, wasm,
149 : function_size_bytes);
150 1073495 : size_histogram->AddSample(static_cast<int>(func_body.end - func_body.start));
151 1073492 : auto timed_histogram = SELECT_WASM_COUNTER(counters, env->module->origin,
152 : wasm_compile, function_time);
153 : TimedHistogramScope wasm_compile_function_time_scope(timed_histogram);
154 :
155 : if (FLAG_trace_wasm_compiler) {
156 : PrintF("Compiling wasm function %d with %s\n\n", func_index_,
157 : GetExecutionTierAsString(executed_tier_));
158 : }
159 :
160 1073488 : WasmCompilationResult result;
161 1073487 : switch (executed_tier_) {
162 : case ExecutionTier::kBaseline:
163 1188369 : result =
164 594208 : liftoff_unit_->ExecuteCompilation(env, func_body, counters, detected);
165 594149 : if (result.succeeded()) break;
166 : // Otherwise, fall back to turbofan.
167 23943 : SwitchTier(ExecutionTier::kOptimized);
168 : // TODO(wasm): We could actually stop or remove the tiering unit for this
169 : // function to avoid compiling it twice with TurboFan.
170 : V8_FALLTHROUGH;
171 : case ExecutionTier::kOptimized:
172 1006444 : result = turbofan_unit_->ExecuteCompilation(env, func_body, counters,
173 503213 : detected);
174 503186 : break;
175 : case ExecutionTier::kInterpreter:
176 0 : UNREACHABLE(); // TODO(titzer): compile interpreter entry stub.
177 : }
178 :
179 1073371 : if (result.succeeded()) {
180 : counters->wasm_generated_code_size()->Increment(
181 1066683 : result.code_desc.instr_size);
182 1066660 : counters->wasm_reloc_size()->Increment(result.code_desc.reloc_size);
183 : }
184 :
185 1073395 : return result;
186 : }
187 :
188 1073333 : WasmCode* WasmCompilationUnit::Publish(WasmCompilationResult result,
189 : NativeModule* native_module) {
190 1073333 : if (!result.succeeded()) {
191 : native_module->compilation_state()->SetError(func_index_,
192 13292 : std::move(result.error));
193 6646 : return nullptr;
194 : }
195 :
196 : DCHECK(result.succeeded());
197 : WasmCode::Tier code_tier = executed_tier_ == ExecutionTier::kBaseline
198 : ? WasmCode::kLiftoff
199 1066687 : : WasmCode::kTurbofan;
200 : DCHECK_EQ(result.code_desc.buffer, result.instr_buffer.get());
201 :
202 : WasmCode* code = native_module->AddCode(
203 : func_index_, result.code_desc, result.frame_slot_count,
204 : result.tagged_parameter_slots, std::move(result.protected_instructions),
205 4266800 : std::move(result.source_positions), WasmCode::kFunction, code_tier);
206 : // TODO(clemensh): Merge this into {AddCode}?
207 1066713 : native_module->PublishCode(code);
208 1066716 : return code;
209 : }
210 :
211 1098733 : void WasmCompilationUnit::SwitchTier(ExecutionTier new_tier) {
212 : // This method is being called in the constructor, where neither
213 : // {liftoff_unit_} nor {turbofan_unit_} are set, or to switch tier from
214 : // kLiftoff to kTurbofan, in which case {liftoff_unit_} is already set.
215 1098733 : executed_tier_ = new_tier;
216 1098733 : switch (new_tier) {
217 : case ExecutionTier::kBaseline:
218 : DCHECK(!turbofan_unit_);
219 : DCHECK(!liftoff_unit_);
220 594543 : liftoff_unit_.reset(new LiftoffCompilationUnit(this));
221 : return;
222 : case ExecutionTier::kOptimized:
223 : DCHECK(!turbofan_unit_);
224 : liftoff_unit_.reset();
225 504200 : turbofan_unit_.reset(new compiler::TurbofanWasmCompilationUnit(this));
226 : return;
227 : case ExecutionTier::kInterpreter:
228 0 : UNREACHABLE(); // TODO(titzer): allow compiling interpreter entry stub.
229 : }
230 0 : UNREACHABLE();
231 : }
232 :
233 : // static
234 130386 : void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
235 : NativeModule* native_module,
236 : WasmFeatures* detected,
237 : const WasmFunction* function,
238 : ExecutionTier tier) {
239 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
240 : FunctionBody function_body{function->sig, function->code.offset(),
241 : wire_bytes.start() + function->code.offset(),
242 : wire_bytes.start() + function->code.end_offset()};
243 :
244 260772 : WasmCompilationUnit unit(isolate->wasm_engine(), function->func_index, tier);
245 130385 : CompilationEnv env = native_module->CreateCompilationEnv();
246 : WasmCompilationResult result = unit.ExecuteCompilation(
247 : &env, native_module->compilation_state()->GetWireBytesStorage(),
248 391157 : isolate->counters(), detected);
249 260772 : unit.Publish(std::move(result), native_module);
250 130386 : }
251 :
252 : } // namespace wasm
253 : } // namespace internal
254 178779 : } // namespace v8
|