%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/wasm/wasm-module.h |
// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if !V8_ENABLE_WEBASSEMBLY #error This header should only be included if WebAssembly is enabled. #endif // !V8_ENABLE_WEBASSEMBLY #ifndef V8_WASM_WASM_MODULE_H_ #define V8_WASM_WASM_MODULE_H_ #include <map> #include <memory> #include "src/base/optional.h" #include "src/base/platform/mutex.h" #include "src/base/vector.h" #include "src/codegen/signature.h" #include "src/common/globals.h" #include "src/handles/handles.h" #include "src/trap-handler/trap-handler.h" #include "src/wasm/branch-hint-map.h" #include "src/wasm/constant-expression.h" #include "src/wasm/struct-types.h" #include "src/wasm/wasm-constants.h" #include "src/wasm/wasm-init-expr.h" #include "src/wasm/wasm-limits.h" #include "src/wasm/well-known-imports.h" namespace v8::internal { class WasmModuleObject; } namespace v8::internal::wasm { using WasmName = base::Vector<const char>; struct AsmJsOffsets; class ErrorThrower; class WellKnownImportsList; // Reference to a string in the wire bytes. class WireBytesRef { public: constexpr WireBytesRef() = default; constexpr WireBytesRef(uint32_t offset, uint32_t length) : offset_(offset), length_(length) { DCHECK_IMPLIES(offset_ == 0, length_ == 0); DCHECK_LE(offset_, offset_ + length_); // no uint32_t overflow. } uint32_t offset() const { return offset_; } uint32_t length() const { return length_; } uint32_t end_offset() const { return offset_ + length_; } bool is_empty() const { return length_ == 0; } bool is_set() const { return offset_ != 0; } private: uint32_t offset_ = 0; uint32_t length_ = 0; }; // Static representation of a wasm function. struct WasmFunction { const FunctionSig* sig = nullptr; // signature of the function. uint32_t func_index = 0; // index into the function table. uint32_t sig_index = 0; // index into the signature table. WireBytesRef code = {}; // code of this function. bool imported = false; bool exported = false; bool declared = false; }; // Static representation of a wasm global variable. struct WasmGlobal { ValueType type; // type of the global. bool mutability; // {true} if mutable. ConstantExpression init; // the initialization expression of the global. union { // Index of imported mutable global. uint32_t index; // Offset into global memory (if not imported & mutable). Expressed in bytes // for value-typed globals, and in tagged words for reference-typed globals. uint32_t offset; }; bool imported; // true if imported. bool exported; // true if exported. }; // Note: An exception tag signature only uses the params portion of a function // signature. using WasmTagSig = FunctionSig; // Static representation of a wasm tag type. struct WasmTag { explicit WasmTag(const WasmTagSig* sig, uint32_t sig_index) : sig(sig), sig_index(sig_index) {} const FunctionSig* ToFunctionSig() const { return sig; } const WasmTagSig* sig; // type signature of the tag. uint32_t sig_index; }; enum ModuleOrigin : uint8_t { kWasmOrigin, kAsmJsSloppyOrigin, kAsmJsStrictOrigin }; enum BoundsCheckStrategy : int8_t { // Emit protected instructions, use the trap handler for OOB detection. kTrapHandler, // Emit explicit bounds checks. kExplicitBoundsChecks, // Emit no bounds checks at all (for testing only). kNoBoundsChecks }; // Static representation of a wasm memory. struct WasmMemory { uint32_t index = 0; // index into the memory table uint32_t initial_pages = 0; // initial size of the memory in 64k pages uint32_t maximum_pages = 0; // maximum size of the memory in 64k pages bool is_shared = false; // true if memory is a SharedArrayBuffer bool has_maximum_pages = false; // true if there is a maximum memory size bool is_memory64 = false; // true if the memory is 64 bit bool imported = false; // true if the memory is imported bool exported = false; // true if the memory is exported // Computed information, cached here for faster compilation. // Updated via {UpdateComputedInformation}. uintptr_t min_memory_size = 0; // smallest size of any memory in bytes uintptr_t max_memory_size = 0; // largest size of any memory in bytes BoundsCheckStrategy bounds_checks = kExplicitBoundsChecks; }; inline void UpdateComputedInformation(WasmMemory* memory, ModuleOrigin origin) { const uintptr_t platform_max_pages = memory->is_memory64 ? kV8MaxWasmMemory64Pages : kV8MaxWasmMemory32Pages; memory->min_memory_size = std::min(platform_max_pages, uintptr_t{memory->initial_pages}) * kWasmPageSize; memory->max_memory_size = std::min(platform_max_pages, uintptr_t{memory->maximum_pages}) * kWasmPageSize; if (!v8_flags.wasm_bounds_checks) { memory->bounds_checks = kNoBoundsChecks; } else if (v8_flags.wasm_enforce_bounds_checks) { // Explicit bounds checks requested via flag (for testing). memory->bounds_checks = kExplicitBoundsChecks; } else if (origin != kWasmOrigin) { // Asm.js modules can't use trap handling. memory->bounds_checks = kExplicitBoundsChecks; } else if (memory->is_memory64) { // Memory64 currently always requires explicit bounds checks. memory->bounds_checks = kExplicitBoundsChecks; } else if (trap_handler::IsTrapHandlerEnabled()) { memory->bounds_checks = kTrapHandler; } else { // If the trap handler is not enabled, fall back to explicit bounds checks. memory->bounds_checks = kExplicitBoundsChecks; } } // Static representation of a wasm literal stringref. struct WasmStringRefLiteral { explicit WasmStringRefLiteral(const WireBytesRef& source) : source(source) {} WireBytesRef source; // start offset in the module bytes. }; // Static representation of a wasm data segment. struct WasmDataSegment { explicit WasmDataSegment(bool is_active, uint32_t memory_index, ConstantExpression dest_addr, WireBytesRef source) : active(is_active), memory_index(memory_index), dest_addr(dest_addr), source(source) {} static WasmDataSegment PassiveForTesting() { return WasmDataSegment{false, 0, {}, {}}; } bool active = true; // true if copied automatically during instantiation. uint32_t memory_index; // memory index (if active). ConstantExpression dest_addr; // destination memory address (if active). WireBytesRef source; // start offset in the module bytes. }; // Static representation of wasm element segment (table initializer). struct WasmElemSegment { enum Status { kStatusActive, // copied automatically during instantiation. kStatusPassive, // copied explicitly after instantiation. kStatusDeclarative // purely declarative and never copied. }; enum ElementType { kFunctionIndexElements, kExpressionElements }; // Construct an active segment. WasmElemSegment(ValueType type, uint32_t table_index, ConstantExpression offset, ElementType element_type, uint32_t element_count, uint32_t elements_wire_bytes_offset) : status(kStatusActive), type(type), table_index(table_index), offset(std::move(offset)), element_type(element_type), element_count(element_count), elements_wire_bytes_offset(elements_wire_bytes_offset) {} // Construct a passive or declarative segment, which has no table index or // offset. WasmElemSegment(ValueType type, Status status, ElementType element_type, uint32_t element_count, uint32_t elements_wire_bytes_offset) : status(status), type(type), table_index(0), element_type(element_type), element_count(element_count), elements_wire_bytes_offset(elements_wire_bytes_offset) { DCHECK_NE(status, kStatusActive); } // Default constructor. Constucts an invalid segment. WasmElemSegment() : status(kStatusActive), type(kWasmBottom), table_index(0), element_type(kFunctionIndexElements), element_count(0), elements_wire_bytes_offset(0) {} WasmElemSegment(const WasmElemSegment&) = delete; WasmElemSegment(WasmElemSegment&&) V8_NOEXCEPT = default; WasmElemSegment& operator=(const WasmElemSegment&) = delete; WasmElemSegment& operator=(WasmElemSegment&&) V8_NOEXCEPT = default; Status status; ValueType type; uint32_t table_index; ConstantExpression offset; ElementType element_type; uint32_t element_count; uint32_t elements_wire_bytes_offset; }; // Static representation of a wasm import. struct WasmImport { WireBytesRef module_name; // module name. WireBytesRef field_name; // import name. ImportExportKindCode kind; // kind of the import. uint32_t index; // index into the respective space. }; // Static representation of a wasm export. struct WasmExport { WireBytesRef name; // exported name. ImportExportKindCode kind; // kind of the export. uint32_t index; // index into the respective space. }; enum class WasmCompilationHintStrategy : uint8_t { kDefault = 0, kLazy = 1, kEager = 2, kLazyBaselineEagerTopTier = 3, }; enum class WasmCompilationHintTier : uint8_t { kDefault = 0, kBaseline = 1, kOptimized = 2, }; // Static representation of a wasm compilation hint struct WasmCompilationHint { WasmCompilationHintStrategy strategy; WasmCompilationHintTier baseline_tier; WasmCompilationHintTier top_tier; }; #define SELECT_WASM_COUNTER(counters, origin, prefix, suffix) \ ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \ : (counters)->prefix##_asm_##suffix()) // Uses a map as backing storage when sparsely, or a vector when densely // populated. Requires {Value} to implement `bool is_set()` to identify // uninitialized objects. template <class Value> class AdaptiveMap { public: // The technical limitation here is that index+1 must not overflow. Since // we have significantly lower maximums on anything that can be named, // we can have a tighter limit here to reject useless entries early. static constexpr uint32_t kMaxKey = 10'000'000; static_assert(kMaxKey < std::numeric_limits<uint32_t>::max()); AdaptiveMap() : map_(new MapType()) {} explicit AdaptiveMap(const AdaptiveMap&) = delete; AdaptiveMap& operator=(const AdaptiveMap&) = delete; AdaptiveMap(AdaptiveMap&& other) V8_NOEXCEPT { *this = std::move(other); } AdaptiveMap& operator=(AdaptiveMap&& other) V8_NOEXCEPT { mode_ = other.mode_; vector_.swap(other.vector_); map_.swap(other.map_); return *this; } void FinishInitialization(); bool is_set() const { return mode_ != kInitializing; } void Put(uint32_t key, const Value& value) { DCHECK(mode_ == kInitializing); DCHECK_LE(key, kMaxKey); map_->insert(std::make_pair(key, value)); } void Put(uint32_t key, Value&& value) { DCHECK(mode_ == kInitializing); DCHECK_LE(key, kMaxKey); map_->insert(std::make_pair(key, std::move(value))); } const Value* Get(uint32_t key) const { if (mode_ == kDense) { if (key >= vector_.size()) return nullptr; if (!vector_[key].is_set()) return nullptr; return &vector_[key]; } else { DCHECK(mode_ == kSparse || mode_ == kInitializing); auto it = map_->find(key); if (it == map_->end()) return nullptr; return &it->second; } } bool Has(uint32_t key) const { if (mode_ == kDense) { return key < vector_.size() && vector_[key].is_set(); } else { DCHECK(mode_ == kSparse || mode_ == kInitializing); return map_->find(key) != map_->end(); } } size_t EstimateCurrentMemoryConsumption() const; private: static constexpr uint32_t kLoadFactor = 4; using MapType = std::map<uint32_t, Value>; enum Mode { kDense, kSparse, kInitializing }; Mode mode_{kInitializing}; std::vector<Value> vector_; std::unique_ptr<MapType> map_; }; using NameMap = AdaptiveMap<WireBytesRef>; using IndirectNameMap = AdaptiveMap<AdaptiveMap<WireBytesRef>>; struct ModuleWireBytes; class V8_EXPORT_PRIVATE LazilyGeneratedNames { public: WireBytesRef LookupFunctionName(ModuleWireBytes wire_bytes, uint32_t function_index); void AddForTesting(int function_index, WireBytesRef name); bool Has(uint32_t function_index); size_t EstimateCurrentMemoryConsumption() const; private: // Lazy loading must guard against concurrent modifications from multiple // {WasmModuleObject}s. mutable base::Mutex mutex_; bool has_functions_{false}; NameMap function_names_; }; class V8_EXPORT_PRIVATE AsmJsOffsetInformation { public: explicit AsmJsOffsetInformation(base::Vector<const uint8_t> encoded_offsets); // Destructor defined in wasm-module.cc, where the definition of // {AsmJsOffsets} is available. ~AsmJsOffsetInformation(); int GetSourcePosition(int func_index, int byte_offset, bool is_at_number_conversion); std::pair<int, int> GetFunctionOffsets(int func_index); private: void EnsureDecodedOffsets(); // The offset information table is decoded lazily, hence needs to be // protected against concurrent accesses. // Exactly one of the two fields below will be set at a time. mutable base::Mutex mutex_; // Holds the encoded offset table bytes. base::OwnedVector<const uint8_t> encoded_offsets_; // Holds the decoded offset table. std::unique_ptr<AsmJsOffsets> decoded_offsets_; }; // Used as the supertype for a type at the top of the type hierarchy. constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::max(); struct TypeDefinition { enum Kind : int8_t { kFunction, kStruct, kArray }; TypeDefinition(const FunctionSig* sig, uint32_t supertype, bool is_final) : function_sig(sig), supertype(supertype), kind(kFunction), is_final(is_final) {} TypeDefinition(const StructType* type, uint32_t supertype, bool is_final) : struct_type(type), supertype(supertype), kind(kStruct), is_final(is_final) {} TypeDefinition(const ArrayType* type, uint32_t supertype, bool is_final) : array_type(type), supertype(supertype), kind(kArray), is_final(is_final) {} TypeDefinition() : function_sig(nullptr), supertype(kNoSuperType), kind(kFunction), is_final(false) {} bool operator==(const TypeDefinition& other) const { if (supertype != other.supertype || kind != other.kind || is_final != other.is_final) { return false; } switch (kind) { case kFunction: return *function_sig == *other.function_sig; case kStruct: return *struct_type == *other.struct_type; case kArray: return *array_type == *other.array_type; } } bool operator!=(const TypeDefinition& other) const { return !(*this == other); } union { const FunctionSig* function_sig; const StructType* struct_type; const ArrayType* array_type; }; uint32_t supertype; Kind kind; bool is_final; uint8_t subtyping_depth = 0; }; struct V8_EXPORT_PRIVATE WasmDebugSymbols { enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF }; Type type = Type::None; WireBytesRef external_url; }; class CallSiteFeedback { public: struct PolymorphicCase { int function_index; int absolute_call_frequency; }; // Regular constructor: uninitialized/unknown, monomorphic, or polymorphic. CallSiteFeedback() : index_or_count_(-1), frequency_or_ool_(0) {} CallSiteFeedback(int function_index, int call_count) : index_or_count_(function_index), frequency_or_ool_(call_count) {} CallSiteFeedback(PolymorphicCase* polymorphic_cases, int num_cases) : index_or_count_(-num_cases), frequency_or_ool_(reinterpret_cast<intptr_t>(polymorphic_cases)) {} // Copying and assignment: prefer moving, as it's cheaper. // The code below makes sure external polymorphic storage is copied and/or // freed as appropriate. CallSiteFeedback(const CallSiteFeedback& other) V8_NOEXCEPT { *this = other; } CallSiteFeedback(CallSiteFeedback&& other) V8_NOEXCEPT { *this = other; } CallSiteFeedback& operator=(const CallSiteFeedback& other) V8_NOEXCEPT { index_or_count_ = other.index_or_count_; if (other.is_polymorphic()) { int num_cases = other.num_cases(); PolymorphicCase* polymorphic = new PolymorphicCase[num_cases]; for (int i = 0; i < num_cases; i++) { polymorphic[i].function_index = other.function_index(i); polymorphic[i].absolute_call_frequency = other.call_count(i); } frequency_or_ool_ = reinterpret_cast<intptr_t>(polymorphic); } else { frequency_or_ool_ = other.frequency_or_ool_; } return *this; } CallSiteFeedback& operator=(CallSiteFeedback&& other) V8_NOEXCEPT { if (this != &other) { index_or_count_ = other.index_or_count_; frequency_or_ool_ = other.frequency_or_ool_; other.frequency_or_ool_ = 0; } return *this; } ~CallSiteFeedback() { if (is_polymorphic()) delete[] polymorphic_storage(); } int num_cases() const { if (is_monomorphic()) return 1; if (is_invalid()) return 0; return -index_or_count_; } int function_index(int i) const { DCHECK(!is_invalid()); if (is_monomorphic()) return index_or_count_; return polymorphic_storage()[i].function_index; } int call_count(int i) const { if (index_or_count_ >= 0) return static_cast<int>(frequency_or_ool_); return polymorphic_storage()[i].absolute_call_frequency; } private: bool is_monomorphic() const { return index_or_count_ >= 0; } bool is_polymorphic() const { return index_or_count_ <= -2; } bool is_invalid() const { return index_or_count_ == -1; } const PolymorphicCase* polymorphic_storage() const { DCHECK(is_polymorphic()); return reinterpret_cast<PolymorphicCase*>(frequency_or_ool_); } int index_or_count_; intptr_t frequency_or_ool_; }; struct FunctionTypeFeedback { // {feedback_vector} is computed from {call_targets} and the instance-specific // feedback vector by {TransitiveTypeFeedbackProcessor}. std::vector<CallSiteFeedback> feedback_vector; // {call_targets} has one entry per "call" and "call_ref" in the function. // For "call", it holds the index of the called function, for "call_ref" the // value will be {kNonDirectCall}. base::OwnedVector<uint32_t> call_targets; // {tierup_priority} is updated and used when triggering tier-up. // TODO(clemensb): This does not belong here; find a better place. int tierup_priority = 0; static constexpr uint32_t kNonDirectCall = 0xFFFFFFFF; }; struct TypeFeedbackStorage { std::unordered_map<uint32_t, FunctionTypeFeedback> feedback_for_function; // Accesses to {feedback_for_function} are guarded by this mutex. // Multiple reads are allowed (shared lock), but only exclusive writes. // Currently known users of the mutex are: // - LiftoffCompiler: writes {call_targets}. // - TransitiveTypeFeedbackProcessor: reads {call_targets}, // writes {feedback_vector}, reads {feedback_vector.size()}. // - TriggerTierUp: increments {tierup_priority}. // - WasmGraphBuilder: reads {feedback_vector}. // - Feedback vector allocation: reads {call_targets.size()}. // - PGO ProfileGenerator: reads everything. // - PGO deserializer: writes everything, currently not locked, relies on // being called before multi-threading enters the picture. mutable base::SharedMutex mutex; WellKnownImportsList well_known_imports; size_t EstimateCurrentMemoryConsumption() const; }; struct WasmTable; // Static representation of a module. struct V8_EXPORT_PRIVATE WasmModule { // ================ Fields =================================================== Zone signature_zone; int start_function_index = -1; // start function, >= 0 if any // Size of the buffer required for all globals that are not imported and // mutable. uint32_t untagged_globals_buffer_size = 0; uint32_t tagged_globals_buffer_size = 0; uint32_t num_imported_globals = 0; uint32_t num_imported_mutable_globals = 0; uint32_t num_imported_functions = 0; uint32_t num_imported_tables = 0; uint32_t num_imported_tags = 0; uint32_t num_declared_functions = 0; // excluding imported uint32_t num_exported_functions = 0; uint32_t num_declared_data_segments = 0; // From the DataCount section. // Position and size of the code section (payload only, i.e. without section // ID and length). WireBytesRef code = {0, 0}; WireBytesRef name = {0, 0}; // Position and size of the name section (payload only, i.e. without section // ID and length). WireBytesRef name_section = {0, 0}; // Set to true if this module has wasm-gc types in its type section. bool is_wasm_gc = false; std::vector<TypeDefinition> types; // by type index // Maps each type index to its global (cross-module) canonical index as per // isorecursive type canonicalization. std::vector<uint32_t> isorecursive_canonical_type_ids; // First index -> size. Used for fuzzing only. std::unordered_map<uint32_t, uint32_t> explicit_recursive_type_groups; std::vector<WasmFunction> functions; std::vector<WasmGlobal> globals; std::vector<WasmDataSegment> data_segments; std::vector<WasmTable> tables; std::vector<WasmMemory> memories; std::vector<WasmImport> import_table; std::vector<WasmExport> export_table; std::vector<WasmTag> tags; std::vector<WasmStringRefLiteral> stringref_literals; std::vector<WasmElemSegment> elem_segments; std::vector<WasmCompilationHint> compilation_hints; BranchHintInfo branch_hints; // Pairs of module offsets and mark id. std::vector<std::pair<uint32_t, uint32_t>> inst_traces; // This is the only member of {WasmModule} where we store dynamic information // that's not a decoded representation of the wire bytes. // TODO(jkummerow): Rename. mutable TypeFeedbackStorage type_feedback; const ModuleOrigin origin; mutable LazilyGeneratedNames lazily_generated_names; WasmDebugSymbols debug_symbols; // Asm.js source position information. Only available for modules compiled // from asm.js. std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information; // {validated_functions} is atomically updated when functions get validated // (during compilation, streaming decoding, or via explicit validation). static_assert(sizeof(std::atomic<uint8_t>) == 1); static_assert(alignof(std::atomic<uint8_t>) == 1); mutable std::unique_ptr<std::atomic<uint8_t>[]> validated_functions; // ================ Constructors ============================================= explicit WasmModule(ModuleOrigin = kWasmOrigin); WasmModule(const WasmModule&) = delete; WasmModule& operator=(const WasmModule&) = delete; // ================ Accessors ================================================ void add_type(TypeDefinition type) { types.push_back(type); if (type.supertype != kNoSuperType) { // Set the subtyping depth. Outside of unit tests this is done by the // module decoder. DCHECK_GT(types.size(), 0); DCHECK_LT(type.supertype, types.size() - 1); types.back().subtyping_depth = types[type.supertype].subtyping_depth + 1; } // Isorecursive canonical type will be computed later. isorecursive_canonical_type_ids.push_back(kNoSuperType); } bool has_type(uint32_t index) const { return index < types.size(); } void add_signature(const FunctionSig* sig, uint32_t supertype, bool is_final) { DCHECK_NOT_NULL(sig); add_type(TypeDefinition(sig, supertype, is_final)); } bool has_signature(uint32_t index) const { return index < types.size() && types[index].kind == TypeDefinition::kFunction; } const FunctionSig* signature(uint32_t index) const { DCHECK(has_signature(index)); size_t num_types = types.size(); V8_ASSUME(index < num_types); return types[index].function_sig; } void add_struct_type(const StructType* type, uint32_t supertype, bool is_final) { DCHECK_NOT_NULL(type); add_type(TypeDefinition(type, supertype, is_final)); } bool has_struct(uint32_t index) const { return index < types.size() && types[index].kind == TypeDefinition::kStruct; } const StructType* struct_type(uint32_t index) const { DCHECK(has_struct(index)); size_t num_types = types.size(); V8_ASSUME(index < num_types); return types[index].struct_type; } void add_array_type(const ArrayType* type, uint32_t supertype, bool is_final) { DCHECK_NOT_NULL(type); add_type(TypeDefinition(type, supertype, is_final)); } bool has_array(uint32_t index) const { return index < types.size() && types[index].kind == TypeDefinition::kArray; } const ArrayType* array_type(uint32_t index) const { DCHECK(has_array(index)); size_t num_types = types.size(); V8_ASSUME(index < num_types); return types[index].array_type; } uint32_t supertype(uint32_t index) const { size_t num_types = types.size(); V8_ASSUME(index < num_types); return types[index].supertype; } bool has_supertype(uint32_t index) const { return supertype(index) != kNoSuperType; } // Linear search. Returns -1 if types are empty. int MaxCanonicalTypeIndex() const { if (isorecursive_canonical_type_ids.empty()) return -1; return *std::max_element(isorecursive_canonical_type_ids.begin(), isorecursive_canonical_type_ids.end()); } bool function_was_validated(int func_index) const { DCHECK_NOT_NULL(validated_functions); static_assert(sizeof(validated_functions[0]) == 1); DCHECK_LE(num_imported_functions, func_index); int pos = func_index - num_imported_functions; DCHECK_LE(pos, num_declared_functions); uint8_t byte = validated_functions[pos >> 3].load(std::memory_order_relaxed); DCHECK_IMPLIES(origin != kWasmOrigin, byte == 0xff); return byte & (1 << (pos & 7)); } void set_function_validated(int func_index) const { DCHECK_EQ(kWasmOrigin, origin); DCHECK_NOT_NULL(validated_functions); DCHECK_LE(num_imported_functions, func_index); int pos = func_index - num_imported_functions; DCHECK_LE(pos, num_declared_functions); std::atomic<uint8_t>* atomic_byte = &validated_functions[pos >> 3]; uint8_t old_byte = atomic_byte->load(std::memory_order_relaxed); uint8_t new_bit = 1 << (pos & 7); while ((old_byte & new_bit) == 0 && !atomic_byte->compare_exchange_weak(old_byte, old_byte | new_bit, std::memory_order_relaxed)) { // Retry with updated {old_byte}. } } void set_all_functions_validated() const { DCHECK_EQ(kWasmOrigin, origin); if (num_declared_functions == 0) return; DCHECK_NOT_NULL(validated_functions); size_t num_words = (num_declared_functions + 7) / 8; for (size_t i = 0; i < num_words; ++i) { validated_functions[i].store(0xff, std::memory_order_relaxed); } } base::Vector<const WasmFunction> declared_functions() const { return base::VectorOf(functions) + num_imported_functions; } size_t EstimateStoredSize() const; // No tracing. size_t EstimateCurrentMemoryConsumption() const; // With tracing. }; // Static representation of a wasm indirect call table. struct WasmTable { MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable); ValueType type = kWasmVoid; // table type. uint32_t initial_size = 0; // initial table size. uint32_t maximum_size = 0; // maximum table size. bool has_maximum_size = false; // true if there is a maximum size. bool imported = false; // true if imported. bool exported = false; // true if exported. ConstantExpression initial_value; }; inline bool is_asmjs_module(const WasmModule* module) { return module->origin != kWasmOrigin; } // Returns the wrapper index for a function with isorecursive canonical // signature index {canonical_sig_index}, and origin defined by {is_import}. int GetExportWrapperIndex(uint32_t canonical_sig_index, bool is_import); // Return the byte offset of the function identified by the given index. // The offset will be relative to the start of the module bytes. // Returns -1 if the function index is invalid. int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index); // Returns the function containing the given byte offset. // Returns -1 if the byte offset is not contained in any // function of this module. int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset); // Returns the function containing the given byte offset. // Will return preceding function if the byte offset is not // contained within a function. int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset); // Gets the explicitly defined subtyping depth for the given type. // Returns 0 if the type has no explicit supertype. // The result is capped to {kV8MaxRttSubtypingDepth + 1}. // Invalid cyclic hierarchies will return -1. V8_EXPORT_PRIVATE int GetSubtypingDepth(const WasmModule* module, uint32_t type_index); // Interface to the storage (wire bytes) of a wasm module. // It is illegal for anyone receiving a ModuleWireBytes to store pointers based // on module_bytes, as this storage is only guaranteed to be alive as long as // this struct is alive. // As {ModuleWireBytes} is just a wrapper around a {base::Vector<const // uint8_t>}, it should generally be passed by value. struct V8_EXPORT_PRIVATE ModuleWireBytes { explicit ModuleWireBytes(base::Vector<const uint8_t> module_bytes) : module_bytes_(module_bytes) {} ModuleWireBytes(const uint8_t* start, const uint8_t* end) : module_bytes_(start, static_cast<int>(end - start)) { DCHECK_GE(kMaxInt, end - start); } // Get a string stored in the module bytes representing a name. WasmName GetNameOrNull(WireBytesRef ref) const; // Get a string stored in the module bytes representing a function name. WasmName GetNameOrNull(int func_index, const WasmModule* module) const; // Checks the given reference is contained within the module bytes. bool BoundsCheck(WireBytesRef ref) const { uint32_t size = static_cast<uint32_t>(module_bytes_.length()); return ref.offset() <= size && ref.length() <= size - ref.offset(); } base::Vector<const uint8_t> GetFunctionBytes( const WasmFunction* function) const { return module_bytes_.SubVector(function->code.offset(), function->code.end_offset()); } base::Vector<const uint8_t> module_bytes() const { return module_bytes_; } const uint8_t* start() const { return module_bytes_.begin(); } const uint8_t* end() const { return module_bytes_.end(); } size_t length() const { return module_bytes_.length(); } private: base::Vector<const uint8_t> module_bytes_; }; ASSERT_TRIVIALLY_COPYABLE(ModuleWireBytes); // A helper for printing out the names of functions. struct WasmFunctionName { WasmFunctionName(int func_index, WasmName name) : func_index_(func_index), name_(name) {} const int func_index_; const WasmName name_; }; V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate, Handle<NativeContext> context); V8_EXPORT_PRIVATE Handle<String> ErrorStringForCodegen(Isolate* isolate, Handle<Context> context); Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig, bool for_exception = false); Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable, ValueType type); Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size, base::Optional<uint32_t> max_size, bool shared, bool is_memory64); Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type, uint32_t min_size, base::Optional<uint32_t> max_size); Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module); Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module); Handle<JSArray> GetCustomSections(Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name, ErrorThrower* thrower); // Get the source position from a given function index and byte offset, // for either asm.js or pure Wasm modules. int GetSourcePosition(const WasmModule*, uint32_t func_index, uint32_t byte_offset, bool is_at_number_conversion); // Translate function index to the index relative to the first declared (i.e. // non-imported) function. inline int declared_function_index(const WasmModule* module, int func_index) { DCHECK_LE(module->num_imported_functions, func_index); int declared_idx = func_index - module->num_imported_functions; DCHECK_GT(module->num_declared_functions, declared_idx); return declared_idx; } // Translate from function index to jump table offset. int JumpTableOffset(const WasmModule* module, int func_index); // TruncatedUserString makes it easy to output names up to a certain length, and // output a truncation followed by '...' if they exceed a limit. // Use like this: // TruncatedUserString<> name (pc, len); // printf("... %.*s ...", name.length(), name.start()) template <int kMaxLen = 50> class TruncatedUserString { static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)"); public: template <typename T> explicit TruncatedUserString(base::Vector<T> name) : TruncatedUserString(name.begin(), name.length()) {} TruncatedUserString(const uint8_t* start, size_t len) : TruncatedUserString(reinterpret_cast<const char*>(start), len) {} TruncatedUserString(const char* start, size_t len) : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) { if (len > static_cast<size_t>(kMaxLen)) { memcpy(buffer_, start, kMaxLen - 3); memset(buffer_ + kMaxLen - 3, '.', 3); start_ = buffer_; } } const char* start() const { return start_; } int length() const { return length_; } private: const char* start_; const int length_; char buffer_[kMaxLen]; }; // Print the signature into the given {buffer}, using {delimiter} as separator // between parameter types and return types. If {buffer} is non-empty, it will // be null-terminated, even if the signature is cut off. Returns the number of // characters written, excluding the terminating null-byte. size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig*, char delimiter = ':'); V8_EXPORT_PRIVATE size_t GetWireBytesHash(base::Vector<const uint8_t> wire_bytes); // Get the required number of feedback slots for a function. int NumFeedbackSlots(const WasmModule* module, int func_index); } // namespace v8::internal::wasm #endif // V8_WASM_WASM_MODULE_H_