#ifndef V8_WASM_STACKS_H_
#define V8_WASM_STACKS_H_
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif
#include <optional>
#include "src/common/globals.h"
#include "src/flags/flags.h"
#include "src/objects/visitors.h"
#include "src/utils/allocation.h"
#include "src/wasm/value-type.h"
namespace v8 {
class Isolate;
namespace internal {
class ThreadLocalTop;
}
}
namespace v8::internal::wasm {
class StackMemory;
struct JumpBuffer {
Address sp;
Address fp;
Address pc;
void* stack_limit;
StackMemory* parent = nullptr;
bool is_on_central_stack;
enum StackState : int32_t {
Active,
Suspended,
Inactive,
Retired
};
StackState state;
};
class StackMemory {
public:
static std::unique_ptr<StackMemory> New() {
return std::unique_ptr<StackMemory>(new StackMemory());
}
static StackMemory* GetCentralStackView(Isolate* isolate);
~StackMemory();
void* jslimit() const;
Address base() const {
Address memory_limit = active_segment_
? active_segment_->base()
: reinterpret_cast<Address>(limit_ + size_);
#ifdef USE_SIMULATOR
constexpr int kStackBaseSafetyOffset = 20 * kSystemPointerSize;
#else
constexpr int kStackBaseSafetyOffset = 0;
#endif
return memory_limit - kStackBaseSafetyOffset;
}
void set_current_continuation(Tagged<WasmContinuationObject> cont) {
current_cont_ = cont;
}
bool IsValidContinuation(Tagged<WasmContinuationObject> cont);
JumpBuffer* jmpbuf() { return &jmpbuf_; }
bool Contains(Address addr) {
if (!owned_) {
return reinterpret_cast<Address>(jslimit()) <= addr && addr < base();
}
for (auto segment = first_segment_; segment;
segment = segment->next_segment_) {
if (reinterpret_cast<Address>(segment->limit_) <= addr &&
addr < segment->base()) {
return true;
}
if (segment == active_segment_) break;
}
return false;
}
int id() { return id_; }
bool IsActive() { return jmpbuf_.state == JumpBuffer::Active; }
void set_index(size_t index) { index_ = index; }
size_t index() { return index_; }
size_t allocated_size() {
size_t size = 0;
auto segment = first_segment_;
while (segment) {
size += segment->size_;
segment = segment->next_segment_;
}
return size;
}
void FillWith(uint8_t value) {
auto segment = first_segment_;
while (segment) {
memset(segment->limit_, value, segment->size_);
segment = segment->next_segment_;
}
}
void Iterate(v8::internal::RootVisitor* v, Isolate* isolate,
ThreadLocalTop* thread);
Address old_fp() { return active_segment_->old_fp; }
bool Grow(Address current_fp, size_t min_size);
Address Shrink();
void ShrinkTo(Address stack_address);
void Reset();
class StackSegment {
public:
Address base() const { return reinterpret_cast<Address>(limit_ + size_); }
private:
explicit StackSegment(size_t size);
~StackSegment();
uint8_t* limit_;
size_t size_;
StackSegment* next_segment_ = nullptr;
StackSegment* prev_segment_ = nullptr;
Address old_fp = 0;
friend class StackMemory;
};
struct StackSwitchInfo {
Address source_fp = kNullAddress;
Address target_sp = kNullAddress;
bool has_value() const { return source_fp != kNullAddress; }
};
const StackSwitchInfo& stack_switch_info() const {
return stack_switch_info_;
}
void set_stack_switch_info(Address fp, Address sp) {
stack_switch_info_ = {fp, sp};
}
void clear_stack_switch_info() {
stack_switch_info_.source_fp = kNullAddress;
}
void set_func_ref(Tagged<WasmFuncRef> func_ref) { func_ref_ = func_ref; }
static int func_ref_offset() { return OFFSET_OF(StackMemory, func_ref_); }
static int JSCentralStackLimitMarginKB() {
#if defined(DEBUG) || defined(V8_USE_ADDRESS_SANITIZER)
return 80;
#else
return 40;
#endif
}
static int JSGrowableStackLimitMarginKB() {
if (!v8_flags.experimental_wasm_growable_stacks) {
return JSCentralStackLimitMarginKB();
}
static_assert(kMaxValueTypeSize == 16);
static_assert(kV8MaxWasmFunctionParams == 1000);
return 20;
}
friend class StackPool;
constexpr static uint32_t stack_switch_source_fp_offset() {
return OFFSET_OF(StackMemory, stack_switch_info_) +
OFFSET_OF(StackMemory::StackSwitchInfo, source_fp);
}
constexpr static uint32_t stack_switch_target_sp_offset() {
return OFFSET_OF(StackMemory, stack_switch_info_) +
OFFSET_OF(StackMemory::StackSwitchInfo, target_sp);
}
constexpr static uint32_t jmpbuf_offset() {
return OFFSET_OF(StackMemory, jmpbuf_);
}
constexpr static uint32_t current_continuation_offset() {
return OFFSET_OF(StackMemory, current_cont_);
}
Address central_stack_sp() const { return central_stack_sp_; }
void set_central_stack_sp(Address sp) { central_stack_sp_ = sp; }
private:
StackMemory();
StackMemory(uint8_t* limit, size_t size);
uint8_t* limit_;
size_t size_;
bool owned_;
JumpBuffer jmpbuf_;
int id_;
size_t index_;
Address central_stack_sp_ = kNullAddress;
StackSwitchInfo stack_switch_info_;
StackSegment* first_segment_ = nullptr;
StackSegment* active_segment_ = nullptr;
Tagged<WasmContinuationObject> current_cont_ = {};
Tagged<WasmFuncRef> func_ref_ = {};
};
constexpr int kStackSpOffset =
wasm::StackMemory::jmpbuf_offset() + offsetof(JumpBuffer, sp);
constexpr int kStackFpOffset =
wasm::StackMemory::jmpbuf_offset() + offsetof(JumpBuffer, fp);
constexpr int kStackPcOffset =
wasm::StackMemory::jmpbuf_offset() + offsetof(JumpBuffer, pc);
constexpr int kStackLimitOffset =
wasm::StackMemory::jmpbuf_offset() + offsetof(JumpBuffer, stack_limit);
constexpr int kStackParentOffset =
wasm::StackMemory::jmpbuf_offset() + offsetof(JumpBuffer, parent);
constexpr int kStackStateOffset =
wasm::StackMemory::jmpbuf_offset() + offsetof(JumpBuffer, state);
class StackPool {
public:
std::unique_ptr<StackMemory> GetOrAllocate();
void Add(std::unique_ptr<StackMemory> stack);
void ReleaseFinishedStacks();
size_t Size() const;
private:
std::vector<std::unique_ptr<StackMemory>> freelist_;
size_t size_ = 0;
static constexpr int kMaxSize = 4 * MB;
};
}
#endif