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 2933435 : class WasmInstructionBufferImpl {
20 : public:
21 : class View : public AssemblerBuffer {
22 : public:
23 : View(Vector<uint8_t> buffer, WasmInstructionBufferImpl* holder)
24 1469634 : : buffer_(buffer), holder_(holder) {}
25 :
26 4401239 : ~View() override {
27 2934560 : if (buffer_.start() == holder_->old_buffer_.start()) {
28 : DCHECK_EQ(buffer_.size(), holder_->old_buffer_.size());
29 : holder_->old_buffer_ = {};
30 : }
31 2933959 : }
32 :
33 4340236 : byte* start() const override { return buffer_.start(); }
34 :
35 2908390 : int size() const override { return static_cast<int>(buffer_.size()); }
36 :
37 1558 : 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 1558 : holder_->old_buffer_ = std::move(holder_->buffer_);
46 3116 : holder_->buffer_ = OwnedVector<uint8_t>::New(new_size);
47 3116 : 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 1465625 : std::unique_ptr<AssemblerBuffer> CreateView() {
56 : DCHECK_NOT_NULL(buffer_);
57 1468076 : 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 1466458 : WasmInstructionBuffer::~WasmInstructionBuffer() {
86 1466458 : Impl(this)->~WasmInstructionBufferImpl();
87 1466969 : }
88 :
89 1465527 : std::unique_ptr<AssemblerBuffer> WasmInstructionBuffer::CreateView() {
90 1465527 : return Impl(this)->CreateView();
91 : }
92 :
93 1437550 : std::unique_ptr<uint8_t[]> WasmInstructionBuffer::ReleaseBuffer() {
94 1437550 : return Impl(this)->ReleaseBuffer();
95 : }
96 :
97 : // static
98 1464260 : std::unique_ptr<WasmInstructionBuffer> WasmInstructionBuffer::New() {
99 : return std::unique_ptr<WasmInstructionBuffer>{
100 : reinterpret_cast<WasmInstructionBuffer*>(
101 2932262 : new WasmInstructionBufferImpl())};
102 : }
103 : // End of PIMPL interface WasmInstructionBuffer for WasmInstBufferImpl
104 :
105 : // static
106 151536 : ExecutionTier WasmCompilationUnit::GetDefaultExecutionTier(
107 : const WasmModule* module) {
108 151536 : if (module->origin == kWasmOrigin) {
109 143614 : if (FLAG_wasm_interpret_all) {
110 : return ExecutionTier::kInterpreter;
111 143306 : } else if (FLAG_liftoff) {
112 : return ExecutionTier::kBaseline;
113 : }
114 : }
115 7958 : return ExecutionTier::kOptimized;
116 : }
117 :
118 1077725 : WasmCompilationUnit::WasmCompilationUnit(WasmEngine* wasm_engine, int index,
119 : ExecutionTier tier)
120 1077725 : : wasm_engine_(wasm_engine), func_index_(index), tier_(tier) {
121 1077725 : if (V8_UNLIKELY(FLAG_wasm_tier_mask_for_testing) && index < 32 &&
122 : (FLAG_wasm_tier_mask_for_testing & (1 << index))) {
123 : tier = ExecutionTier::kOptimized;
124 : }
125 1077725 : SwitchTier(tier);
126 1077725 : }
127 :
128 : // Declared here such that {LiftoffCompilationUnit} and
129 : // {TurbofanWasmCompilationUnit} can be opaque in the header file.
130 : WasmCompilationUnit::~WasmCompilationUnit() = default;
131 :
132 1075653 : WasmCompilationResult WasmCompilationUnit::ExecuteCompilation(
133 : CompilationEnv* env,
134 : const std::shared_ptr<WireBytesStorage>& wire_bytes_storage,
135 : Counters* counters, WasmFeatures* detected) {
136 1075653 : auto* func = &env->module->functions[func_index_];
137 1075653 : Vector<const uint8_t> code = wire_bytes_storage->GetCode(func->code);
138 1075570 : wasm::FunctionBody func_body{func->sig, func->code.offset(), code.start(),
139 : code.end()};
140 :
141 1075570 : auto size_histogram = SELECT_WASM_COUNTER(counters, env->module->origin, wasm,
142 : function_size_bytes);
143 1075570 : size_histogram->AddSample(static_cast<int>(func_body.end - func_body.start));
144 1075588 : auto timed_histogram = SELECT_WASM_COUNTER(counters, env->module->origin,
145 : wasm_compile, function_time);
146 : TimedHistogramScope wasm_compile_function_time_scope(timed_histogram);
147 :
148 : // Exactly one compiler-specific unit must be set.
149 : DCHECK_EQ(1, !!liftoff_unit_ + !!turbofan_unit_ + !!interpreter_unit_);
150 :
151 : if (FLAG_trace_wasm_compiler) {
152 : const char* tier =
153 : liftoff_unit_ ? "liftoff" : turbofan_unit_ ? "turbofan" : "interpreter";
154 : PrintF("Compiling wasm function %d with %s\n\n", func_index_, tier);
155 : }
156 :
157 : WasmCompilationResult result;
158 1074892 : if (liftoff_unit_) {
159 : result =
160 594921 : liftoff_unit_->ExecuteCompilation(env, func_body, counters, detected);
161 593570 : if (!result.succeeded()) {
162 : // If Liftoff failed, fall back to turbofan.
163 : // TODO(wasm): We could actually stop or remove the tiering unit for this
164 : // function to avoid compiling it twice with TurboFan.
165 25486 : SwitchTier(ExecutionTier::kOptimized);
166 : DCHECK_NOT_NULL(turbofan_unit_);
167 : }
168 : }
169 1074214 : if (turbofan_unit_) {
170 : result =
171 505086 : turbofan_unit_->ExecuteCompilation(env, func_body, counters, detected);
172 : }
173 1072043 : if (interpreter_unit_) {
174 896 : result = interpreter_unit_->ExecuteCompilation(env, func_body, counters,
175 448 : detected);
176 : }
177 1072038 : result.func_index = func_index_;
178 1072038 : result.requested_tier = tier_;
179 :
180 1072038 : if (result.succeeded()) {
181 1064352 : counters->wasm_generated_code_size()->Increment(
182 1064352 : result.code_desc.instr_size);
183 2127648 : counters->wasm_reloc_size()->Increment(result.code_desc.reloc_size);
184 : }
185 :
186 1071887 : return result;
187 : }
188 :
189 1103213 : void WasmCompilationUnit::SwitchTier(ExecutionTier new_tier) {
190 : // This method is being called in the constructor, where neither
191 : // {liftoff_unit_} nor {turbofan_unit_} nor {interpreter_unit_} are set, or to
192 : // switch tier from kLiftoff to kTurbofan, in which case {liftoff_unit_} is
193 : // already set.
194 1103213 : switch (new_tier) {
195 : case ExecutionTier::kBaseline:
196 : DCHECK(!turbofan_unit_);
197 : DCHECK(!liftoff_unit_);
198 : DCHECK(!interpreter_unit_);
199 596202 : liftoff_unit_.reset(new LiftoffCompilationUnit(this));
200 : return;
201 : case ExecutionTier::kOptimized:
202 : DCHECK(!turbofan_unit_);
203 : DCHECK(!interpreter_unit_);
204 : liftoff_unit_.reset();
205 506626 : turbofan_unit_.reset(new compiler::TurbofanWasmCompilationUnit(this));
206 506543 : return;
207 : case ExecutionTier::kInterpreter:
208 : DCHECK(!turbofan_unit_);
209 : DCHECK(!liftoff_unit_);
210 : DCHECK(!interpreter_unit_);
211 452 : interpreter_unit_.reset(new compiler::InterpreterCompilationUnit(this));
212 : return;
213 : }
214 0 : UNREACHABLE();
215 : }
216 :
217 : // static
218 131489 : void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
219 : NativeModule* native_module,
220 : WasmFeatures* detected,
221 : const WasmFunction* function,
222 : ExecutionTier tier) {
223 : ModuleWireBytes wire_bytes(native_module->wire_bytes());
224 : FunctionBody function_body{function->sig, function->code.offset(),
225 : wire_bytes.start() + function->code.offset(),
226 : wire_bytes.start() + function->code.end_offset()};
227 :
228 262977 : WasmCompilationUnit unit(isolate->wasm_engine(), function->func_index, tier);
229 131489 : CompilationEnv env = native_module->CreateCompilationEnv();
230 : WasmCompilationResult result = unit.ExecuteCompilation(
231 262978 : &env, native_module->compilation_state()->GetWireBytesStorage(),
232 262978 : isolate->counters(), detected);
233 131489 : if (result.succeeded()) {
234 124360 : native_module->AddCompiledCode(std::move(result));
235 : } else {
236 7129 : native_module->compilation_state()->SetError();
237 : }
238 131488 : }
239 :
240 : } // namespace wasm
241 : } // namespace internal
242 120216 : } // namespace v8
|