1 // Copyright 2015 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 #ifndef V8_WASM_WASM_MODULE_BUILDER_H_
6 #define V8_WASM_WASM_MODULE_BUILDER_H_
7
8 #include "src/base/memory.h"
9 #include "src/codegen/signature.h"
10 #include "src/utils/vector.h"
11 #include "src/wasm/leb-helper.h"
12 #include "src/wasm/local-decl-encoder.h"
13 #include "src/wasm/value-type.h"
14 #include "src/wasm/wasm-module.h"
15 #include "src/wasm/wasm-opcodes.h"
16 #include "src/wasm/wasm-result.h"
17 #include "src/zone/zone-containers.h"
18
19 namespace v8 {
20 namespace internal {
21 namespace wasm {
22
23 class ZoneBuffer : public ZoneObject {
24 public:
25 // This struct is just a type tag for Zone::NewArray<T>(size_t) call.
26 struct Buffer {};
27
28 static constexpr size_t kInitialSize = 1024;
29 explicit ZoneBuffer(Zone* zone, size_t initial = kInitialSize)
zone_(zone)30 : zone_(zone), buffer_(zone->NewArray<byte, Buffer>(initial)) {
31 pos_ = buffer_;
32 end_ = buffer_ + initial;
33 }
34
write_u8(uint8_t x)35 void write_u8(uint8_t x) {
36 EnsureSpace(1);
37 *(pos_++) = x;
38 }
39
write_u16(uint16_t x)40 void write_u16(uint16_t x) {
41 EnsureSpace(2);
42 base::WriteLittleEndianValue<uint16_t>(reinterpret_cast<Address>(pos_), x);
43 pos_ += 2;
44 }
45
write_u32(uint32_t x)46 void write_u32(uint32_t x) {
47 EnsureSpace(4);
48 base::WriteLittleEndianValue<uint32_t>(reinterpret_cast<Address>(pos_), x);
49 pos_ += 4;
50 }
51
write_u64(uint64_t x)52 void write_u64(uint64_t x) {
53 EnsureSpace(8);
54 base::WriteLittleEndianValue<uint64_t>(reinterpret_cast<Address>(pos_), x);
55 pos_ += 8;
56 }
57
write_u32v(uint32_t val)58 void write_u32v(uint32_t val) {
59 EnsureSpace(kMaxVarInt32Size);
60 LEBHelper::write_u32v(&pos_, val);
61 }
62
write_i32v(int32_t val)63 void write_i32v(int32_t val) {
64 EnsureSpace(kMaxVarInt32Size);
65 LEBHelper::write_i32v(&pos_, val);
66 }
67
write_u64v(uint64_t val)68 void write_u64v(uint64_t val) {
69 EnsureSpace(kMaxVarInt64Size);
70 LEBHelper::write_u64v(&pos_, val);
71 }
72
write_i64v(int64_t val)73 void write_i64v(int64_t val) {
74 EnsureSpace(kMaxVarInt64Size);
75 LEBHelper::write_i64v(&pos_, val);
76 }
77
write_size(size_t val)78 void write_size(size_t val) {
79 EnsureSpace(kMaxVarInt32Size);
80 DCHECK_EQ(val, static_cast<uint32_t>(val));
81 LEBHelper::write_u32v(&pos_, static_cast<uint32_t>(val));
82 }
83
write_f32(float val)84 void write_f32(float val) { write_u32(bit_cast<uint32_t>(val)); }
85
write_f64(double val)86 void write_f64(double val) { write_u64(bit_cast<uint64_t>(val)); }
87
write(const byte * data,size_t size)88 void write(const byte* data, size_t size) {
89 if (size == 0) return;
90 EnsureSpace(size);
91 memcpy(pos_, data, size);
92 pos_ += size;
93 }
94
write_string(Vector<const char> name)95 void write_string(Vector<const char> name) {
96 write_size(name.length());
97 write(reinterpret_cast<const byte*>(name.begin()), name.length());
98 }
99
reserve_u32v()100 size_t reserve_u32v() {
101 size_t off = offset();
102 EnsureSpace(kMaxVarInt32Size);
103 pos_ += kMaxVarInt32Size;
104 return off;
105 }
106
107 // Patch a (padded) u32v at the given offset to be the given value.
patch_u32v(size_t offset,uint32_t val)108 void patch_u32v(size_t offset, uint32_t val) {
109 byte* ptr = buffer_ + offset;
110 for (size_t pos = 0; pos != kPaddedVarInt32Size; ++pos) {
111 uint32_t next = val >> 7;
112 byte out = static_cast<byte>(val & 0x7f);
113 if (pos != kPaddedVarInt32Size - 1) {
114 *(ptr++) = 0x80 | out;
115 val = next;
116 } else {
117 *(ptr++) = out;
118 }
119 }
120 }
121
patch_u8(size_t offset,byte val)122 void patch_u8(size_t offset, byte val) {
123 DCHECK_GE(size(), offset);
124 buffer_[offset] = val;
125 }
126
offset()127 size_t offset() const { return static_cast<size_t>(pos_ - buffer_); }
size()128 size_t size() const { return static_cast<size_t>(pos_ - buffer_); }
data()129 const byte* data() const { return buffer_; }
begin()130 const byte* begin() const { return buffer_; }
end()131 const byte* end() const { return pos_; }
132
EnsureSpace(size_t size)133 void EnsureSpace(size_t size) {
134 if ((pos_ + size) > end_) {
135 size_t new_size = size + (end_ - buffer_) * 2;
136 byte* new_buffer = zone_->NewArray<byte, Buffer>(new_size);
137 memcpy(new_buffer, buffer_, (pos_ - buffer_));
138 pos_ = new_buffer + (pos_ - buffer_);
139 buffer_ = new_buffer;
140 end_ = new_buffer + new_size;
141 }
142 DCHECK(pos_ + size <= end_);
143 }
144
Truncate(size_t size)145 void Truncate(size_t size) {
146 DCHECK_GE(offset(), size);
147 pos_ = buffer_ + size;
148 }
149
pos_ptr()150 byte** pos_ptr() { return &pos_; }
151
152 private:
153 Zone* zone_;
154 byte* buffer_;
155 byte* pos_;
156 byte* end_;
157 };
158
159 class WasmModuleBuilder;
160
161 class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
162 public:
163 // Building methods.
164 void SetSignature(FunctionSig* sig);
165 uint32_t AddLocal(ValueType type);
166 void EmitByte(byte b);
167 void EmitI32V(int32_t val);
168 void EmitU32V(uint32_t val);
169 void EmitCode(const byte* code, uint32_t code_size);
170 void Emit(WasmOpcode opcode);
171 void EmitWithPrefix(WasmOpcode opcode);
172 void EmitGetLocal(uint32_t index);
173 void EmitSetLocal(uint32_t index);
174 void EmitTeeLocal(uint32_t index);
175 void EmitI32Const(int32_t val);
176 void EmitI64Const(int64_t val);
177 void EmitF32Const(float val);
178 void EmitF64Const(double val);
179 void EmitS128Const(Simd128 val);
180 void EmitWithU8(WasmOpcode opcode, const byte immediate);
181 void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
182 void EmitWithI32V(WasmOpcode opcode, int32_t immediate);
183 void EmitWithU32V(WasmOpcode opcode, uint32_t immediate);
184 void EmitDirectCallIndex(uint32_t index);
185 void SetName(Vector<const char> name);
186 void AddAsmWasmOffset(size_t call_position, size_t to_number_position);
187 void SetAsmFunctionStartPosition(size_t function_position);
188 void SetCompilationHint(WasmCompilationHintStrategy strategy,
189 WasmCompilationHintTier baseline,
190 WasmCompilationHintTier top_tier);
191
GetPosition()192 size_t GetPosition() const { return body_.size(); }
FixupByte(size_t position,byte value)193 void FixupByte(size_t position, byte value) {
194 body_.patch_u8(position, value);
195 }
196 void DeleteCodeAfter(size_t position);
197
198 void WriteSignature(ZoneBuffer* buffer) const;
199 void WriteBody(ZoneBuffer* buffer) const;
200 void WriteAsmWasmOffsetTable(ZoneBuffer* buffer) const;
201
builder()202 WasmModuleBuilder* builder() const { return builder_; }
func_index()203 uint32_t func_index() { return func_index_; }
204 FunctionSig* signature();
205
206 private:
207 explicit WasmFunctionBuilder(WasmModuleBuilder* builder);
208 friend class WasmModuleBuilder;
209 friend Zone;
210
211 struct DirectCallIndex {
212 size_t offset;
213 uint32_t direct_index;
214 };
215
216 WasmModuleBuilder* builder_;
217 LocalDeclEncoder locals_;
218 uint32_t signature_index_;
219 uint32_t func_index_;
220 ZoneBuffer body_;
221 Vector<const char> name_;
222 ZoneVector<uint32_t> i32_temps_;
223 ZoneVector<uint32_t> i64_temps_;
224 ZoneVector<uint32_t> f32_temps_;
225 ZoneVector<uint32_t> f64_temps_;
226 ZoneVector<DirectCallIndex> direct_calls_;
227
228 // Delta-encoded mapping from wasm bytes to asm.js source positions.
229 ZoneBuffer asm_offsets_;
230 uint32_t last_asm_byte_offset_ = 0;
231 uint32_t last_asm_source_position_ = 0;
232 uint32_t asm_func_start_source_position_ = 0;
233 uint8_t hint_ = kNoCompilationHint;
234 };
235
236 class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
237 public:
238 explicit WasmModuleBuilder(Zone* zone);
239 WasmModuleBuilder(const WasmModuleBuilder&) = delete;
240 WasmModuleBuilder& operator=(const WasmModuleBuilder&) = delete;
241
242 // Building methods.
243 uint32_t AddImport(Vector<const char> name, FunctionSig* sig,
244 Vector<const char> module = {});
245 WasmFunctionBuilder* AddFunction(FunctionSig* sig = nullptr);
246 uint32_t AddGlobal(ValueType type, bool mutability = true,
247 WasmInitExpr init = WasmInitExpr());
248 uint32_t AddGlobalImport(Vector<const char> name, ValueType type,
249 bool mutability, Vector<const char> module = {});
250 void AddDataSegment(const byte* data, uint32_t size, uint32_t dest);
251 uint32_t AddSignature(FunctionSig* sig);
252 uint32_t AddStructType(StructType* type);
253 uint32_t AddArrayType(ArrayType* type);
254 // In the current implementation, it's supported to have uninitialized slots
255 // at the beginning and/or end of the indirect function table, as long as
256 // the filled slots form a contiguous block in the middle.
257 uint32_t AllocateIndirectFunctions(uint32_t count);
258 void SetIndirectFunction(uint32_t indirect, uint32_t direct);
259 void SetMaxTableSize(uint32_t max);
260 uint32_t AddTable(ValueType type, uint32_t min_size);
261 uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size);
262 void MarkStartFunction(WasmFunctionBuilder* builder);
263 void AddExport(Vector<const char> name, ImportExportKindCode kind,
264 uint32_t index);
AddExport(Vector<const char> name,WasmFunctionBuilder * builder)265 void AddExport(Vector<const char> name, WasmFunctionBuilder* builder) {
266 AddExport(name, kExternalFunction, builder->func_index());
267 }
268 uint32_t AddExportedGlobal(ValueType type, bool mutability, WasmInitExpr init,
269 Vector<const char> name);
270 void ExportImportedFunction(Vector<const char> name, int import_index);
271 void SetMinMemorySize(uint32_t value);
272 void SetMaxMemorySize(uint32_t value);
273 void SetHasSharedMemory();
274
275 // Writing methods.
276 void WriteTo(ZoneBuffer* buffer) const;
277 void WriteAsmJsOffsetTable(ZoneBuffer* buffer) const;
278
zone()279 Zone* zone() { return zone_; }
280
GetSignature(uint32_t index)281 FunctionSig* GetSignature(uint32_t index) {
282 DCHECK(types_[index].kind == Type::kFunctionSig);
283 return types_[index].sig;
284 }
285
286 private:
287 struct Type {
288 enum Kind { kFunctionSig, kStructType, kArrayType };
TypeType289 explicit Type(FunctionSig* signature)
290 : kind(kFunctionSig), sig(signature) {}
TypeType291 explicit Type(StructType* struct_type)
292 : kind(kStructType), struct_type(struct_type) {}
TypeType293 explicit Type(ArrayType* array_type)
294 : kind(kArrayType), array_type(array_type) {}
295 Kind kind;
296 union {
297 FunctionSig* sig;
298 StructType* struct_type;
299 ArrayType* array_type;
300 };
301 };
302
303 struct WasmFunctionImport {
304 Vector<const char> module;
305 Vector<const char> name;
306 uint32_t sig_index;
307 };
308
309 struct WasmGlobalImport {
310 Vector<const char> module;
311 Vector<const char> name;
312 ValueTypeCode type_code;
313 bool mutability;
314 };
315
316 struct WasmExport {
317 Vector<const char> name;
318 ImportExportKindCode kind;
319 int index; // Can be negative for re-exported imports.
320 };
321
322 struct WasmGlobal {
323 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmGlobal);
324
325 ValueType type;
326 bool mutability;
327 WasmInitExpr init;
328 };
329
330 struct WasmTable {
331 ValueType type;
332 uint32_t min_size;
333 uint32_t max_size;
334 bool has_maximum;
335 };
336
337 struct WasmDataSegment {
338 ZoneVector<byte> data;
339 uint32_t dest;
340 };
341
342 friend class WasmFunctionBuilder;
343 Zone* zone_;
344 ZoneVector<Type> types_;
345 ZoneVector<WasmFunctionImport> function_imports_;
346 ZoneVector<WasmGlobalImport> global_imports_;
347 ZoneVector<WasmExport> exports_;
348 ZoneVector<WasmFunctionBuilder*> functions_;
349 ZoneVector<WasmTable> tables_;
350 ZoneVector<WasmDataSegment> data_segments_;
351 ZoneVector<uint32_t> indirect_functions_;
352 ZoneVector<WasmGlobal> globals_;
353 ZoneUnorderedMap<FunctionSig, uint32_t> signature_map_;
354 int start_function_index_;
355 uint32_t max_table_size_ = 0;
356 uint32_t min_memory_size_;
357 uint32_t max_memory_size_;
358 bool has_max_memory_size_;
359 bool has_shared_memory_;
360 #if DEBUG
361 // Once AddExportedImport is called, no more imports can be added.
362 bool adding_imports_allowed_ = true;
363 // Indirect functions must be allocated before adding extra tables.
364 bool allocating_indirect_functions_allowed_ = true;
365 #endif
366 };
367
signature()368 inline FunctionSig* WasmFunctionBuilder::signature() {
369 return builder_->types_[signature_index_].sig;
370 }
371
372 } // namespace wasm
373 } // namespace internal
374 } // namespace v8
375
376 #endif // V8_WASM_WASM_MODULE_BUILDER_H_
377