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#if !V8_ENABLE_WEBASSEMBLY 6#error This header should only be included if WebAssembly is enabled. 7#endif // !V8_ENABLE_WEBASSEMBLY 8 9#ifndef V8_WASM_WASM_MODULE_BUILDER_H_ 10#define V8_WASM_WASM_MODULE_BUILDER_H_ 11 12#include "src/base/memory.h" 13#include "src/base/platform/wrappers.h" 14#include "src/base/vector.h" 15#include "src/codegen/signature.h" 16#include "src/wasm/leb-helper.h" 17#include "src/wasm/local-decl-encoder.h" 18#include "src/wasm/value-type.h" 19#include "src/wasm/wasm-module.h" 20#include "src/wasm/wasm-opcodes.h" 21#include "src/wasm/wasm-result.h" 22#include "src/zone/zone-containers.h" 23 24namespace v8 { 25namespace internal { 26namespace wasm { 27 28class ZoneBuffer : public ZoneObject { 29 public: 30 // This struct is just a type tag for Zone::NewArray<T>(size_t) call. 31 struct Buffer {}; 32 33 static constexpr size_t kInitialSize = 1024; 34 explicit ZoneBuffer(Zone* zone, size_t initial = kInitialSize) 35 : zone_(zone), buffer_(zone->NewArray<byte, Buffer>(initial)) { 36 pos_ = buffer_; 37 end_ = buffer_ + initial; 38 } 39 40 void write_u8(uint8_t x) { 41 EnsureSpace(1); 42 *(pos_++) = x; 43 } 44 45 void write_u16(uint16_t x) { 46 EnsureSpace(2); 47 base::WriteLittleEndianValue<uint16_t>(reinterpret_cast<Address>(pos_), x); 48 pos_ += 2; 49 } 50 51 void write_u32(uint32_t x) { 52 EnsureSpace(4); 53 base::WriteLittleEndianValue<uint32_t>(reinterpret_cast<Address>(pos_), x); 54 pos_ += 4; 55 } 56 57 void write_u64(uint64_t x) { 58 EnsureSpace(8); 59 base::WriteLittleEndianValue<uint64_t>(reinterpret_cast<Address>(pos_), x); 60 pos_ += 8; 61 } 62 63 void write_u32v(uint32_t val) { 64 EnsureSpace(kMaxVarInt32Size); 65 LEBHelper::write_u32v(&pos_, val); 66 } 67 68 void write_i32v(int32_t val) { 69 EnsureSpace(kMaxVarInt32Size); 70 LEBHelper::write_i32v(&pos_, val); 71 } 72 73 void write_u64v(uint64_t val) { 74 EnsureSpace(kMaxVarInt64Size); 75 LEBHelper::write_u64v(&pos_, val); 76 } 77 78 void write_i64v(int64_t val) { 79 EnsureSpace(kMaxVarInt64Size); 80 LEBHelper::write_i64v(&pos_, val); 81 } 82 83 void write_size(size_t val) { 84 EnsureSpace(kMaxVarInt32Size); 85 DCHECK_EQ(val, static_cast<uint32_t>(val)); 86 LEBHelper::write_u32v(&pos_, static_cast<uint32_t>(val)); 87 } 88 89 void write_f32(float val) { write_u32(bit_cast<uint32_t>(val)); } 90 91 void write_f64(double val) { write_u64(bit_cast<uint64_t>(val)); } 92 93 void write(const byte* data, size_t size) { 94 if (size == 0) return; 95 EnsureSpace(size); 96 memcpy(pos_, data, size); 97 pos_ += size; 98 } 99 100 void write_string(base::Vector<const char> name) { 101 write_size(name.length()); 102 write(reinterpret_cast<const byte*>(name.begin()), name.length()); 103 } 104 105 size_t reserve_u32v() { 106 size_t off = offset(); 107 EnsureSpace(kMaxVarInt32Size); 108 pos_ += kMaxVarInt32Size; 109 return off; 110 } 111 112 // Patch a (padded) u32v at the given offset to be the given value. 113 void patch_u32v(size_t offset, uint32_t val) { 114 byte* ptr = buffer_ + offset; 115 for (size_t pos = 0; pos != kPaddedVarInt32Size; ++pos) { 116 uint32_t next = val >> 7; 117 byte out = static_cast<byte>(val & 0x7f); 118 if (pos != kPaddedVarInt32Size - 1) { 119 *(ptr++) = 0x80 | out; 120 val = next; 121 } else { 122 *(ptr++) = out; 123 } 124 } 125 } 126 127 void patch_u8(size_t offset, byte val) { 128 DCHECK_GE(size(), offset); 129 buffer_[offset] = val; 130 } 131 132 size_t offset() const { return static_cast<size_t>(pos_ - buffer_); } 133 size_t size() const { return static_cast<size_t>(pos_ - buffer_); } 134 const byte* data() const { return buffer_; } 135 const byte* begin() const { return buffer_; } 136 const byte* end() const { return pos_; } 137 138 void EnsureSpace(size_t size) { 139 if ((pos_ + size) > end_) { 140 size_t new_size = size + (end_ - buffer_) * 2; 141 byte* new_buffer = zone_->NewArray<byte, Buffer>(new_size); 142 memcpy(new_buffer, buffer_, (pos_ - buffer_)); 143 pos_ = new_buffer + (pos_ - buffer_); 144 buffer_ = new_buffer; 145 end_ = new_buffer + new_size; 146 } 147 DCHECK(pos_ + size <= end_); 148 } 149 150 void Truncate(size_t size) { 151 DCHECK_GE(offset(), size); 152 pos_ = buffer_ + size; 153 } 154 155 byte** pos_ptr() { return &pos_; } 156 157 private: 158 Zone* zone_; 159 byte* buffer_; 160 byte* pos_; 161 byte* end_; 162}; 163 164class WasmModuleBuilder; 165 166class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject { 167 public: 168 // Building methods. 169 void SetSignature(const FunctionSig* sig); 170 void SetSignature(uint32_t sig_index); 171 uint32_t AddLocal(ValueType type); 172 void EmitByte(byte b); 173 void EmitI32V(int32_t val); 174 void EmitU32V(uint32_t val); 175 void EmitCode(const byte* code, uint32_t code_size); 176 void Emit(WasmOpcode opcode); 177 void EmitWithPrefix(WasmOpcode opcode); 178 void EmitGetLocal(uint32_t index); 179 void EmitSetLocal(uint32_t index); 180 void EmitTeeLocal(uint32_t index); 181 void EmitI32Const(int32_t val); 182 void EmitI64Const(int64_t val); 183 void EmitF32Const(float val); 184 void EmitF64Const(double val); 185 void EmitS128Const(Simd128 val); 186 void EmitWithU8(WasmOpcode opcode, const byte immediate); 187 void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2); 188 void EmitWithI32V(WasmOpcode opcode, int32_t immediate); 189 void EmitWithU32V(WasmOpcode opcode, uint32_t immediate); 190 void EmitValueType(ValueType type); 191 void EmitDirectCallIndex(uint32_t index); 192 void SetName(base::Vector<const char> name); 193 void AddAsmWasmOffset(size_t call_position, size_t to_number_position); 194 void SetAsmFunctionStartPosition(size_t function_position); 195 void SetCompilationHint(WasmCompilationHintStrategy strategy, 196 WasmCompilationHintTier baseline, 197 WasmCompilationHintTier top_tier); 198 199 size_t GetPosition() const { return body_.size(); } 200 void FixupByte(size_t position, byte value) { 201 body_.patch_u8(position, value); 202 } 203 void DeleteCodeAfter(size_t position); 204 205 void WriteSignature(ZoneBuffer* buffer) const; 206 void WriteBody(ZoneBuffer* buffer) const; 207 void WriteAsmWasmOffsetTable(ZoneBuffer* buffer) const; 208 209 WasmModuleBuilder* builder() const { return builder_; } 210 uint32_t func_index() { return func_index_; } 211 uint32_t sig_index() { return signature_index_; } 212 inline const FunctionSig* signature(); 213 214 private: 215 explicit WasmFunctionBuilder(WasmModuleBuilder* builder); 216 friend class WasmModuleBuilder; 217 friend Zone; 218 219 struct DirectCallIndex { 220 size_t offset; 221 uint32_t direct_index; 222 }; 223 224 WasmModuleBuilder* builder_; 225 LocalDeclEncoder locals_; 226 uint32_t signature_index_; 227 uint32_t func_index_; 228 ZoneBuffer body_; 229 base::Vector<const char> name_; 230 ZoneVector<uint32_t> i32_temps_; 231 ZoneVector<uint32_t> i64_temps_; 232 ZoneVector<uint32_t> f32_temps_; 233 ZoneVector<uint32_t> f64_temps_; 234 ZoneVector<DirectCallIndex> direct_calls_; 235 236 // Delta-encoded mapping from wasm bytes to asm.js source positions. 237 ZoneBuffer asm_offsets_; 238 uint32_t last_asm_byte_offset_ = 0; 239 uint32_t last_asm_source_position_ = 0; 240 uint32_t asm_func_start_source_position_ = 0; 241 uint8_t hint_ = kNoCompilationHint; 242}; 243 244class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { 245 public: 246 explicit WasmModuleBuilder(Zone* zone); 247 WasmModuleBuilder(const WasmModuleBuilder&) = delete; 248 WasmModuleBuilder& operator=(const WasmModuleBuilder&) = delete; 249 250 // Static representation of wasm element segment (table initializer). This is 251 // different than the version in wasm-module.h. 252 class WasmElemSegment { 253 public: 254 // asm.js gives function indices starting with the first non-imported 255 // function. 256 enum FunctionIndexingMode { 257 kRelativeToImports, 258 kRelativeToDeclaredFunctions 259 }; 260 enum Status { 261 kStatusActive, // copied automatically during instantiation. 262 kStatusPassive, // copied explicitly after instantiation. 263 kStatusDeclarative // purely declarative and never copied. 264 }; 265 struct Entry { 266 enum Kind { kGlobalGetEntry, kRefFuncEntry, kRefNullEntry } kind; 267 uint32_t index; 268 Entry(Kind kind, uint32_t index) : kind(kind), index(index) {} 269 Entry() : kind(kRefNullEntry), index(0) {} 270 }; 271 272 // Construct an active segment. 273 WasmElemSegment(Zone* zone, ValueType type, uint32_t table_index, 274 WasmInitExpr offset) 275 : type(type), 276 table_index(table_index), 277 offset(offset), 278 entries(zone), 279 status(kStatusActive) { 280 DCHECK(IsValidOffsetKind(offset.kind())); 281 } 282 283 // Construct a passive or declarative segment, which has no table 284 // index or offset. 285 WasmElemSegment(Zone* zone, ValueType type, bool declarative) 286 : type(type), 287 table_index(0), 288 entries(zone), 289 status(declarative ? kStatusDeclarative : kStatusPassive) { 290 DCHECK(IsValidOffsetKind(offset.kind())); 291 } 292 293 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmElemSegment); 294 295 ValueType type; 296 uint32_t table_index; 297 WasmInitExpr offset; 298 FunctionIndexingMode indexing_mode = kRelativeToImports; 299 ZoneVector<Entry> entries; 300 Status status; 301 302 private: 303 // This ensures no {WasmInitExpr} with subexpressions is used, which would 304 // cause a memory leak because those are stored in an std::vector. Such 305 // offset would also be mistyped. 306 bool IsValidOffsetKind(WasmInitExpr::Operator kind) { 307 return kind == WasmInitExpr::kI32Const || 308 kind == WasmInitExpr::kGlobalGet; 309 } 310 }; 311 312 // Building methods. 313 uint32_t AddImport(base::Vector<const char> name, FunctionSig* sig, 314 base::Vector<const char> module = {}); 315 WasmFunctionBuilder* AddFunction(const FunctionSig* sig = nullptr); 316 WasmFunctionBuilder* AddFunction(uint32_t sig_index); 317 uint32_t AddGlobal(ValueType type, bool mutability = true, 318 WasmInitExpr init = WasmInitExpr()); 319 uint32_t AddGlobalImport(base::Vector<const char> name, ValueType type, 320 bool mutability, 321 base::Vector<const char> module = {}); 322 void AddDataSegment(const byte* data, uint32_t size, uint32_t dest); 323 // Add an element segment to this {WasmModuleBuilder}. {segment}'s enties 324 // have to be initialized. 325 void AddElementSegment(WasmElemSegment segment); 326 // Helper method to create an active segment with one function. Assumes that 327 // table segment at {table_index} is typed as funcref. 328 void SetIndirectFunction(uint32_t table_index, uint32_t index_in_table, 329 uint32_t direct_function_index, 330 WasmElemSegment::FunctionIndexingMode indexing_mode); 331 // Increase the starting size of the table at {table_index} by {count}. Also 332 // increases the maximum table size if needed. Returns the former starting 333 // size, or the maximum uint32_t value if the maximum table size has been 334 // exceeded. 335 uint32_t IncreaseTableMinSize(uint32_t table_index, uint32_t count); 336 // Adds the signature to the module if it does not already exist. 337 uint32_t AddSignature(const FunctionSig* sig, 338 uint32_t supertype = kNoSuperType); 339 // Does not deduplicate function signatures. 340 uint32_t ForceAddSignature(const FunctionSig* sig, 341 uint32_t supertype = kNoSuperType); 342 uint32_t AddException(const FunctionSig* type); 343 uint32_t AddStructType(StructType* type, uint32_t supertype = kNoSuperType); 344 uint32_t AddArrayType(ArrayType* type, uint32_t supertype = kNoSuperType); 345 uint32_t AddTable(ValueType type, uint32_t min_size); 346 uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size); 347 uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size, 348 WasmInitExpr init); 349 void MarkStartFunction(WasmFunctionBuilder* builder); 350 void AddExport(base::Vector<const char> name, ImportExportKindCode kind, 351 uint32_t index); 352 void AddExport(base::Vector<const char> name, WasmFunctionBuilder* builder) { 353 AddExport(name, kExternalFunction, builder->func_index()); 354 } 355 uint32_t AddExportedGlobal(ValueType type, bool mutability, WasmInitExpr init, 356 base::Vector<const char> name); 357 void ExportImportedFunction(base::Vector<const char> name, int import_index); 358 void SetMinMemorySize(uint32_t value); 359 void SetMaxMemorySize(uint32_t value); 360 void SetHasSharedMemory(); 361 362 void StartRecursiveTypeGroup() { 363 DCHECK_EQ(current_recursive_group_start_, -1); 364 current_recursive_group_start_ = static_cast<int>(types_.size()); 365 } 366 367 void EndRecursiveTypeGroup() { 368 // Make sure we are in a recursive group. 369 DCHECK_NE(current_recursive_group_start_, -1); 370 // Make sure the current recursive group has at least one element. 371 DCHECK_GT(static_cast<int>(types_.size()), current_recursive_group_start_); 372 recursive_groups_.emplace( 373 current_recursive_group_start_, 374 static_cast<uint32_t>(types_.size()) - current_recursive_group_start_); 375 current_recursive_group_start_ = -1; 376 } 377 378 // Writing methods. 379 void WriteTo(ZoneBuffer* buffer) const; 380 void WriteAsmJsOffsetTable(ZoneBuffer* buffer) const; 381 382 Zone* zone() { return zone_; } 383 384 ValueType GetTableType(uint32_t index) { return tables_[index].type; } 385 386 bool IsSignature(uint32_t index) { 387 return types_[index].kind == TypeDefinition::kFunction; 388 } 389 390 const FunctionSig* GetSignature(uint32_t index) { 391 DCHECK(types_[index].kind == TypeDefinition::kFunction); 392 return types_[index].function_sig; 393 } 394 395 bool IsStructType(uint32_t index) { 396 return types_[index].kind == TypeDefinition::kStruct; 397 } 398 const StructType* GetStructType(uint32_t index) { 399 return types_[index].struct_type; 400 } 401 402 bool IsArrayType(uint32_t index) { 403 return types_[index].kind == TypeDefinition::kArray; 404 } 405 const ArrayType* GetArrayType(uint32_t index) { 406 return types_[index].array_type; 407 } 408 409 WasmFunctionBuilder* GetFunction(uint32_t index) { return functions_[index]; } 410 int NumExceptions() { return static_cast<int>(exceptions_.size()); } 411 412 int NumTypes() { return static_cast<int>(types_.size()); } 413 414 int NumTables() { return static_cast<int>(tables_.size()); } 415 416 int NumFunctions() { return static_cast<int>(functions_.size()); } 417 418 const FunctionSig* GetExceptionType(int index) { 419 return types_[exceptions_[index]].function_sig; 420 } 421 422 private: 423 struct WasmFunctionImport { 424 base::Vector<const char> module; 425 base::Vector<const char> name; 426 uint32_t sig_index; 427 }; 428 429 struct WasmGlobalImport { 430 base::Vector<const char> module; 431 base::Vector<const char> name; 432 ValueTypeCode type_code; 433 bool mutability; 434 }; 435 436 struct WasmExport { 437 base::Vector<const char> name; 438 ImportExportKindCode kind; 439 int index; // Can be negative for re-exported imports. 440 }; 441 442 struct WasmGlobal { 443 ValueType type; 444 bool mutability; 445 WasmInitExpr init; 446 }; 447 448 struct WasmTable { 449 ValueType type; 450 uint32_t min_size; 451 uint32_t max_size; 452 bool has_maximum; 453 WasmInitExpr init; 454 }; 455 456 struct WasmDataSegment { 457 ZoneVector<byte> data; 458 uint32_t dest; 459 }; 460 461 friend class WasmFunctionBuilder; 462 Zone* zone_; 463 ZoneVector<TypeDefinition> types_; 464 ZoneVector<WasmFunctionImport> function_imports_; 465 ZoneVector<WasmGlobalImport> global_imports_; 466 ZoneVector<WasmExport> exports_; 467 ZoneVector<WasmFunctionBuilder*> functions_; 468 ZoneVector<WasmTable> tables_; 469 ZoneVector<WasmDataSegment> data_segments_; 470 ZoneVector<WasmElemSegment> element_segments_; 471 ZoneVector<WasmGlobal> globals_; 472 ZoneVector<int> exceptions_; 473 ZoneUnorderedMap<FunctionSig, uint32_t> signature_map_; 474 int current_recursive_group_start_; 475 // first index -> size 476 ZoneUnorderedMap<uint32_t, uint32_t> recursive_groups_; 477 int start_function_index_; 478 uint32_t min_memory_size_; 479 uint32_t max_memory_size_; 480 bool has_max_memory_size_; 481 bool has_shared_memory_; 482#if DEBUG 483 // Once AddExportedImport is called, no more imports can be added. 484 bool adding_imports_allowed_ = true; 485#endif 486}; 487 488const FunctionSig* WasmFunctionBuilder::signature() { 489 return builder_->types_[signature_index_].function_sig; 490} 491 492} // namespace wasm 493} // namespace internal 494} // namespace v8 495 496#endif // V8_WASM_WASM_MODULE_BUILDER_H_ 497