#ifndef V8_WASM_WASM_CODE_MANAGER_H_
#define V8_WASM_WASM_CODE_MANAGER_H_
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif
#include <atomic>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "src/base/address-region.h"
#include "src/base/bit-field.h"
#include "src/base/macros.h"
#include "src/base/vector.h"
#include "src/builtins/builtins.h"
#include "src/codegen/safepoint-table.h"
#include "src/codegen/source-position.h"
#include "src/handles/handles.h"
#include "src/logging/counters.h"
#include "src/sandbox/sandbox-malloc.h"
#include "src/tasks/operations-barrier.h"
#include "src/trap-handler/trap-handler.h"
#include "src/wasm/compilation-environment.h"
#include "src/wasm/wasm-code-coverage.h"
#include "src/wasm/wasm-code-pointer-table.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module-sourcemap.h"
#include "src/wasm/wasm-tier.h"
namespace v8 {
class CFunctionInfo;
namespace internal {
class CodeDesc;
class InstructionStream;
class Isolate;
namespace wasm {
class AssumptionsJournal;
class DebugInfo;
class NamesProvider;
class NativeModule;
struct WasmCompilationResult;
class WasmEngine;
class WasmImportWrapperCache;
struct WasmModule;
enum class WellKnownImport : uint8_t;
class V8_EXPORT_PRIVATE DisjointAllocationPool final {
public:
MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(DisjointAllocationPool);
explicit DisjointAllocationPool(base::AddressRegion region)
: regions_({region}) {}
base::AddressRegion Merge(base::AddressRegion);
base::AddressRegion Allocate(size_t size);
base::AddressRegion AllocateInRegion(size_t size, base::AddressRegion);
bool IsEmpty() const { return regions_.empty(); }
const auto& regions() const { return regions_; }
private:
std::set<base::AddressRegion, base::AddressRegion::StartAddressLess> regions_;
};
constexpr WasmCodePointer kInvalidWasmCodePointer =
WasmCodePointer{WasmCodePointerTable::kInvalidHandle};
class V8_EXPORT_PRIVATE WasmCode final {
public:
enum Kind {
kWasmFunction,
kWasmToCapiWrapper,
kWasmToJsWrapper,
kWasmStackEntryWrapper,
#if V8_ENABLE_DRUMBRAKE
kInterpreterEntry,
#endif
kJumpTable
};
static constexpr Builtin GetRecordWriteBuiltin(SaveFPRegsMode fp_mode) {
switch (fp_mode) {
case SaveFPRegsMode::kIgnore:
return Builtin::kRecordWriteIgnoreFP;
case SaveFPRegsMode::kSave:
return Builtin::kRecordWriteSaveFP;
}
}
#ifdef V8_IS_TSAN
static Builtin GetTSANStoreBuiltin(SaveFPRegsMode fp_mode, int size,
std::memory_order order) {
if (order == std::memory_order_relaxed) {
if (size == kInt8Size) {
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANRelaxedStore8IgnoreFP
: Builtin::kTSANRelaxedStore8SaveFP;
} else if (size == kInt16Size) {
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANRelaxedStore16IgnoreFP
: Builtin::kTSANRelaxedStore16SaveFP;
} else if (size == kInt32Size) {
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANRelaxedStore32IgnoreFP
: Builtin::kTSANRelaxedStore32SaveFP;
} else {
CHECK_EQ(size, kInt64Size);
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANRelaxedStore64IgnoreFP
: Builtin::kTSANRelaxedStore64SaveFP;
}
} else {
DCHECK_EQ(order, std::memory_order_seq_cst);
if (size == kInt8Size) {
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANSeqCstStore8IgnoreFP
: Builtin::kTSANSeqCstStore8SaveFP;
} else if (size == kInt16Size) {
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANSeqCstStore16IgnoreFP
: Builtin::kTSANSeqCstStore16SaveFP;
} else if (size == kInt32Size) {
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANSeqCstStore32IgnoreFP
: Builtin::kTSANSeqCstStore32SaveFP;
} else {
CHECK_EQ(size, kInt64Size);
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANSeqCstStore64IgnoreFP
: Builtin::kTSANSeqCstStore64SaveFP;
}
}
}
static Builtin GetTSANRelaxedLoadBuiltin(SaveFPRegsMode fp_mode, int size) {
if (size == kInt32Size) {
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANRelaxedLoad32IgnoreFP
: Builtin::kTSANRelaxedLoad32SaveFP;
} else {
CHECK_EQ(size, kInt64Size);
return fp_mode == SaveFPRegsMode::kIgnore
? Builtin::kTSANRelaxedLoad64IgnoreFP
: Builtin::kTSANRelaxedLoad64SaveFP;
}
}
#endif
base::Vector<uint8_t> instructions() const {
return base::VectorOf(instructions_, instructions_size_);
}
Address instruction_start() const {
return reinterpret_cast<Address>(instructions_);
}
size_t instructions_size() const { return instructions_size_; }
base::Vector<const uint8_t> reloc_info() const {
return {protected_instructions_data().end(), reloc_info_size_};
}
base::Vector<const uint8_t> source_positions() const {
return {reloc_info().end(), source_positions_size_};
}
base::Vector<const uint8_t> inlining_positions() const {
return {source_positions().end(), inlining_positions_size_};
}
base::Vector<const uint8_t> deopt_data() const {
return {inlining_positions().end(), deopt_data_size_};
}
int index() const { return index_; }
bool IsAnonymous() const { return index_ == kAnonymousFuncIndex; }
Kind kind() const { return KindField::decode(flags_); }
NativeModule* native_module() const { return native_module_; }
ExecutionTier tier() const { return ExecutionTierField::decode(flags_); }
Address constant_pool() const;
Address handler_table() const;
int handler_table_size() const;
Address code_comments() const;
int code_comments_size() const;
Address jump_table_info() const;
int jump_table_info_size() const;
bool has_jump_table_info() const { return jump_table_info_size() > 0; }
int constant_pool_offset() const { return constant_pool_offset_; }
int safepoint_table_offset() const { return safepoint_table_offset_; }
int handler_table_offset() const { return handler_table_offset_; }
int code_comments_offset() const { return code_comments_offset_; }
int jump_table_info_offset() const { return jump_table_info_offset_; }
int unpadded_binary_size() const { return unpadded_binary_size_; }
int stack_slots() const { return stack_slots_; }
int ool_spills() const { return ool_spills_; }
uint64_t signature_hash() const { return signature_hash_; }
uint16_t first_tagged_parameter_slot() const {
return tagged_parameter_slots_ >> 16;
}
uint16_t num_tagged_parameter_slots() const {
return tagged_parameter_slots_ & 0xFFFF;
}
uint32_t raw_tagged_parameter_slots_for_serialization() const {
return tagged_parameter_slots_;
}
bool is_liftoff() const { return tier() == ExecutionTier::kLiftoff; }
bool is_turbofan() const { return tier() == ExecutionTier::kTurbofan; }
bool contains(Address pc) const {
return reinterpret_cast<Address>(instructions_) <= pc &&
pc < reinterpret_cast<Address>(instructions_ + instructions_size_);
}
bool is_inspectable() const { return is_liftoff() && for_debugging(); }
base::Vector<const uint8_t> protected_instructions_data() const {
return {meta_data_.get(), protected_instructions_size_};
}
base::Vector<const trap_handler::ProtectedInstructionData>
protected_instructions() const {
return base::Vector<const trap_handler::ProtectedInstructionData>::cast(
protected_instructions_data());
}
struct __attribute__((packed)) EffectHandler {
int call_offset;
int tag_index;
int handler_offset;
};
static_assert(sizeof(WasmCode::EffectHandler) == 3 * kIntSize);
base::Vector<const EffectHandler> effect_handlers() const {
return effect_handlers_.as_vector();
}
bool IsProtectedInstruction(Address pc);
void Validate() const;
void Print(const char* name = nullptr) const;
void MaybePrint() const;
void Disassemble(const char* name, std::ostream& os,
Address current_pc = kNullAddress) const;
static bool ShouldBeLogged(Isolate* isolate);
void LogCode(Isolate* isolate, const char* source_url, int script_id) const;
WasmCode(const WasmCode&) = delete;
WasmCode& operator=(const WasmCode&) = delete;
~WasmCode();
void IncRef() {
[[maybe_unused]] uint32_t old_field =
ref_count_bitfield_.fetch_add(1, std::memory_order_acq_rel);
DCHECK_LE(1, refcount(old_field));
DCHECK_GT(kMaxInt, refcount(old_field));
}
bool IncRefIfNotDying() {
uint32_t old_field = ref_count_bitfield_.load(std::memory_order_acquire);
while (true) {
if (is_dying(old_field)) return false;
if (ref_count_bitfield_.compare_exchange_weak(
old_field, old_field + 1, std::memory_order_acq_rel)) {
return true;
}
}
}
V8_WARN_UNUSED_RESULT bool DecRef() {
uint32_t old_field = ref_count_bitfield_.load(std::memory_order_acquire);
while (true) {
DCHECK_LE(1, refcount(old_field));
if (V8_UNLIKELY(refcount(old_field) == 1)) {
if (is_dying(old_field)) {
return DecRefOnDeadCode();
}
if (ref_count_bitfield_.compare_exchange_weak(
old_field, old_field | kIsDyingMask,
std::memory_order_acq_rel)) {
DecRefOnPotentiallyDeadCode();
return false;
}
continue;
}
DCHECK_LT(1, refcount(old_field));
if (ref_count_bitfield_.compare_exchange_weak(
old_field, old_field - 1, std::memory_order_acq_rel)) {
return false;
}
}
}
void DecRefOnLiveCode() {
[[maybe_unused]] uint32_t old_bitfield_value =
ref_count_bitfield_.fetch_sub(1, std::memory_order_acq_rel);
DCHECK_LE(2, refcount(old_bitfield_value));
}
V8_WARN_UNUSED_RESULT bool DecRefOnDeadCode() {
uint32_t old_bitfield_value =
ref_count_bitfield_.fetch_sub(1, std::memory_order_acq_rel);
return refcount(old_bitfield_value) == 1;
}
static void DecrementRefCount(base::Vector<WasmCode* const>);
void DcheckRefCountIsOne() {
DCHECK_EQ(1, refcount(ref_count_bitfield_.load(std::memory_order_acquire)));
}
SourcePosition GetSourcePositionBefore(int code_offset);
int GetSourceOffsetBefore(int code_offset);
std::tuple<int, bool, SourcePosition> GetInliningPosition(
int inlining_id) const;
ForDebugging for_debugging() const {
return ForDebuggingField::decode(flags_);
}
bool is_dying() const {
return is_dying(ref_count_bitfield_.load(std::memory_order_acquire));
}
static bool is_dying(uint32_t bit_field_value) {
return (bit_field_value & kIsDyingMask) != 0;
}
static uint32_t refcount(uint32_t bit_field_value) {
return bit_field_value & ~kIsDyingMask;
}
bool frame_has_feedback_slot() const {
return FrameHasFeedbackSlotField::decode(flags_);
}
enum FlushICache : bool { kFlushICache = true, kNoFlushICache = false };
size_t EstimateCurrentMemoryConsumption() const;
std::string DebugName() const;
private:
friend class NativeModule;
friend class WasmImportWrapperCache;
WasmCode(NativeModule* native_module, int index,
base::Vector<uint8_t> instructions, int stack_slots, int ool_spills,
uint32_t tagged_parameter_slots, int safepoint_table_offset,
int handler_table_offset, int constant_pool_offset,
int code_comments_offset, int jump_table_info_offset,
int unpadded_binary_size,
base::Vector<const uint8_t> protected_instructions_data,
base::Vector<const uint8_t> reloc_info,
base::Vector<const uint8_t> source_position_table,
base::Vector<const uint8_t> inlining_positions,
base::Vector<const uint8_t> deopt_data, Kind kind,
ExecutionTier tier, ForDebugging for_debugging,
uint64_t signature_hash,
base::OwnedVector<const EffectHandler> effect_handlers,
bool frame_has_feedback_slot = false)
: native_module_(native_module),
instructions_(instructions.begin()),
signature_hash_(signature_hash),
meta_data_(ConcatenateBytes({protected_instructions_data, reloc_info,
source_position_table, inlining_positions,
deopt_data})),
instructions_size_(static_cast<uint32_t>(instructions.size())),
reloc_info_size_(static_cast<uint32_t>(reloc_info.size())),
source_positions_size_(
static_cast<uint32_t>(source_position_table.size())),
inlining_positions_size_(
static_cast<uint32_t>(inlining_positions.size())),
deopt_data_size_(static_cast<uint32_t>(deopt_data.size())),
protected_instructions_size_(
static_cast<uint32_t>(protected_instructions_data.size())),
index_(index),
constant_pool_offset_(constant_pool_offset),
stack_slots_(stack_slots),
ool_spills_(ool_spills),
tagged_parameter_slots_(tagged_parameter_slots),
safepoint_table_offset_(safepoint_table_offset),
handler_table_offset_(handler_table_offset),
code_comments_offset_(code_comments_offset),
jump_table_info_offset_(jump_table_info_offset),
unpadded_binary_size_(unpadded_binary_size),
effect_handlers_(std::move(effect_handlers)),
flags_(KindField::encode(kind) | ExecutionTierField::encode(tier) |
ForDebuggingField::encode(for_debugging) |
FrameHasFeedbackSlotField::encode(frame_has_feedback_slot)) {
DCHECK_LE(safepoint_table_offset, unpadded_binary_size);
DCHECK_LE(handler_table_offset, unpadded_binary_size);
DCHECK_LE(code_comments_offset, unpadded_binary_size);
DCHECK_LE(constant_pool_offset, unpadded_binary_size);
DCHECK_LE(jump_table_info_offset, unpadded_binary_size);
}
std::unique_ptr<const uint8_t[]> ConcatenateBytes(
std::initializer_list<base::Vector<const uint8_t>>);
int trap_handler_index() const {
CHECK(has_trap_handler_index());
return trap_handler_index_;
}
void set_trap_handler_index(int value) {
CHECK(!has_trap_handler_index());
trap_handler_index_ = value;
}
bool has_trap_handler_index() const { return trap_handler_index_ >= 0; }
void RegisterTrapHandlerData();
V8_NOINLINE void DecRefOnPotentiallyDeadCode();
NativeModule* const native_module_ = nullptr;
uint8_t* const instructions_;
const uint64_t signature_hash_;
std::unique_ptr<const uint8_t[]> meta_data_;
const uint32_t instructions_size_;
const uint32_t reloc_info_size_;
const uint32_t source_positions_size_;
const uint32_t inlining_positions_size_;
const uint32_t deopt_data_size_;
const uint32_t protected_instructions_size_;
const int index_;
const int constant_pool_offset_;
const int stack_slots_;
const int ool_spills_;
const uint32_t tagged_parameter_slots_;
const int safepoint_table_offset_;
const int handler_table_offset_;
const int code_comments_offset_;
const int jump_table_info_offset_;
const int unpadded_binary_size_;
int trap_handler_index_ = -1;
base::OwnedVector<const EffectHandler> effect_handlers_;
const uint8_t flags_;
using KindField = base::BitField8<Kind, 0, 3>;
using ExecutionTierField = KindField::Next<ExecutionTier, 2>;
using ForDebuggingField = ExecutionTierField::Next<ForDebugging, 2>;
using FrameHasFeedbackSlotField = ForDebuggingField::Next<bool, 1>;
static constexpr uint32_t kIsDyingMask = 0x8000'0000u;
std::atomic<uint32_t> ref_count_bitfield_{1};
};
WasmCode::Kind GetCodeKind(const WasmCompilationResult& result);
const char* GetWasmCodeKindAsString(WasmCode::Kind);
struct UnpublishedWasmCode {
std::unique_ptr<WasmCode> code;
std::unique_ptr<AssumptionsJournal> assumptions;
static constexpr AssumptionsJournal* kNoAssumptions = nullptr;
};
class WasmCodeAllocator {
public:
explicit WasmCodeAllocator(DelayedCounterUpdates*);
~WasmCodeAllocator();
void Init(VirtualMemory code_space);
void InitializeCodeRange(NativeModule* native_module,
base::AddressRegion region);
size_t committed_code_space() const {
return committed_code_space_.load(std::memory_order_acquire);
}
size_t generated_code_size() const {
return generated_code_size_.load(std::memory_order_acquire);
}
size_t freed_code_size() const {
return freed_code_size_.load(std::memory_order_acquire);
}
base::Vector<uint8_t> AllocateForCode(NativeModule*, size_t size);
base::Vector<uint8_t> AllocateForWrapper(size_t size);
base::Vector<uint8_t> AllocateForCodeInRegion(NativeModule*, size_t size,
base::AddressRegion);
void FreeCode(base::Vector<WasmCode* const>);
size_t GetNumCodeSpaces() const;
private:
DisjointAllocationPool free_code_space_;
DisjointAllocationPool freed_code_space_;
std::vector<VirtualMemory> owned_code_space_;
std::atomic<size_t> committed_code_space_{0};
std::atomic<size_t> generated_code_size_{0};
std::atomic<size_t> freed_code_size_{0};
DelayedCounterUpdates* counter_updates_;
};
class V8_EXPORT_PRIVATE NativeModule final {
public:
class V8_NODISCARD NativeModuleAllocationLockScope {
public:
explicit NativeModuleAllocationLockScope(NativeModule* module)
: lock_(module->allocation_mutex_) {}
private:
base::RecursiveMutexGuard lock_;
};
static constexpr ExternalPointerTag kManagedTag = kWasmNativeModuleTag;
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_S390X || V8_TARGET_ARCH_ARM64 || \
V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_LOONG64 || \
V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_MIPS64
static constexpr bool kNeedsFarJumpsBetweenCodeSpaces = true;
#else
static constexpr bool kNeedsFarJumpsBetweenCodeSpaces = false;
#endif
NativeModule(const NativeModule&) = delete;
NativeModule& operator=(const NativeModule&) = delete;
~NativeModule();
uint32_t DisassembleForLcov(
std::ostream& out, std::vector<int>& function_body_offsets,
std::map<uint32_t, uint32_t>& bytecode_disasm_offsets);
WasmCode* PublishCode(UnpublishedWasmCode);
std::vector<WasmCode*> PublishCode(base::Vector<UnpublishedWasmCode>);
void UpdateWellKnownImports(base::Vector<WellKnownImport> entries);
void ReinstallDebugCode(WasmCode*);
struct JumpTablesRef {
Address jump_table_start = kNullAddress;
Address far_jump_table_start = kNullAddress;
bool is_valid() const { return far_jump_table_start != kNullAddress; }
};
std::pair<base::Vector<uint8_t>, JumpTablesRef> AllocateForDeserializedCode(
size_t total_code_size);
std::unique_ptr<WasmCode> AddDeserializedCode(
int index, base::Vector<uint8_t> instructions, int stack_slots,
int ool_spills, uint32_t tagged_parameter_slots,
int safepoint_table_offset, int handler_table_offset,
int constant_pool_offset, int code_comments_offset,
int jump_table_info_offset, int unpadded_binary_size,
base::Vector<const uint8_t> protected_instructions_data,
base::Vector<const uint8_t> reloc_info,
base::Vector<const uint8_t> source_position_table,
base::Vector<const uint8_t> inlining_positions,
base::Vector<const uint8_t> deopt_data, WasmCode::Kind kind,
ExecutionTier tier,
base::OwnedVector<const WasmCode::EffectHandler> effect_handlers);
WasmCode* AddCodeForTesting(DirectHandle<Code> code, uint64_t signature_hash);
void InitializeJumpTableForLazyCompilation(uint32_t num_wasm_functions);
void InitializeCodePointerTableHandles(uint32_t num_wasm_functions);
void FreeCodePointerTableHandles();
void UseLazyStubLocked(uint32_t func_index);
std::pair<std::vector<WasmCode*>, std::vector<WellKnownImport>>
SnapshotCodeTable() const;
std::vector<WasmCode*> SnapshotAllOwnedCode() const;
WasmCode* GetCode(uint32_t index) const;
bool HasCode(uint32_t index) const;
bool HasCodeWithTier(uint32_t index, ExecutionTier tier) const;
void SetWasmSourceMap(std::unique_ptr<WasmModuleSourceMap> source_map);
WasmModuleSourceMap* GetWasmSourceMap() const;
Address jump_table_start() const {
return main_jump_table_ ? main_jump_table_->instruction_start()
: kNullAddress;
}
Address GetNearCallTargetForFunction(uint32_t func_index,
const JumpTablesRef&) const;
Address GetJumpTableEntryForBuiltin(Builtin builtin,
const JumpTablesRef&) const;
uint32_t GetFunctionIndexFromJumpTableSlot(Address slot_address) const;
using CallIndirectTargetMap = absl::flat_hash_map<WasmCodePointer, uint32_t>;
CallIndirectTargetMap CreateIndirectCallTargetToFunctionIndexMap() const;
void LogWasmCodes(Isolate*, Tagged<Script>);
CompilationState* compilation_state() const {
return compilation_state_.get();
}
uint32_t num_functions() const {
return module_->num_declared_functions + module_->num_imported_functions;
}
uint32_t num_imported_functions() const {
return module_->num_imported_functions;
}
uint32_t num_declared_functions() const {
return module_->num_declared_functions;
}
void set_lazy_compile_frozen(bool frozen) { lazy_compile_frozen_ = frozen; }
bool lazy_compile_frozen() const { return lazy_compile_frozen_; }
base::Vector<const uint8_t> wire_bytes() const {
return std::atomic_load(&wire_bytes_)->as_vector();
}
const WasmModule* module() const { return module_.get(); }
std::shared_ptr<const WasmModule> shared_module() const { return module_; }
size_t committed_code_space() const {
return code_allocator_.committed_code_space();
}
size_t generated_code_size() const {
return code_allocator_.generated_code_size();
}
size_t liftoff_bailout_count() const {
return liftoff_bailout_count_.load(std::memory_order_relaxed);
}
void AddLazyCompilationTimeSample(int64_t sample);
int num_lazy_compilations() const {
return num_lazy_compilations_.load(std::memory_order_relaxed);
}
int64_t sum_lazy_compilation_time_in_ms() const {
return sum_lazy_compilation_time_in_micro_sec_.load(
std::memory_order_relaxed) /
1000;
}
int64_t max_lazy_compilation_time_in_ms() const {
return max_lazy_compilation_time_in_micro_sec_.load(
std::memory_order_relaxed) /
1000;
}
bool ShouldLazyCompilationMetricsBeReported() {
return should_metrics_be_reported_.exchange(false,
std::memory_order_relaxed);
}
bool ShouldPgoDataBeWritten() {
return should_pgo_data_be_written_.exchange(false,
std::memory_order_relaxed);
}
bool HasWireBytes() const {
auto wire_bytes = std::atomic_load(&wire_bytes_);
return wire_bytes && !wire_bytes->empty();
}
void SetWireBytes(base::OwnedVector<const uint8_t> wire_bytes);
void AddLiftoffBailout() {
liftoff_bailout_count_.fetch_add(1, std::memory_order_relaxed);
}
WasmCode* Lookup(Address) const;
WasmEnabledFeatures enabled_features() const { return enabled_features_; }
const CompileTimeImports& compile_imports() const { return compile_imports_; }
Builtin GetBuiltinInJumptableSlot(Address target) const;
void SampleCodeSize(Counters*) const;
V8_WARN_UNUSED_RESULT UnpublishedWasmCode
AddCompiledCode(WasmCompilationResult&);
V8_WARN_UNUSED_RESULT std::vector<UnpublishedWasmCode> AddCompiledCode(
base::Vector<WasmCompilationResult>);
void SetDebugState(DebugState);
DebugState IsInDebugState() const {
base::RecursiveMutexGuard lock(&allocation_mutex_);
return debug_state_;
}
enum class RemoveFilter {
kRemoveDebugCode,
kRemoveNonDebugCode,
kRemoveLiftoffCode,
kRemoveTurbofanCode,
kRemoveAllCode,
};
void RemoveCompiledCode(RemoveFilter filter);
size_t SumLiftoffCodeSizeForTesting() const;
void FreeCode(base::Vector<WasmCode* const>);
size_t GetNumberOfCodeSpacesForTesting() const;
bool HasDebugInfo() const;
DebugInfo* GetDebugInfo();
NamesProvider* GetNamesProvider();
std::atomic<uint32_t>* tiering_budget_array() const {
return tiering_budgets_.get();
}
size_t EstimateCurrentMemoryConsumption() const;
void PrintCurrentMemoryConsumptionEstimate() const;
bool log_code() const { return log_code_.load(std::memory_order_relaxed); }
void EnableCodeLogging() { log_code_.store(true, std::memory_order_relaxed); }
void DisableCodeLogging() {
log_code_.store(false, std::memory_order_relaxed);
}
enum class JumpTableType {
kJumpTable,
kFarJumpTable,
kLazyCompileTable,
};
bool TrySetFastApiCallTarget(int func_index, Address target) {
Address old_val =
fast_api_targets_[func_index].load(std::memory_order_relaxed);
if (old_val == target) {
return true;
}
if (old_val != kNullAddress) {
return false;
}
if (fast_api_targets_[func_index].compare_exchange_strong(
old_val, target, std::memory_order_relaxed)) {
return true;
}
return old_val == target;
}
std::atomic<Address>* fast_api_targets() const {
return fast_api_targets_.get();
}
void set_fast_api_signature(int func_index, const MachineSignature* sig) {
fast_api_signatures_[func_index] = sig;
}
bool has_fast_api_signature(int index) {
return fast_api_signatures_[index] != nullptr;
}
std::atomic<const MachineSignature*>* fast_api_signatures() const {
return fast_api_signatures_.get();
}
WasmCodePointer GetCodePointerHandle(int index) const;
const std::shared_ptr<WasmModuleCoverageData>& coverage_data() const {
return coverage_data_;
}
void set_continuation_wrapper(WasmCode* wrapper) {
continuation_wrapper_ = wrapper;
}
WasmCode* continuation_wrapper() {
DCHECK_NOT_NULL(continuation_wrapper_);
return continuation_wrapper_;
}
DelayedCounterUpdates* counter_updates() { return &counter_updates_; }
private:
friend class WasmCode;
friend class WasmCodeAllocator;
friend class WasmCodeManager;
friend class CodeSpaceWriteScope;
struct CodeSpaceData {
base::AddressRegion region;
WasmCode* jump_table;
WasmCode* far_jump_table;
};
NativeModule(WasmEnabledFeatures enabled_features,
WasmDetectedFeatures detected_features,
CompileTimeImports compile_imports, VirtualMemory code_space,
std::shared_ptr<const WasmModule> module,
std::shared_ptr<NativeModule>* shared_this);
std::unique_ptr<WasmCode> AddCodeWithCodeSpace(
int index, const CodeDesc& desc, int stack_slots, int ool_spill_count,
uint32_t tagged_parameter_slots,
base::Vector<const uint8_t> protected_instructions_data,
base::Vector<const uint8_t> source_position_table,
base::Vector<const uint8_t> inlining_positions,
base::Vector<const uint8_t> deopt_data, WasmCode::Kind kind,
ExecutionTier tier, ForDebugging for_debugging,
base::OwnedVector<const WasmCode::EffectHandler> effect_handlers,
bool frame_has_feedback_slot, base::Vector<uint8_t> code_space,
const JumpTablesRef& jump_tables_ref);
WasmCode* CreateEmptyJumpTableLocked(int jump_table_size, JumpTableType type);
WasmCode* CreateEmptyJumpTableInRegionLocked(int jump_table_size,
base::AddressRegion,
JumpTableType type);
JumpTablesRef FindJumpTablesForRegionLocked(base::AddressRegion) const;
void PatchJumpTablesLocked(uint32_t slot_index, Address target,
Address code_pointer_table_target,
uint64_t signature_hash);
void PatchJumpTableLocked(WritableJumpTablePair& jump_table_pair,
const CodeSpaceData&, uint32_t slot_index,
Address target);
void AddCodeSpaceLocked(base::AddressRegion);
WasmCode* PublishCodeLocked(std::unique_ptr<WasmCode>, AssumptionsJournal*);
void TransferNewOwnedCodeLocked() const;
bool should_update_code_table(WasmCode* new_code, WasmCode* prior_code) const;
OperationsBarrier::Token engine_scope_;
WasmCodeAllocator code_allocator_;
const WasmEnabledFeatures enabled_features_;
const CompileTimeImports compile_imports_;
std::shared_ptr<const WasmModule> module_;
std::unique_ptr<WasmModuleSourceMap> source_map_;
std::shared_ptr<base::OwnedVector<const uint8_t>> wire_bytes_;
WasmCode* main_jump_table_ = nullptr;
WasmCode* main_far_jump_table_ = nullptr;
WasmCode* lazy_compile_table_ = nullptr;
std::unique_ptr<CompilationState> compilation_state_;
#ifdef V8_ENABLE_SANDBOX_HARDWARE_SUPPORT
std::unique_ptr<std::atomic<uint32_t>[], SandboxFreeDeleter> tiering_budgets_;
#else
std::unique_ptr<std::atomic<uint32_t>[]> tiering_budgets_;
#endif
mutable base::RecursiveMutex allocation_mutex_;
mutable std::map<Address, std::unique_ptr<WasmCode>> owned_code_;
mutable std::vector<std::unique_ptr<WasmCode>> new_owned_code_;
std::unique_ptr<WasmCode*[]> code_table_;
std::unique_ptr<WasmCodePointer[]> code_pointer_handles_;
size_t code_pointer_handles_size_ = 0;
std::vector<CodeSpaceData> code_space_data_;
std::unique_ptr<DebugInfo> debug_info_;
std::unique_ptr<NamesProvider> names_provider_;
DebugState debug_state_ = kNotDebugging;
bool lazy_compile_frozen_ = false;
std::atomic<size_t> liftoff_bailout_count_{0};
std::atomic<int> num_lazy_compilations_{0};
std::atomic<int64_t> sum_lazy_compilation_time_in_micro_sec_{0};
std::atomic<int64_t> max_lazy_compilation_time_in_micro_sec_{0};
std::atomic<bool> should_metrics_be_reported_{true};
std::atomic<bool> should_pgo_data_be_written_{true};
std::atomic<bool> log_code_{false};
std::unique_ptr<std::atomic<Address>[]> fast_api_targets_;
std::unique_ptr<std::atomic<const MachineSignature*>[]> fast_api_signatures_;
std::shared_ptr<WasmModuleCoverageData> coverage_data_;
WasmCode* continuation_wrapper_{nullptr};
DelayedCounterUpdates counter_updates_;
};
class V8_EXPORT_PRIVATE WasmCodeManager final {
public:
WasmCodeManager();
WasmCodeManager(const WasmCodeManager&) = delete;
WasmCodeManager& operator=(const WasmCodeManager&) = delete;
~WasmCodeManager();
#if defined(V8_OS_WIN64)
static bool CanRegisterUnwindInfoForNonABICompliantCodeRange();
#endif
NativeModule* LookupNativeModule(Address pc) const;
WasmCode* LookupCode(Isolate* isolate, Address pc) const;
std::pair<WasmCode*, SafepointEntry> LookupCodeAndSafepoint(Isolate* isolate,
Address pc);
void FlushCodeLookupCache(Isolate* isolate);
size_t committed_code_space() const {
return total_committed_code_space_.load();
}
static size_t EstimateLiftoffCodeSize(int body_size);
static size_t EstimateNativeModuleCodeSize(const WasmModule*);
static size_t EstimateNativeModuleCodeSize(int num_functions,
size_t code_section_length);
static size_t EstimateNativeModuleMetaDataSize(const WasmModule*);
static bool HasMemoryProtectionKeySupport();
static bool MemoryProtectionKeysEnabled();
static bool MemoryProtectionKeyWritable();
private:
friend class WasmCodeAllocator;
friend class WasmCodeLookupCache;
friend class WasmEngine;
friend class WasmImportWrapperCache;
std::shared_ptr<NativeModule> NewNativeModule(
WasmEnabledFeatures enabled_features,
WasmDetectedFeatures detected_features,
CompileTimeImports compile_imports, size_t code_size_estimate,
std::shared_ptr<const WasmModule> module);
V8_WARN_UNUSED_RESULT VirtualMemory TryAllocate(size_t size);
void Commit(base::AddressRegion);
void Decommit(base::AddressRegion);
void FreeNativeModule(base::Vector<VirtualMemory> owned_code,
size_t committed_size);
void AssignRange(base::AddressRegion, NativeModule*);
WasmCode* LookupCode(Address pc) const;
const size_t max_committed_code_space_;
std::atomic<size_t> total_committed_code_space_{0};
std::atomic<size_t> critical_committed_code_space_;
mutable base::Mutex native_modules_mutex_;
std::map<Address, std::pair<Address, NativeModule*>> lookup_map_;
std::atomic<Address> next_code_space_hint_;
};
class V8_EXPORT_PRIVATE V8_NODISCARD WasmCodeRefScope {
public:
WasmCodeRefScope();
WasmCodeRefScope(const WasmCodeRefScope&) = delete;
WasmCodeRefScope& operator=(const WasmCodeRefScope&) = delete;
~WasmCodeRefScope();
static void AddRef(WasmCode*);
static WasmCode* AddRefIfNotDying(WasmCode* code);
private:
WasmCodeRefScope* const previous_scope_;
std::vector<WasmCode*> code_ptrs_;
};
class WasmCodeLookupCache final {
friend WasmCodeManager;
public:
WasmCodeLookupCache() { Flush(); }
WasmCodeLookupCache(const WasmCodeLookupCache&) = delete;
WasmCodeLookupCache& operator=(const WasmCodeLookupCache&) = delete;
private:
struct CacheEntry {
std::atomic<Address> pc;
wasm::WasmCode* code;
SafepointEntry safepoint_entry;
CacheEntry() : safepoint_entry() {}
};
void Flush();
CacheEntry* GetCacheEntry(Address pc);
static const int kWasmCodeLookupCacheSize = 1024;
CacheEntry cache_[kWasmCodeLookupCacheSize];
};
}
}
}
#endif