#include "src/execution/frames.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <sstream>
#include "src/api/api-arguments.h"
#include "src/api/api-natives.h"
#include "src/base/bits.h"
#include "src/codegen/interface-descriptors-inl.h"
#include "src/codegen/linkage-location.h"
#include "src/codegen/macro-assembler.h"
#include "src/codegen/maglev-safepoint-table.h"
#include "src/codegen/register-configuration.h"
#include "src/codegen/safepoint-table.h"
#include "src/common/globals.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/arguments.h"
#include "src/execution/frame-constants.h"
#include "src/execution/frames-inl.h"
#include "src/execution/vm-state-inl.h"
#include "src/ic/ic-stats.h"
#include "src/logging/counters.h"
#include "src/objects/casting-inl.h"
#include "src/objects/code.h"
#include "src/objects/instance-type-checker.h"
#include "src/objects/slots.h"
#include "src/objects/smi.h"
#include "src/objects/visitors.h"
#include "src/roots/roots.h"
#include "src/snapshot/embedded/embedded-data-inl.h"
#include "src/strings/string-stream.h"
#include "src/zone/zone-containers.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-wasm-objects.h"
#include "src/wasm/stacks.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-linkage.h"
#include "src/wasm/wasm-objects-inl.h"
#if V8_ENABLE_DRUMBRAKE
#include "src/wasm/interpreter/wasm-interpreter-runtime.h"
#endif
#endif
namespace v8 {
namespace internal {
ReturnAddressLocationResolver StackFrame::return_address_location_resolver_ =
nullptr;
namespace {
Address AddressOf(const StackHandler* handler) {
Address raw = handler->address();
#ifdef V8_USE_ADDRESS_SANITIZER
Address padding =
base::Memory<Address>(raw + StackHandlerConstants::kPaddingOffset);
if (padding != 0) return padding;
#endif
return raw;
}
}
#if V8_ENABLE_WEBASSEMBLY
bool IsWasmToJsWrapperCSA(Address pc) {
size_t builtin_size =
EmbeddedData::FromBlob().InstructionSizeOf(Builtin::kWasmToJsWrapperCSA);
Address wrapper_addr =
Builtins::EmbeddedEntryOf(Builtin::kWasmToJsWrapperCSA);
if (pc >= wrapper_addr && pc < wrapper_addr + builtin_size) {
return true;
}
return false;
}
#endif
class StackHandlerIterator {
public:
StackHandlerIterator(const StackFrame* frame, StackHandler* handler)
: limit_(frame->fp()), handler_(handler) {
#if V8_ENABLE_WEBASSEMBLY
#if !V8_ENABLE_DRUMBRAKE && !USE_SIMULATOR
DCHECK_IMPLIES(frame->isolate()->wasm_stacks().empty(),
frame->InFastCCall() || frame->sp() <= AddressOf(handler));
#endif
if (frame->is_c_wasm_entry()) {
handler_ = handler_->next();
#if V8_ENABLE_DRUMBRAKE
} else if (v8_flags.wasm_jitless && frame->is_wasm_to_js()) {
handler_ = handler_->next();
#ifdef USE_SIMULATOR
limit_ = 0;
#endif
#endif
}
#else
DCHECK_LE(frame->sp(), AddressOf(handler));
#endif
}
StackHandler* handler() const { return handler_; }
bool done() { return handler_ == nullptr || AddressOf(handler_) > limit_; }
void Advance() {
DCHECK(!done());
handler_ = handler_->next();
}
private:
#if V8_ENABLE_DRUMBRAKE && USE_SIMULATOR
Address limit_;
#else
const Address limit_;
#endif
StackHandler* handler_;
};
StackFrameIteratorBase::StackFrameIteratorBase(Isolate* isolate)
: isolate_(isolate), frame_(nullptr), handler_(nullptr) {}
StackFrameIterator::StackFrameIterator(Isolate* isolate)
: StackFrameIterator(isolate, isolate->thread_local_top()) {}
StackFrameIterator::StackFrameIterator(Isolate* isolate, ThreadLocalTop* t)
: StackFrameIteratorBase(isolate) {
Reset(t);
}
#if V8_ENABLE_WEBASSEMBLY
StackFrameIterator::StackFrameIterator(Isolate* isolate, ThreadLocalTop* t,
FirstStackOnly)
: StackFrameIteratorBase(isolate) {
first_stack_only_ = true;
Reset(t);
}
StackFrameIterator::StackFrameIterator(Isolate* isolate,
wasm::StackMemory* stack)
: StackFrameIteratorBase(isolate) {
first_stack_only_ = true;
Reset(isolate->thread_local_top(), stack);
}
#endif
void StackFrameIterator::Advance() {
DCHECK(!done());
StackFrame::State state;
StackFrame::Type type;
#if V8_ENABLE_WEBASSEMBLY
if (((frame_->type() == StackFrame::WASM_JSPI &&
Memory<Address>(frame_->fp() +
WasmJspiFrameConstants::kCallerFPOffset) ==
kNullAddress) ||
frame_->type() == StackFrame::WASM_STACK_ENTRY) &&
!first_stack_only_) {
wasm_stack_ = wasm_stack()->jmpbuf()->parent;
CHECK_NOT_NULL(wasm_stack_);
CHECK_EQ(wasm_stack_->jmpbuf()->state, wasm::JumpBuffer::Inactive);
WasmJspiFrame::GetStateForJumpBuffer(wasm_stack_->jmpbuf(), &state);
type = StackFrame::MarkerToType(
Memory<intptr_t>(wasm_stack_->jmpbuf()->fp +
CommonFrameConstants::kContextOrFrameTypeOffset));
DCHECK((frame_->type() == StackFrame::WASM_JSPI &&
type == StackFrame::WASM_JSPI) ||
(frame_->type() == StackFrame::WASM_STACK_ENTRY &&
type == StackFrame::WASM_STACK_EXIT));
SetNewFrame(type, &state);
return;
}
#endif
type = frame_->GetCallerState(&state);
if (frame_->type() == StackFrame::ENTRY ||
frame_->type() == StackFrame::CONSTRUCT_ENTRY
#if V8_ENABLE_WEBASSEMBLY
|| frame_->type() == StackFrame::C_WASM_ENTRY
#endif
) {
StackHandlerIterator it(frame_, handler_);
while (!it.done()) it.Advance();
handler_ = it.handler();
}
SetNewFrame(type, &state);
#if V8_ENABLE_WEBASSEMBLY
DCHECK_IMPLIES(done() && !first_stack_only_, handler_ == nullptr);
#else
DCHECK_IMPLIES(done(), handler_ == nullptr);
#endif
}
StackFrame* StackFrameIterator::Reframe() {
StackFrame::State state = frame_->state_;
StackFrame::Type type = ComputeStackFrameType(&state);
SetNewFrame(type, &state);
return frame();
}
namespace {
StackFrame::Type GetStateForFastCCallCallerFP(Isolate* isolate, Address fp,
Address pc, Address pc_address,
StackFrame::State* state) {
DCHECK_NE(kNullAddress, pc);
state->fp = fp;
state->sp = kNullAddress;
state->pc_address = reinterpret_cast<Address*>(pc_address);
state->callee_pc = kNullAddress;
state->constant_pool_address = nullptr;
#if V8_ENABLE_WEBASSEMBLY
if (wasm::WasmCode* code =
wasm::GetWasmCodeManager()->LookupCode(isolate, pc)) {
if (code->kind() == wasm::WasmCode::kWasmToJsWrapper) {
return StackFrame::WASM_TO_JS;
}
DCHECK_EQ(code->kind(), wasm::WasmCode::kWasmFunction);
return StackFrame::WASM;
}
#endif
return StackFrame::TURBOFAN_JS;
}
}
void StackFrameIterator::Reset(ThreadLocalTop* top) {
StackFrame::State state;
StackFrame::Type type;
const Address fast_c_call_caller_fp =
isolate_->isolate_data()->fast_c_call_caller_fp();
if (fast_c_call_caller_fp != kNullAddress) {
const Address caller_pc = isolate_->isolate_data()->fast_c_call_caller_pc();
const Address caller_pc_address =
isolate_->isolate_data()->fast_c_call_caller_pc_address();
type = GetStateForFastCCallCallerFP(isolate_, fast_c_call_caller_fp,
caller_pc, caller_pc_address, &state);
} else {
type = ExitFrame::GetStateForFramePointer(Isolate::c_entry_fp(top), &state);
}
#if V8_ENABLE_WEBASSEMBLY
wasm_stack_ = isolate_->isolate_data()->active_stack();
#endif
handler_ = StackHandler::FromAddress(Isolate::handler(top));
SetNewFrame(type, &state);
}
#if V8_ENABLE_WEBASSEMBLY
void StackFrameIterator::Reset(ThreadLocalTop* top, wasm::StackMemory* stack) {
if (stack->jmpbuf()->sp == stack->base() ||
stack->jmpbuf()->state == wasm::JumpBuffer::Retired) {
return;
}
StackFrame::State state;
WasmJspiFrame::GetStateForJumpBuffer(stack->jmpbuf(), &state);
handler_ = StackHandler::FromAddress(Isolate::handler(top));
wasm_stack_ = stack;
StackFrame::Type type = StackFrame::MarkerToType(
Memory<intptr_t>(wasm_stack_->jmpbuf()->fp +
CommonFrameConstants::kContextOrFrameTypeOffset));
DCHECK(type == StackFrame::WASM_JSPI || type == StackFrame::WASM_STACK_EXIT);
SetNewFrame(type, &state);
}
#endif
void StackFrameIteratorBase::SetNewFrame(StackFrame::Type type,
StackFrame::State* state) {
SetNewFrame(type);
DCHECK_EQ(!frame_, type == StackFrame::NO_FRAME_TYPE);
if (frame_) frame_->state_ = *state;
}
void StackFrameIteratorBase::SetNewFrame(StackFrame::Type type) {
switch (type) {
#define FRAME_TYPE_CASE(type, class) \
case StackFrame::type: \
frame_ = new (&class##_) class(this); \
return;
STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE)
#undef FRAME_TYPE_CASE
case StackFrame::NO_FRAME_TYPE:
case StackFrame::NUMBER_OF_TYPES:
case StackFrame::MANUAL:
break;
}
frame_ = nullptr;
}
void TypedFrameWithJSLinkage::Iterate(RootVisitor* v) const {
IterateExpressions(v);
IteratePc(v, constant_pool_address(), GcSafeLookupCode());
}
void ConstructFrame::Iterate(RootVisitor* v) const {
FullObjectSlot argc(
&Memory<Address>(fp() + ConstructFrameConstants::kLengthOffset));
const int last_object_offset = ConstructFrameConstants::kLastObjectOffset;
FullObjectSlot base(&Memory<Address>(sp()));
FullObjectSlot limit(&Memory<Address>(fp() + last_object_offset) + 1);
v->VisitRootPointers(Root::kStackRoots, nullptr, base, argc);
v->VisitRootPointers(Root::kStackRoots, nullptr, argc + 1, limit);
IteratePc(v, constant_pool_address(), GcSafeLookupCode());
}
void JavaScriptStackFrameIterator::Advance() {
do {
iterator_.Advance();
} while (!iterator_.done() && !iterator_.frame()->is_javascript());
}
DebuggableStackFrameIterator::DebuggableStackFrameIterator(Isolate* isolate)
: iterator_(isolate) {
if (!done() && !IsValidFrame(iterator_.frame())) Advance();
}
DebuggableStackFrameIterator::DebuggableStackFrameIterator(Isolate* isolate,
StackFrameId id)
: DebuggableStackFrameIterator(isolate) {
while (!done() && frame()->id() != id) Advance();
}
void DebuggableStackFrameIterator::Advance() {
do {
iterator_.Advance();
} while (!done() && !IsValidFrame(iterator_.frame()));
}
int DebuggableStackFrameIterator::FrameFunctionCount() const {
DCHECK(!done());
if (!iterator_.frame()->is_optimized_js()) return 1;
std::vector<Tagged<SharedFunctionInfo>> infos;
TurbofanJSFrame::cast(iterator_.frame())->GetFunctions(&infos);
return static_cast<int>(infos.size());
}
FrameSummary DebuggableStackFrameIterator::GetTopValidFrame() const {
DCHECK(!done());
FrameSummaries summaries = frame()->Summarize();
if (is_javascript()) {
for (int i = summaries.size() - 1; i >= 0; i--) {
const FrameSummary& summary = summaries.frames[i];
if (summary.is_subject_to_debugging()) {
return summary;
}
}
UNREACHABLE();
}
#if V8_ENABLE_WEBASSEMBLY
if (is_wasm()) return summaries.frames.back();
#endif
UNREACHABLE();
}
bool DebuggableStackFrameIterator::IsValidFrame(StackFrame* frame) {
if (frame->is_javascript()) {
Tagged<JSFunction> function =
static_cast<JavaScriptFrame*>(frame)->function();
return function->shared()->IsSubjectToDebugging();
}
#if V8_ENABLE_WEBASSEMBLY
if (frame->is_wasm()) return true;
#endif
return false;
}
namespace {
std::optional<bool> IsInterpreterFramePc(Isolate* isolate, Address pc,
StackFrame::State* state) {
Builtin builtin = OffHeapInstructionStream::TryLookupCode(isolate, pc);
if (builtin != Builtin::kNoBuiltinId &&
(builtin == Builtin::kInterpreterEntryTrampoline ||
builtin == Builtin::kInterpreterEnterAtBytecode ||
builtin == Builtin::kInterpreterEnterAtNextBytecode)) {
return true;
} else if (isolate->interpreted_frames_native_stack()) {
intptr_t marker = Memory<intptr_t>(
state->fp + CommonFrameConstants::kContextOrFrameTypeOffset);
MSAN_MEMORY_IS_INITIALIZED(
state->fp + StandardFrameConstants::kFunctionOffset,
kSystemPointerSize);
Tagged<Object> maybe_function = Tagged<Object>(
Memory<Address>(state->fp + StandardFrameConstants::kFunctionOffset));
if (StackFrame::IsTypeMarker(marker) || IsSmi(maybe_function)) {
return false;
} else if (!isolate->heap()->InSpaceSlow(pc, CODE_SPACE)) {
return false;
}
if (!ThreadIsolation::CanLookupStartOfJitAllocationAt(pc)) {
return {};
}
Tagged<Code> interpreter_entry_trampoline =
isolate->heap()->FindCodeForInnerPointer(pc);
return interpreter_entry_trampoline->is_interpreter_trampoline_builtin();
} else {
return false;
}
}
}
bool StackFrameIteratorForProfiler::IsNoFrameBytecodeHandlerPc(
Isolate* isolate, Address pc, Address fp) const {
EmbeddedData d = EmbeddedData::FromBlob(isolate);
if (pc < d.InstructionStartOfBytecodeHandlers() ||
pc >= d.InstructionEndOfBytecodeHandlers()) {
return false;
}
Address frame_type_address =
fp + CommonFrameConstants::kContextOrFrameTypeOffset;
if (!IsValidStackAddress(frame_type_address)) {
return false;
}
MSAN_MEMORY_IS_INITIALIZED(frame_type_address, kSystemPointerSize);
intptr_t marker = Memory<intptr_t>(frame_type_address);
if (StackFrame::IsTypeMarker(marker) &&
StackFrame::MarkerToType(marker) == StackFrame::STUB) {
return false;
}
return true;
}
StackFrameIteratorForProfiler::StackFrameIteratorForProfiler(
Isolate* isolate, Address pc, Address fp, Address sp, Address lr,
Address js_entry_sp)
: StackFrameIteratorBase(isolate),
low_bound_(sp),
high_bound_(js_entry_sp),
top_frame_type_(StackFrame::NO_FRAME_TYPE),
external_callback_scope_(isolate->external_callback_scope()),
top_link_register_(lr)
#if V8_ENABLE_WEBASSEMBLY
,
wasm_stacks_(isolate->wasm_stacks())
#endif
{
if (!isolate->isolate_data()->stack_is_iterable()) {
DCHECK(done());
return;
}
static constexpr StackFrame::Type kTypeForAdvance = StackFrame::TURBOFAN_JS;
StackFrame::State state;
state.is_profiler_entry_frame = true;
StackFrame::Type type;
ThreadLocalTop* const top = isolate->thread_local_top();
bool advance_frame = true;
const Address fast_c_fp = isolate->isolate_data()->fast_c_call_caller_fp();
if (fast_c_fp != kNullAddress) {
DCHECK_NE(kNullAddress, isolate->isolate_data()->fast_c_call_caller_pc());
state.fp = fast_c_fp;
state.sp = sp;
state.pc_address = reinterpret_cast<Address*>(
isolate->isolate_data()->fast_c_call_caller_pc_address());
static_assert(StandardFrameConstants::kFunctionOffset <
StandardFrameConstants::kContextOffset);
if (IsValidStackAddress(state.fp +
StandardFrameConstants::kFunctionOffset)) {
type = ComputeStackFrameType(&state);
if (IsValidFrameType(type)) {
top_frame_type_ = type;
advance_frame = false;
}
} else {
type = kTypeForAdvance;
}
} else if (IsValidTop(top)) {
type = ExitFrame::GetStateForFramePointer(Isolate::c_entry_fp(top), &state);
top_frame_type_ = type;
} else if (IsValidStackAddress(fp)) {
DCHECK_NE(fp, kNullAddress);
state.fp = fp;
state.sp = sp;
state.pc_address =
StackFrame::ResolveReturnAddressLocation(reinterpret_cast<Address*>(
fp + StandardFrameConstants::kCallerPCOffset));
bool can_lookup_frame_type =
(sp < fp) &&
(fp - sp) >= TypedFrameConstants::kFixedFrameSizeFromFp;
bool is_no_frame_bytecode_handler = false;
if (can_lookup_frame_type && IsNoFrameBytecodeHandlerPc(isolate, pc, fp)) {
Address* top_location = nullptr;
if (top_link_register_) {
top_location = &top_link_register_;
} else if (IsValidStackAddress(sp)) {
MSAN_MEMORY_IS_INITIALIZED(sp, kSystemPointerSize);
top_location = reinterpret_cast<Address*>(sp);
}
std::optional<bool> is_interpreter_frame_pc =
IsInterpreterFramePc(isolate, *top_location, &state);
if (!is_interpreter_frame_pc.has_value()) {
can_lookup_frame_type = false;
} else if (is_interpreter_frame_pc.value()) {
state.pc_address = top_location;
is_no_frame_bytecode_handler = true;
advance_frame = false;
}
}
static_assert(StandardFrameConstants::kFunctionOffset <
StandardFrameConstants::kContextOffset);
Address function_slot = fp + StandardFrameConstants::kFunctionOffset;
if (!can_lookup_frame_type) {
type = StackFrame::NO_FRAME_TYPE;
} else if (IsValidStackAddress(function_slot)) {
if (is_no_frame_bytecode_handler) {
type = StackFrame::INTERPRETED;
} else {
type = ComputeStackFrameType(&state);
}
top_frame_type_ = type;
} else {
type = kTypeForAdvance;
}
} else {
DCHECK(done());
return;
}
SetNewFrame(type, &state);
if (advance_frame && !done()) {
Advance();
}
}
bool StackFrameIteratorForProfiler::IsValidTop(ThreadLocalTop* top) const {
Address c_entry_fp = Isolate::c_entry_fp(top);
if (!IsValidExitFrame(c_entry_fp)) return false;
Address handler = Isolate::handler(top);
return handler != kNullAddress;
}
void StackFrameIteratorForProfiler::AdvanceOneFrame() {
DCHECK(!done());
StackFrame* last_frame = frame_;
Address last_sp = last_frame->sp(), last_fp = last_frame->fp();
if (!IsValidState(last_frame->state_) ||
!HasValidExitIfEntryFrame(last_frame)) {
frame_ = nullptr;
return;
}
StackFrame::State state;
last_frame->ComputeCallerState(&state);
if (!IsValidState(state)) {
frame_ = nullptr;
return;
}
StackFrame::Type type = ComputeStackFrameType(&state);
SetNewFrame(type, &state);
if (!frame_) return;
if (frame_->sp() <= last_sp || frame_->fp() <= last_fp) {
frame_ = nullptr;
}
}
bool StackFrameIteratorForProfiler::IsValidState(
const StackFrame::State& state) const {
return IsValidStackAddress(state.sp) && IsValidStackAddress(state.fp);
}
bool StackFrameIteratorForProfiler::HasValidExitIfEntryFrame(
const StackFrame* frame) const {
if (!frame->is_entry() && !frame->is_construct_entry()) return true;
Address next_exit_frame_fp_address =
frame->fp() + EntryFrameConstants::kNextExitFrameFPOffset;
MSAN_MEMORY_IS_INITIALIZED(next_exit_frame_fp_address, kSystemPointerSize);
Address next_exit_frame_fp = Memory<Address>(next_exit_frame_fp_address);
return IsValidExitFrame(next_exit_frame_fp);
}
bool StackFrameIteratorForProfiler::IsValidExitFrame(Address fp) const {
if (!IsAligned(fp, kSystemPointerSize)) return false;
if (!IsValidStackAddress(fp)) return false;
Address sp = ExitFrame::ComputeStackPointer(fp);
if (!IsValidStackAddress(sp)) return false;
StackFrame::State state;
ExitFrame::FillState(fp, sp, &state);
MSAN_MEMORY_IS_INITIALIZED(state.pc_address, sizeof(state.pc_address));
return *state.pc_address != kNullAddress;
}
void StackFrameIteratorForProfiler::Advance() {
while (true) {
AdvanceOneFrame();
if (done()) break;
ExternalCallbackScope* last_callback_scope = nullptr;
while (external_callback_scope_ != nullptr &&
external_callback_scope_->JSStackComparableAddress() <
frame_->fp()) {
last_callback_scope = external_callback_scope_;
external_callback_scope_ = external_callback_scope_->previous();
}
if (frame_->is_javascript()) break;
#if V8_ENABLE_WEBASSEMBLY
if (frame_->is_wasm() || frame_->is_wasm_to_js() ||
frame_->is_js_to_wasm()) {
break;
}
#endif
if (frame_->is_exit() || frame_->is_builtin_exit() ||
frame_->is_api_accessor_exit() || frame_->is_api_callback_exit()) {
if (last_callback_scope) {
frame_->state_.pc_address =
last_callback_scope->callback_entrypoint_address();
}
break;
}
}
}
StackFrameIteratorForProfilerForTesting::
StackFrameIteratorForProfilerForTesting(Isolate* isolate, Address pc,
Address fp, Address sp, Address lr,
Address js_entry_sp)
: StackFrameIteratorForProfiler(isolate, pc, fp, sp, lr, js_entry_sp) {}
void StackFrameIteratorForProfilerForTesting::Advance() {
StackFrameIteratorForProfiler::Advance();
}
namespace {
std::optional<Tagged<GcSafeCode>> GetContainingCode(Isolate* isolate,
Address pc) {
return isolate->inner_pointer_to_code_cache()->GetCacheEntry(pc)->code;
}
}
Tagged<GcSafeCode> StackFrame::GcSafeLookupCode() const {
return GcSafeLookupCodeAndOffset().first;
}
std::pair<Tagged<GcSafeCode>, int> StackFrame::GcSafeLookupCodeAndOffset()
const {
const Address pc = maybe_unauthenticated_pc();
std::optional<Tagged<GcSafeCode>> result = GetContainingCode(isolate(), pc);
return {result.value(),
result.value()->GetOffsetFromInstructionStart(isolate(), pc)};
}
Tagged<Code> StackFrame::LookupCode() const {
DCHECK_NE(isolate()->heap()->gc_state(), Heap::MARK_COMPACT);
return GcSafeLookupCode()->UnsafeCastToCode();
}
std::pair<Tagged<Code>, int> StackFrame::LookupCodeAndOffset() const {
DCHECK_NE(isolate()->heap()->gc_state(), Heap::MARK_COMPACT);
auto gc_safe_pair = GcSafeLookupCodeAndOffset();
return {gc_safe_pair.first->UnsafeCastToCode(), gc_safe_pair.second};
}
void StackFrame::IteratePc(RootVisitor* v, Address* constant_pool_address,
Tagged<GcSafeCode> holder) const {
#if DEBUG
const Address old_pc = maybe_unauthenticated_pc();
DCHECK_GE(old_pc, holder->InstructionStart(isolate(), old_pc));
DCHECK_LT(old_pc, holder->InstructionEnd(isolate(), old_pc));
#endif
Tagged<GcSafeCode> visited_holder = holder;
PtrComprCageBase code_cage_base{isolate()->code_cage_base()};
const Tagged<Object> old_istream =
holder->raw_instruction_stream(code_cage_base);
Tagged<Object> visited_istream = old_istream;
v->VisitRunningCode(FullObjectSlot{&visited_holder},
FullObjectSlot{&visited_istream});
CHECK_EQ(visited_istream, old_istream);
}
void StackFrame::SetReturnAddressLocationResolver(
ReturnAddressLocationResolver resolver) {
DCHECK_NULL(return_address_location_resolver_);
return_address_location_resolver_ = resolver;
}
namespace {
StackFrame::Type ComputeBuiltinFrameType(Tagged<GcSafeCode> code) {
if (code->is_interpreter_trampoline_builtin() ||
code->is_baseline_trampoline_builtin()) {
return StackFrame::INTERPRETED;
} else if (code->is_baseline_leave_frame_builtin()) {
return StackFrame::BASELINE;
} else if (code->is_turbofanned()) {
return StackFrame::TURBOFAN_JS;
}
return StackFrame::BUILTIN;
}
StackFrame::Type SafeStackFrameType(StackFrame::Type candidate) {
DCHECK_LE(static_cast<uintptr_t>(candidate), StackFrame::NUMBER_OF_TYPES);
switch (candidate) {
case StackFrame::API_ACCESSOR_EXIT:
case StackFrame::API_CALLBACK_EXIT:
case StackFrame::BUILTIN_CONTINUATION:
case StackFrame::BUILTIN_EXIT:
case StackFrame::CONSTRUCT:
case StackFrame::FAST_CONSTRUCT:
case StackFrame::CONSTRUCT_ENTRY:
case StackFrame::ENTRY:
case StackFrame::EXIT:
case StackFrame::INTERNAL:
case StackFrame::IRREGEXP:
case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION:
case StackFrame::JAVASCRIPT_BUILTIN_CONTINUATION_WITH_CATCH:
case StackFrame::STUB:
return candidate;
#if V8_ENABLE_WEBASSEMBLY
case StackFrame::JS_TO_WASM:
case StackFrame::WASM_JSPI:
case StackFrame::WASM:
case StackFrame::WASM_DEBUG_BREAK:
case StackFrame::WASM_EXIT:
case StackFrame::WASM_LIFTOFF_SETUP:
case StackFrame::WASM_TO_JS:
case StackFrame::WASM_SEGMENT_START:
case StackFrame::WASM_STACK_ENTRY:
case StackFrame::WASM_STACK_EXIT:
#if V8_ENABLE_DRUMBRAKE
case StackFrame::C_WASM_ENTRY:
case StackFrame::WASM_INTERPRETER_ENTRY:
#endif
return candidate;
#endif
case StackFrame::BASELINE:
case StackFrame::BUILTIN:
case StackFrame::INTERPRETED:
case StackFrame::MAGLEV:
case StackFrame::MANUAL:
case StackFrame::NATIVE:
case StackFrame::NO_FRAME_TYPE:
case StackFrame::NUMBER_OF_TYPES:
case StackFrame::TURBOFAN_JS:
case StackFrame::TURBOFAN_STUB_WITH_CONTEXT:
#if V8_ENABLE_WEBASSEMBLY && !V8_ENABLE_DRUMBRAKE
case StackFrame::C_WASM_ENTRY:
#endif
return StackFrame::NATIVE;
}
UNREACHABLE();
}
}
StackFrame::Type StackFrameIterator::ComputeStackFrameType(
StackFrame::State* state) const {
#if V8_ENABLE_WEBASSEMBLY
if (state->fp == kNullAddress && first_stack_only_) {
DCHECK(!isolate_->wasm_stacks().empty());
return StackFrame::NO_FRAME_TYPE;
}
#endif
const Address pc = StackFrame::ReadPC(state->pc_address);
#if V8_ENABLE_WEBASSEMBLY
if (wasm::WasmCode* wasm_code =
wasm::GetWasmCodeManager()->LookupCode(isolate(), pc)) {
switch (wasm_code->kind()) {
case wasm::WasmCode::kWasmFunction:
return StackFrame::WASM;
case wasm::WasmCode::kWasmToCapiWrapper:
return StackFrame::WASM_EXIT;
case wasm::WasmCode::kWasmToJsWrapper:
return StackFrame::WASM_TO_JS;
case wasm::WasmCode::kWasmStackEntryWrapper:
return StackFrame::WASM_STACK_ENTRY;
#if V8_ENABLE_DRUMBRAKE
case wasm::WasmCode::kInterpreterEntry:
return StackFrame::WASM_INTERPRETER_ENTRY;
#endif
default:
UNREACHABLE();
}
}
#endif
std::optional<Tagged<GcSafeCode>> lookup_result =
GetContainingCode(isolate(), pc);
if (!lookup_result.has_value()) return StackFrame::NATIVE;
MSAN_MEMORY_IS_INITIALIZED(
state->fp + CommonFrameConstants::kContextOrFrameTypeOffset,
kSystemPointerSize);
const intptr_t marker = Memory<intptr_t>(
state->fp + CommonFrameConstants::kContextOrFrameTypeOffset);
switch (lookup_result.value()->kind()) {
case CodeKind::BUILTIN:
case CodeKind::FOR_TESTING:
case CodeKind::FOR_TESTING_JS: {
if (StackFrame::IsTypeMarker(marker)) break;
return ComputeBuiltinFrameType(lookup_result.value());
}
case CodeKind::BASELINE:
if (lookup_result.value()->marked_for_deoptimization())
return StackFrame::INTERPRETED;
return StackFrame::BASELINE;
case CodeKind::MAGLEV:
if (StackFrame::IsTypeMarker(marker)) {
DCHECK_EQ(StackFrame::MarkerToType(marker), StackFrame::INTERNAL);
return StackFrame::INTERNAL;
}
return StackFrame::MAGLEV;
case CodeKind::TURBOFAN_JS:
return StackFrame::TURBOFAN_JS;
#if V8_ENABLE_WEBASSEMBLY
case CodeKind::JS_TO_WASM_FUNCTION:
if (lookup_result.value()->builtin_id() == Builtin::kJSToWasmWrapperAsm) {
return StackFrame::JS_TO_WASM;
}
#if V8_ENABLE_DRUMBRAKE
if (lookup_result.value()->builtin_id() ==
Builtin::kGenericJSToWasmInterpreterWrapper) {
return StackFrame::JS_TO_WASM;
}
#endif
return StackFrame::TURBOFAN_STUB_WITH_CONTEXT;
case CodeKind::C_WASM_ENTRY:
return StackFrame::C_WASM_ENTRY;
case CodeKind::WASM_TO_JS_FUNCTION:
case CodeKind::WASM_STACK_ENTRY:
UNREACHABLE();
case CodeKind::WASM_FUNCTION:
case CodeKind::WASM_TO_CAPI_FUNCTION:
UNREACHABLE();
#else
case CodeKind::C_WASM_ENTRY:
case CodeKind::JS_TO_WASM_FUNCTION:
case CodeKind::WASM_FUNCTION:
case CodeKind::WASM_TO_CAPI_FUNCTION:
case CodeKind::WASM_TO_JS_FUNCTION:
case CodeKind::WASM_STACK_ENTRY:
UNREACHABLE();
#endif
case CodeKind::BYTECODE_HANDLER:
case CodeKind::REGEXP:
case CodeKind::INTERPRETED_FUNCTION:
break;
}
return SafeStackFrameType(StackFrame::MarkerToType(marker));
}
StackFrame::Type StackFrameIteratorForProfiler::ComputeStackFrameType(
StackFrame::State* state) const {
#if V8_ENABLE_WEBASSEMBLY
if (state->fp == kNullAddress) {
DCHECK(!isolate_->wasm_stacks().empty());
return StackFrame::NO_FRAME_TYPE;
}
#endif
const Address pc = StackFrame::unauthenticated_pc(state->pc_address);
#if V8_ENABLE_WEBASSEMBLY
if (IsWasmToJsWrapperCSA(pc)) {
return StackFrame::WASM_TO_JS;
}
#endif
MSAN_MEMORY_IS_INITIALIZED(
state->fp + CommonFrameConstants::kContextOrFrameTypeOffset,
kSystemPointerSize);
const intptr_t marker = Memory<intptr_t>(
state->fp + CommonFrameConstants::kContextOrFrameTypeOffset);
if (StackFrame::IsTypeMarker(marker)) {
return SafeStackFrameType(StackFrame::MarkerToType(marker));
}
MSAN_MEMORY_IS_INITIALIZED(
state->fp + StandardFrameConstants::kFunctionOffset, kSystemPointerSize);
Tagged<Object> maybe_function = Tagged<Object>(
Memory<Address>(state->fp + StandardFrameConstants::kFunctionOffset));
if (IsSmi(maybe_function)) {
return StackFrame::NATIVE;
}
std::optional<bool> is_interpreter_frame =
IsInterpreterFramePc(isolate(), pc, state);
if (!is_interpreter_frame.has_value()) {
return StackFrame::NO_FRAME_TYPE;
}
if (is_interpreter_frame.value()) {
return StackFrame::INTERPRETED;
}
return StackFrame::TURBOFAN_JS;
}
StackFrame::Type StackFrame::GetCallerState(State* state) const {
ComputeCallerState(state);
return iterator_->ComputeStackFrameType(state);
}
Address CommonFrame::GetCallerStackPointer() const {
return fp() + CommonFrameConstants::kCallerSPOffset;
}
void NativeFrame::ComputeCallerState(State* state) const {
state->sp = caller_sp();
state->fp = Memory<Address>(fp() + CommonFrameConstants::kCallerFPOffset);
state->pc_address = ResolveReturnAddressLocation(
reinterpret_cast<Address*>(fp() + CommonFrameConstants::kCallerPCOffset));
state->callee_pc = kNullAddress;
state->constant_pool_address = nullptr;
}
Tagged<HeapObject> EntryFrame::unchecked_code() const {
return isolate()->builtins()->code(Builtin::kJSEntry);
}
void EntryFrame::ComputeCallerState(State* state) const {
GetCallerState(state);
}
StackFrame::Type EntryFrame::GetCallerState(State* state) const {
const Address fast_c_call_caller_fp =
Memory<Address>(fp() + EntryFrameConstants::kNextFastCallFrameFPOffset);
if (fast_c_call_caller_fp != kNullAddress) {
Address caller_pc_address =
fp() + EntryFrameConstants::kNextFastCallFramePCOffset;
Address caller_pc = Memory<Address>(caller_pc_address);
return GetStateForFastCCallCallerFP(isolate(), fast_c_call_caller_fp,
caller_pc, caller_pc_address, state);
}
Address next_exit_frame_fp =
Memory<Address>(fp() + EntryFrameConstants::kNextExitFrameFPOffset);
return ExitFrame::GetStateForFramePointer(next_exit_frame_fp, state);
}
#if V8_ENABLE_WEBASSEMBLY
StackFrame::Type CWasmEntryFrame::GetCallerState(State* state) const {
const int offset = CWasmEntryFrameConstants::kCEntryFPOffset;
Address fp = Memory<Address>(this->fp() + offset);
return ExitFrame::GetStateForFramePointer(fp, state);
}
#if V8_ENABLE_DRUMBRAKE
void CWasmEntryFrame::Iterate(RootVisitor* v) const {
if (!v8_flags.wasm_jitless) {
StubFrame::Iterate(v);
}
}
#endif
#endif
Tagged<HeapObject> ConstructEntryFrame::unchecked_code() const {
return isolate()->builtins()->code(Builtin::kJSConstructEntry);
}
void ExitFrame::ComputeCallerState(State* state) const {
state->sp = caller_sp();
state->fp = Memory<Address>(fp() + ExitFrameConstants::kCallerFPOffset);
state->pc_address = ResolveReturnAddressLocation(
reinterpret_cast<Address*>(fp() + ExitFrameConstants::kCallerPCOffset));
state->callee_pc = kNullAddress;
if (V8_EMBEDDED_CONSTANT_POOL_BOOL) {
state->constant_pool_address = reinterpret_cast<Address*>(
fp() + ExitFrameConstants::kConstantPoolOffset);
}
}
void ExitFrame::Iterate(RootVisitor* v) const {
IteratePc(v, constant_pool_address(), GcSafeLookupCode());
}
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
if (fp == 0) return NO_FRAME_TYPE;
StackFrame::Type type = ComputeFrameType(fp);
#if V8_ENABLE_WEBASSEMBLY
Address sp = type == WASM_EXIT ? WasmExitFrame::ComputeStackPointer(fp)
: ExitFrame::ComputeStackPointer(fp);
#else
Address sp = ExitFrame::ComputeStackPointer(fp);
#endif
FillState(fp, sp, state);
DCHECK_NE(*state->pc_address, kNullAddress);
return type;
}
StackFrame::Type ExitFrame::ComputeFrameType(Address fp) {
const int offset = ExitFrameConstants::kFrameTypeOffset;
Tagged<Object> marker(Memory<Address>(fp + offset));
if (!IsSmi(marker)) {
return EXIT;
}
intptr_t marker_int = base::bit_cast<intptr_t>(marker);
StackFrame::Type frame_type = static_cast<StackFrame::Type>(marker_int >> 1);
switch (frame_type) {
case BUILTIN_EXIT:
case API_ACCESSOR_EXIT:
case API_CALLBACK_EXIT:
#if V8_ENABLE_WEBASSEMBLY
case WASM_EXIT:
case WASM_JSPI:
case WASM_STACK_EXIT:
#endif
return frame_type;
default:
return EXIT;
}
}
Address ExitFrame::ComputeStackPointer(Address fp) {
MSAN_MEMORY_IS_INITIALIZED(fp + ExitFrameConstants::kSPOffset,
kSystemPointerSize);
return Memory<Address>(fp + ExitFrameConstants::kSPOffset);
}
#if V8_ENABLE_WEBASSEMBLY
Address WasmExitFrame::ComputeStackPointer(Address fp) {
Address sp = fp + WasmExitFrameConstants::kWasmInstanceDataOffset;
DCHECK_EQ(sp - 1 * kPCOnStackSize,
fp + WasmExitFrameConstants::kCallingPCOffset);
return sp;
}
#endif
void ExitFrame::FillState(Address fp, Address sp, State* state) {
state->sp = sp;
state->fp = fp;
state->pc_address = ResolveReturnAddressLocation(
reinterpret_cast<Address*>(sp - 1 * kPCOnStackSize));
state->callee_pc = kNullAddress;
state->constant_pool_address = nullptr;
}
FrameSummaries BuiltinExitFrame::Summarize() const {
DirectHandle<FixedArray> parameters = GetParameters();
DisallowGarbageCollection no_gc;
Tagged<Code> code;
int code_offset = -1;
std::tie(code, code_offset) = LookupCodeAndOffset();
FrameSummary::JavaScriptFrameSummary summary(
isolate(), receiver(), function(), Cast<AbstractCode>(code), code_offset,
IsConstructor(), *parameters);
return FrameSummaries(summary);
}
Tagged<JSFunction> BuiltinExitFrame::function() const {
return Cast<JSFunction>(target_slot_object());
}
Tagged<Object> BuiltinExitFrame::receiver() const {
return receiver_slot_object();
}
Tagged<Object> BuiltinExitFrame::GetParameter(int i) const {
DCHECK(i >= 0 && i < ComputeParametersCount());
int offset =
BuiltinExitFrameConstants::kFirstArgumentOffset + i * kSystemPointerSize;
return Tagged<Object>(Memory<Address>(fp() + offset));
}
int BuiltinExitFrame::ComputeParametersCount() const {
Tagged<Object> argc_slot = argc_slot_object();
DCHECK(IsSmi(argc_slot));
int argc = Smi::ToInt(argc_slot) -
BuiltinExitFrameConstants::kNumExtraArgsWithReceiver;
DCHECK_GE(argc, 0);
return argc;
}
DirectHandle<FixedArray> BuiltinExitFrame::GetParameters() const {
if (V8_LIKELY(!v8_flags.detailed_error_stack_trace)) {
return isolate()->factory()->empty_fixed_array();
}
int param_count = ComputeParametersCount();
auto parameters = isolate()->factory()->NewFixedArray(param_count);
for (int i = 0; i < param_count; i++) {
parameters->set(i, GetParameter(i));
}
return parameters;
}
bool BuiltinExitFrame::IsConstructor() const {
return !IsUndefined(new_target_slot_object(), isolate());
}
namespace ensure_layout {
using FC = ApiCallbackExitFrameConstants;
using FCA = FunctionCallbackArguments;
static_assert(FC::kFunctionCallbackInfoContextIndex == FCA::kContextIndex);
static_assert(FC::kFunctionCallbackInfoReturnValueIndex ==
FCA::kReturnValueIndex);
static_assert(FC::kFunctionCallbackInfoTargetIndex == FCA::kTargetIndex);
static_assert(FC::kFunctionCallbackInfoNewTargetIndex == FCA::kNewTargetIndex);
static_assert(FC::kFunctionCallbackInfoArgsLength == FCA::kArgsLength);
}
DirectHandle<JSFunction> ApiCallbackExitFrame::GetFunction() const {
Tagged<HeapObject> maybe_function = target();
if (IsJSFunction(maybe_function)) {
return DirectHandle<JSFunction>::FromSlot(target_slot().location());
}
DCHECK(IsFunctionTemplateInfo(maybe_function));
DirectHandle<FunctionTemplateInfo> function_template_info(
Cast<FunctionTemplateInfo>(maybe_function), isolate());
DCHECK(IsContext(context()));
DirectHandle<NativeContext> native_context(
Cast<Context>(context())->native_context(), isolate());
DirectHandle<JSFunction> function =
ApiNatives::InstantiateFunction(isolate(), native_context,
function_template_info)
.ToHandleChecked();
set_target(*function);
return function;
}
DirectHandle<FunctionTemplateInfo>
ApiCallbackExitFrame::GetFunctionTemplateInfo() const {
Tagged<HeapObject> maybe_function = target();
if (IsJSFunction(maybe_function)) {
Tagged<SharedFunctionInfo> shared_info =
Cast<JSFunction>(maybe_function)->shared();
DCHECK(shared_info->IsApiFunction());
return direct_handle(shared_info->api_func_data(), isolate());
}
DCHECK(IsFunctionTemplateInfo(maybe_function));
return direct_handle(Cast<FunctionTemplateInfo>(maybe_function), isolate());
}
DirectHandle<FixedArray> ApiCallbackExitFrame::GetParameters() const {
if (V8_LIKELY(!v8_flags.detailed_error_stack_trace)) {
return isolate()->factory()->empty_fixed_array();
}
int param_count = ComputeParametersCount();
auto parameters = isolate()->factory()->NewFixedArray(param_count);
for (int i = 0; i < param_count; i++) {
parameters->set(i, GetParameter(i));
}
return parameters;
}
FrameSummaries ApiCallbackExitFrame::Summarize() const {
DirectHandle<FixedArray> parameters = GetParameters();
DirectHandle<JSFunction> function = GetFunction();
DisallowGarbageCollection no_gc;
Tagged<Code> code;
int code_offset = -1;
std::tie(code, code_offset) = LookupCodeAndOffset();
FrameSummary::JavaScriptFrameSummary summary(
isolate(), receiver(), *function, Cast<AbstractCode>(code), code_offset,
IsConstructor(), *parameters);
return FrameSummaries(summary);
}
static_assert(
ApiAccessorExitFrameConstants::kPropertyCallbackInfoPropertyKeyIndex ==
PropertyCallbackArguments::kPropertyKeyIndex);
static_assert(
ApiAccessorExitFrameConstants::kPropertyCallbackInfoReturnValueIndex ==
PropertyCallbackArguments::kReturnValueIndex);
static_assert(
ApiAccessorExitFrameConstants::kPropertyCallbackInfoReceiverIndex ==
PropertyCallbackArguments::kThisIndex);
static_assert(ApiAccessorExitFrameConstants::kPropertyCallbackInfoHolderIndex ==
PropertyCallbackArguments::kHolderIndex);
static_assert(ApiAccessorExitFrameConstants::kPropertyCallbackInfoArgsLength ==
PropertyCallbackArguments::kArgsLength);
FrameSummaries ApiAccessorExitFrame::Summarize() const {
DCHECK(IsName(property_name()));
DCHECK(IsJSReceiver(receiver()));
DCHECK(IsJSReceiver(holder()));
return FrameSummaries();
}
namespace {
void PrintIndex(StringStream* accumulator, StackFrame::PrintMode mode,
int index) {
accumulator->Add((mode == StackFrame::OVERVIEW) ? "%5d: " : "[%d]: ", index);
}
const char* StringForStackFrameType(StackFrame::Type type) {
switch (type) {
#define CASE(value, name) \
case StackFrame::value: \
return #name;
STACK_FRAME_TYPE_LIST(CASE)
#undef CASE
case StackFrame::NO_FRAME_TYPE:
return "NoFrameType";
default:
UNREACHABLE();
}
}
}
void StackFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
DisallowGarbageCollection no_gc;
PrintIndex(accumulator, mode, index);
accumulator->Add(StringForStackFrameType(type()));
accumulator->Add(" [pc: %p]\n",
reinterpret_cast<void*>(maybe_unauthenticated_pc()));
}
void BuiltinExitFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
DisallowGarbageCollection no_gc;
Tagged<Object> receiver = this->receiver();
Tagged<JSFunction> function = this->function();
Tagged<SharedFunctionInfo> sfi = function->shared();
accumulator->PrintSecurityTokenIfChanged(isolate(), function);
PrintIndex(accumulator, mode, index);
accumulator->Add("BuiltinExitFrame ");
if (sfi->HasBuiltinId()) {
accumulator->Add("[builtin: %s] ", Builtins::name(sfi->builtin_id()));
}
if (IsConstructor()) accumulator->Add("new ");
accumulator->PrintFunction(isolate(), function, receiver);
accumulator->Add("(this=%o", receiver);
int parameters_count = ComputeParametersCount();
for (int i = 0; i < parameters_count; i++) {
accumulator->Add(",%o", GetParameter(i));
}
accumulator->Add(")\n");
}
void ApiCallbackExitFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
DirectHandle<JSFunction> function = GetFunction();
DisallowGarbageCollection no_gc;
Tagged<Object> receiver = this->receiver();
accumulator->PrintSecurityTokenIfChanged(isolate(), *function);
PrintIndex(accumulator, mode, index);
accumulator->Add("ApiCallbackExitFrame ");
if (IsConstructor()) accumulator->Add("new ");
accumulator->PrintFunction(isolate(), *function, receiver);
accumulator->Add("(this=%o", receiver);
int parameters_count = ComputeParametersCount();
for (int i = 0; i < parameters_count; i++) {
accumulator->Add(",%o", GetParameter(i));
}
accumulator->Add(")\n\n");
}
void ApiAccessorExitFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
DisallowGarbageCollection no_gc;
PrintIndex(accumulator, mode, index);
accumulator->Add("api accessor exit frame: ");
Tagged<Name> name = property_name();
Tagged<Object> receiver = this->receiver();
Tagged<Object> holder = this->holder();
accumulator->Add("(this=%o, holder=%o, name=%o)\n", receiver, holder, name);
}
Address CommonFrame::GetExpressionAddress(int n) const {
const int offset = StandardFrameConstants::kExpressionsOffset;
return fp() + offset - n * kSystemPointerSize;
}
Address UnoptimizedJSFrame::GetExpressionAddress(int n) const {
const int offset = UnoptimizedFrameConstants::kExpressionsOffset;
return fp() + offset - n * kSystemPointerSize;
}
Tagged<Object> CommonFrame::context() const {
return ReadOnlyRoots(isolate()).undefined_value();
}
int CommonFrame::position() const {
Tagged<Code> code;
int code_offset = -1;
std::tie(code, code_offset) = LookupCodeAndOffset();
return code->SourcePosition(code_offset);
}
int CommonFrame::ComputeExpressionsCount() const {
Address base = GetExpressionAddress(0);
Address limit = sp() - kSystemPointerSize;
DCHECK(base >= limit);
return static_cast<int>((base - limit) / kSystemPointerSize);
}
void CommonFrame::ComputeCallerState(State* state) const {
state->fp = caller_fp();
#if V8_ENABLE_WEBASSEMBLY
if (state->fp == kNullAddress) {
return;
}
#endif
state->sp = caller_sp();
state->pc_address = ResolveReturnAddressLocation(reinterpret_cast<Address*>(
fp() + StandardFrameConstants::kCallerPCOffset));
state->callee_fp = fp();
state->callee_pc = maybe_unauthenticated_pc();
state->constant_pool_address = reinterpret_cast<Address*>(
fp() + StandardFrameConstants::kConstantPoolOffset);
}
FrameSummaries CommonFrame::Summarize() const {
UNREACHABLE();
}
namespace {
void VisitSpillSlot(Isolate* isolate, RootVisitor* v,
FullObjectSlot spill_slot) {
#ifdef V8_COMPRESS_POINTERS
PtrComprCageBase cage_base(isolate);
bool was_compressed = false;
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
Address value = *spill_slot.location();
if (!HAS_SMI_TAG(value) && value <= 0xffffffff) {
was_compressed = true;
*spill_slot.location() = V8HeapCompressionScheme::DecompressTagged(
static_cast<Tagged_t>(value));
if (DEBUG_BOOL) {
Tagged<HeapObject> raw =
Cast<HeapObject>(Tagged<Object>(*spill_slot.location()));
if (!IsAnyHole(raw)) {
MapWord map_word = raw->map_word(cage_base, kRelaxedLoad);
Tagged<HeapObject> forwarded = map_word.IsForwardingAddress()
? map_word.ToForwardingAddress(raw)
: raw;
bool is_self_forwarded =
HeapLayout::IsSelfForwarded(forwarded, cage_base);
if (is_self_forwarded) {
const MemoryChunk* chunk = MemoryChunk::FromHeapObject(forwarded);
CHECK(chunk->InNewLargeObjectSpace() ||
chunk->Metadata(isolate)->is_quarantined());
} else {
Tagged<HeapObject> forwarded_map = forwarded->map(cage_base);
MapWord fwd_map_map_word =
forwarded_map->map_word(cage_base, kRelaxedLoad);
if (fwd_map_map_word.IsForwardingAddress()) {
forwarded_map =
fwd_map_map_word.ToForwardingAddress(forwarded_map);
}
CHECK(IsMap(forwarded_map, cage_base));
}
}
}
}
} else {
Address slot_contents = *spill_slot.location();
Tagged_t compressed_value = static_cast<Tagged_t>(slot_contents);
if (!HAS_SMI_TAG(compressed_value)) {
was_compressed = slot_contents <= 0xFFFFFFFF;
*spill_slot.location() =
V8HeapCompressionScheme::DecompressTagged(compressed_value);
}
}
#endif
v->VisitRootPointer(Root::kStackRoots, nullptr, spill_slot);
#if V8_COMPRESS_POINTERS
if (was_compressed) {
*spill_slot.location() =
V8HeapCompressionScheme::CompressObject(*spill_slot.location());
}
#endif
}
void VisitSpillSlots(Isolate* isolate, RootVisitor* v,
FullObjectSlot first_slot_offset,
base::Vector<const uint8_t> tagged_slots) {
FullObjectSlot slot_offset = first_slot_offset;
for (uint8_t bits : tagged_slots) {
while (bits) {
const int bit = base::bits::CountTrailingZeros(bits);
bits &= ~(1 << bit);
FullObjectSlot spill_slot = slot_offset + bit;
VisitSpillSlot(isolate, v, spill_slot);
}
slot_offset += kBitsPerByte;
}
}
SafepointEntry GetSafepointEntryFromCodeCache(
Isolate* isolate, Address inner_pointer,
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry) {
if (!entry->safepoint_entry.is_initialized()) {
entry->safepoint_entry =
SafepointTable::FindEntry(isolate, entry->code.value(), inner_pointer);
DCHECK(entry->safepoint_entry.is_initialized());
} else {
DCHECK_EQ(
entry->safepoint_entry,
SafepointTable::FindEntry(isolate, entry->code.value(), inner_pointer));
}
return entry->safepoint_entry;
}
MaglevSafepointEntry GetMaglevSafepointEntryFromCodeCache(
Isolate* isolate, Address inner_pointer,
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry) {
if (!entry->maglev_safepoint_entry.is_initialized()) {
entry->maglev_safepoint_entry = MaglevSafepointTable::FindEntry(
isolate, entry->code.value(), inner_pointer);
DCHECK(entry->maglev_safepoint_entry.is_initialized());
} else {
DCHECK_EQ(entry->maglev_safepoint_entry,
MaglevSafepointTable::FindEntry(isolate, entry->code.value(),
inner_pointer));
}
return entry->maglev_safepoint_entry;
}
}
#ifdef V8_ENABLE_WEBASSEMBLY
#if V8_ENABLE_DRUMBRAKE
class DrumBrakeWasmCode {
public:
explicit DrumBrakeWasmCode(wasm::WasmCode* wasm_code)
: wasm_code_(wasm_code) {}
static std::unique_ptr<DrumBrakeWasmCode> Interpreted() {
return std::make_unique<DrumBrakeWasmCode>(nullptr);
}
static std::unique_ptr<DrumBrakeWasmCode> Compiled(
wasm::WasmCode* wasm_code) {
return std::make_unique<DrumBrakeWasmCode>(wasm_code);
}
bool is_liftoff() const {
return wasm_code_ ? wasm_code_->is_liftoff() : false;
}
bool frame_has_feedback_slot() const {
return wasm_code_ ? wasm_code_->frame_has_feedback_slot() : false;
}
int stack_slots() const { return wasm_code_ ? wasm_code_->stack_slots() : 0; }
wasm::WasmCode::Kind kind() const {
return wasm_code_ ? wasm_code_->kind() : wasm::WasmCode::kInterpreterEntry;
}
uint16_t first_tagged_parameter_slot() const {
return wasm_code_ ? wasm_code_->first_tagged_parameter_slot() : 0;
}
uint16_t num_tagged_parameter_slots() const {
return wasm_code_ ? wasm_code_->num_tagged_parameter_slots() : 0;
}
private:
const wasm::WasmCode* wasm_code_;
};
#endif
void WasmFrame::Iterate(RootVisitor* v) const {
DCHECK(!iterator_->IsStackFrameIteratorForProfiler());
#if !V8_ENABLE_DRUMBRAKE
auto pair = wasm::GetWasmCodeManager()->LookupCodeAndSafepoint(
isolate(), maybe_unauthenticated_pc());
wasm::WasmCode* wasm_code = pair.first;
SafepointEntry safepoint_entry = pair.second;
#else
std::unique_ptr<DrumBrakeWasmCode> interpreter_wasm_code;
SafepointEntry safepoint_entry;
bool is_wasm_interpreter_frame =
v8_flags.wasm_jitless &&
(type() == WASM_INTERPRETER_ENTRY || type() == C_WASM_ENTRY);
if (is_wasm_interpreter_frame) {
interpreter_wasm_code = DrumBrakeWasmCode::Interpreted();
} else {
auto pair =
wasm::GetWasmCodeManager()->LookupCodeAndSafepoint(isolate(), pc());
wasm::WasmCode* wasm_code = pair.first;
safepoint_entry = pair.second;
DCHECK(wasm_code);
interpreter_wasm_code = DrumBrakeWasmCode::Compiled(wasm_code);
}
DrumBrakeWasmCode* wasm_code = interpreter_wasm_code.get();
#endif
#ifdef DEBUG
intptr_t marker =
Memory<intptr_t>(fp() + CommonFrameConstants::kContextOrFrameTypeOffset);
DCHECK(StackFrame::IsTypeMarker(marker));
StackFrame::Type type = StackFrame::MarkerToType(marker);
DCHECK(type == WASM_TO_JS || type == WASM || type == WASM_EXIT ||
type == WASM_SEGMENT_START);
#endif
static_assert(WasmExitFrameConstants::kFixedSlotCountFromFp ==
WasmFrameConstants::kFixedSlotCountFromFp + 1,
"WasmExitFrame has one slot more than WasmFrame");
int frame_header_size = WasmFrameConstants::kFixedFrameSizeFromFp;
if (wasm_code->is_liftoff() && wasm_code->frame_has_feedback_slot()) {
frame_header_size += kSystemPointerSize;
}
int spill_slot_space =
wasm_code->stack_slots() * kSystemPointerSize -
(frame_header_size + StandardFrameConstants::kFixedFrameSizeAboveFp);
FullObjectSlot frame_header_base(&Memory<Address>(fp() - frame_header_size));
FullObjectSlot frame_header_limit(
&Memory<Address>(fp() - StandardFrameConstants::kCPSlotSize));
wasm::StackMemory::StackSwitchInfo maybe_stack_switch;
if (iterator_->wasm_stack() != nullptr) {
maybe_stack_switch = iterator_->wasm_stack()->stack_switch_info();
}
FullObjectSlot parameters_limit(
maybe_stack_switch.has_value() && maybe_stack_switch.source_fp == fp()
? maybe_stack_switch.target_sp
: frame_header_base.address() - spill_slot_space);
FullObjectSlot spill_space_end =
FullObjectSlot(frame_header_base.address() - spill_slot_space);
bool has_tagged_outgoing_params =
wasm_code->kind() != wasm::WasmCode::kWasmFunction &&
wasm_code->kind() != wasm::WasmCode::kWasmToCapiWrapper;
if (!InFastCCall() && has_tagged_outgoing_params) {
FullObjectSlot parameters_base(&Memory<Address>(sp()));
v->VisitRootPointers(Root::kStackRoots, nullptr, parameters_base,
parameters_limit);
}
if (safepoint_entry.is_initialized()) {
DCHECK_GE((wasm_code->stack_slots() + kBitsPerByte) / kBitsPerByte,
safepoint_entry.tagged_slots().size());
VisitSpillSlots(isolate(), v, spill_space_end,
safepoint_entry.tagged_slots());
}
if (wasm_code->num_tagged_parameter_slots() > 0) {
FullObjectSlot tagged_parameter_base(&Memory<Address>(caller_sp()));
tagged_parameter_base += wasm_code->first_tagged_parameter_slot();
FullObjectSlot tagged_parameter_limit =
tagged_parameter_base + wasm_code->num_tagged_parameter_slots();
v->VisitRootPointers(Root::kStackRoots, nullptr, tagged_parameter_base,
tagged_parameter_limit);
}
v->VisitRootPointers(Root::kStackRoots, nullptr, frame_header_base,
frame_header_limit);
}
void TypedFrame::IterateParamsOfGenericWasmToJSWrapper(RootVisitor* v) const {
Address maybe_sig =
Memory<Address>(fp() + WasmToJSWrapperConstants::kSignatureOffset);
if (maybe_sig == 0 || maybe_sig == static_cast<Address>(-1)) {
return;
}
const wasm::CanonicalSig* sig =
reinterpret_cast<wasm::CanonicalSig*>(maybe_sig);
DCHECK(wasm::GetTypeCanonicalizer()->Contains(sig));
wasm::LinkageLocationAllocator allocator(wasm::kGpParamRegisters,
wasm::kFpParamRegisters, 0);
allocator.Next(MachineRepresentation::kTaggedPointer);
bool has_tagged_param = false;
for (wasm::CanonicalValueType type : sig->parameters()) {
MachineRepresentation param = type.machine_representation();
if (IsAnyTagged(param)) {
has_tagged_param = true;
continue;
}
if (kSystemPointerSize == 8 || param != MachineRepresentation::kWord64) {
allocator.Next(param);
} else {
allocator.Next(MachineRepresentation::kWord32);
allocator.Next(MachineRepresentation::kWord32);
}
}
allocator.EndSlotArea();
if (!has_tagged_param) return;
#if V8_TARGET_ARCH_ARM64
constexpr size_t size_of_sig = 2;
#else
constexpr size_t size_of_sig = 1;
#endif
for (wasm::CanonicalValueType type : sig->parameters()) {
MachineRepresentation param = type.machine_representation();
if (!IsAnyTagged(param)) continue;
LinkageLocation l = allocator.Next(param);
if (l.IsRegister()) {
int slot_offset = 0;
for (size_t i = 1; i < arraysize(wasm::kGpParamRegisters); ++i) {
if (wasm::kGpParamRegisters[i].code() == l.AsRegister()) {
slot_offset = static_cast<int>(i) - 1;
break;
}
}
size_t param_start_offset = 2 + size_of_sig;
FullObjectSlot param_start(fp() +
param_start_offset * kSystemPointerSize);
FullObjectSlot tagged_slot = param_start + slot_offset;
VisitSpillSlot(isolate(), v, tagged_slot);
} else {
int slot_offset = -l.GetLocation() - 1;
size_t slots_per_float64 = kDoubleSize / kSystemPointerSize;
size_t param_start_offset =
arraysize(wasm::kGpParamRegisters) - 1 +
(arraysize(wasm::kFpParamRegisters) * slots_per_float64) + 2 +
size_of_sig;
if (arraysize(wasm::kGpParamRegisters) % 2 == (0) &&
kSystemPointerSize != kDoubleSize) {
param_start_offset++;
}
FullObjectSlot param_start(fp() +
param_start_offset * kSystemPointerSize);
FullObjectSlot tagged_slot = param_start + slot_offset;
VisitSpillSlot(isolate(), v, tagged_slot);
}
}
}
#endif
void TypedFrame::Iterate(RootVisitor* v) const {
DCHECK(!iterator_->IsStackFrameIteratorForProfiler());
Address inner_pointer = pc();
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
CHECK(entry->code.has_value());
Tagged<GcSafeCode> code = entry->code.value();
#if V8_ENABLE_WEBASSEMBLY
bool is_generic_wasm_to_js =
code->is_builtin() && code->builtin_id() == Builtin::kWasmToJsWrapperCSA;
DCHECK_NE(this->type(), WASM_TO_JS);
if (is_generic_wasm_to_js) {
IterateParamsOfGenericWasmToJSWrapper(v);
}
#endif
DCHECK(code->is_turbofanned());
SafepointEntry safepoint_entry =
GetSafepointEntryFromCodeCache(isolate(), inner_pointer, entry);
#ifdef DEBUG
intptr_t marker =
Memory<intptr_t>(fp() + CommonFrameConstants::kContextOrFrameTypeOffset);
DCHECK(StackFrame::IsTypeMarker(marker));
#endif
int frame_header_size = TypedFrameConstants::kFixedFrameSizeFromFp;
int spill_slots_size =
code->stack_slots() * kSystemPointerSize -
(frame_header_size + StandardFrameConstants::kFixedFrameSizeAboveFp);
FullObjectSlot frame_header_base(&Memory<Address>(fp() - frame_header_size));
FullObjectSlot frame_header_limit(
&Memory<Address>(fp() - StandardFrameConstants::kCPSlotSize));
#if V8_ENABLE_WEBASSEMBLY
wasm::StackMemory::StackSwitchInfo maybe_stack_switch;
if (iterator_->wasm_stack() != nullptr) {
maybe_stack_switch = iterator_->wasm_stack()->stack_switch_info();
}
FullObjectSlot parameters_limit(
maybe_stack_switch.has_value() && maybe_stack_switch.source_fp == fp()
? maybe_stack_switch.target_sp
: frame_header_base.address() - spill_slots_size);
#else
FullObjectSlot parameters_limit(frame_header_base.address() -
spill_slots_size);
#endif
FullObjectSlot parameters_base(&Memory<Address>(sp()));
FullObjectSlot spill_slots_end(frame_header_base.address() -
spill_slots_size);
if (HasTaggedOutgoingParams(code)) {
v->VisitRootPointers(Root::kStackRoots, nullptr, parameters_base,
parameters_limit);
}
DCHECK_GE((code->stack_slots() + kBitsPerByte) / kBitsPerByte,
safepoint_entry.tagged_slots().size());
VisitSpillSlots(isolate(), v, spill_slots_end,
safepoint_entry.tagged_slots());
v->VisitRootPointers(Root::kStackRoots, nullptr, frame_header_base,
frame_header_limit);
IteratePc(v, constant_pool_address(), code);
}
void MaglevFrame::Iterate(RootVisitor* v) const {
DCHECK(!iterator_->IsStackFrameIteratorForProfiler());
Address inner_pointer = pc();
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
CHECK(entry->code.has_value());
Tagged<GcSafeCode> code = entry->code.value();
DCHECK(code->is_maglevved());
MaglevSafepointEntry maglev_safepoint_entry =
GetMaglevSafepointEntryFromCodeCache(isolate(), inner_pointer, entry);
#ifdef DEBUG
intptr_t marker =
Memory<intptr_t>(fp() + CommonFrameConstants::kContextOrFrameTypeOffset);
DCHECK(!StackFrame::IsTypeMarker(marker));
#endif
FullObjectSlot frame_header_base(
&Memory<Address>(fp() - StandardFrameConstants::kFixedFrameSizeFromFp));
FullObjectSlot frame_header_limit(
&Memory<Address>(fp() - StandardFrameConstants::kCPSlotSize));
uint32_t tagged_slot_count = maglev_safepoint_entry.num_tagged_slots();
uint32_t spill_slot_count =
code->stack_slots() - StandardFrameConstants::kFixedSlotCount;
DCHECK(code->has_tagged_outgoing_params());
FullObjectSlot parameters_base(&Memory<Address>(sp()));
FullObjectSlot parameters_limit =
frame_header_base - spill_slot_count -
maglev_safepoint_entry.num_extra_spill_slots();
v->VisitRootPointers(Root::kStackRoots, nullptr, parameters_base,
parameters_limit);
if (maglev_safepoint_entry.num_extra_spill_slots() > 0) {
FullObjectSlot pushed_register_base =
frame_header_base - spill_slot_count - 1;
uint32_t tagged_register_indexes =
maglev_safepoint_entry.tagged_register_indexes();
while (tagged_register_indexes != 0) {
int index = base::bits::CountTrailingZeros(tagged_register_indexes);
tagged_register_indexes &= ~(1 << index);
FullObjectSlot spill_slot = pushed_register_base - index;
VisitSpillSlot(isolate(), v, spill_slot);
}
}
for (uint32_t i = 0; i < tagged_slot_count; ++i) {
FullObjectSlot spill_slot = frame_header_base - 1 - i;
VisitSpillSlot(isolate(), v, spill_slot);
}
v->VisitRootPointers(Root::kStackRoots, nullptr, frame_header_base + 1,
frame_header_limit);
IteratePc(v, constant_pool_address(), code);
}
DirectHandle<JSFunction> MaglevFrame::GetInnermostFunction() const {
return Summarize().frames.back().AsJavaScript().function();
}
BytecodeOffset MaglevFrame::GetBytecodeOffsetForOSR() const {
int deopt_index = SafepointEntry::kNoDeoptIndex;
Tagged<Code> code = LookupCode();
const Tagged<DeoptimizationData> data =
GetDeoptimizationData(code, &deopt_index);
if (deopt_index == SafepointEntry::kNoDeoptIndex) {
CHECK(data.is_null());
FATAL(
"Missing deoptimization information for OptimizedJSFrame::Summarize.");
}
DeoptimizationFrameTranslation::Iterator it(
data->FrameTranslation(), data->TranslationIndex(deopt_index).value());
int js_frames = it.EnterBeginOpcode().js_frame_count;
DCHECK_GT(js_frames, 0);
BytecodeOffset offset = BytecodeOffset::None();
while (js_frames > 0) {
TranslationOpcode frame = it.SeekNextJSFrame();
--js_frames;
if (IsTranslationInterpreterFrameOpcode(frame)) {
offset = BytecodeOffset(it.NextOperand());
it.SkipOperands(TranslationOpcodeOperandCount(frame) - 1);
} else {
it.SkipOperands(TranslationOpcodeOperandCount(frame));
}
}
return offset;
}
bool CommonFrame::HasTaggedOutgoingParams(
Tagged<GcSafeCode> code_lookup) const {
#if V8_ENABLE_WEBASSEMBLY
wasm::WasmCode* wasm_callee =
wasm::GetWasmCodeManager()->LookupCode(isolate(), callee_pc());
if (wasm_callee) return false;
if (IsWasmToJsWrapperCSA(callee_pc())) {
return false;
}
return code_lookup->has_tagged_outgoing_params();
#else
return code_lookup->has_tagged_outgoing_params();
#endif
}
Tagged<HeapObject> TurbofanStubWithContextFrame::unchecked_code() const {
std::optional<Tagged<GcSafeCode>> code_lookup =
isolate()->heap()->GcSafeTryFindCodeForInnerPointer(pc());
if (!code_lookup.has_value()) return {};
return code_lookup.value();
}
void CommonFrame::IterateTurbofanJSOptimizedFrame(RootVisitor* v) const {
DCHECK(!iterator_->IsStackFrameIteratorForProfiler());
const Address inner_pointer = maybe_unauthenticated_pc();
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry* entry =
isolate()->inner_pointer_to_code_cache()->GetCacheEntry(inner_pointer);
CHECK(entry->code.has_value());
Tagged<GcSafeCode> code = entry->code.value();
DCHECK(code->is_turbofanned());
SafepointEntry safepoint_entry =
GetSafepointEntryFromCodeCache(isolate(), inner_pointer, entry);
#ifdef DEBUG
intptr_t marker =
Memory<intptr_t>(fp() + CommonFrameConstants::kContextOrFrameTypeOffset);
DCHECK(!StackFrame::IsTypeMarker(marker));
#endif
int frame_header_size = StandardFrameConstants::kFixedFrameSizeFromFp;
int spill_slot_count =
code->stack_slots() - StandardFrameConstants::kFixedSlotCount;
FullObjectSlot frame_header_base(&Memory<Address>(fp() - frame_header_size));
FullObjectSlot frame_header_limit(
&Memory<Address>(fp() - StandardFrameConstants::kCPSlotSize));
FullObjectSlot parameters_limit = frame_header_base - spill_slot_count;
if (!InFastCCall()) {
FullObjectSlot parameters_base(&Memory<Address>(sp()));
if (HasTaggedOutgoingParams(code)) {
v->VisitRootPointers(Root::kStackRoots, nullptr, parameters_base,
parameters_limit);
}
} else {
}
DCHECK_GE((code->stack_slots() + kBitsPerByte) / kBitsPerByte,
safepoint_entry.tagged_slots().size());
VisitSpillSlots(isolate(), v, parameters_limit,
safepoint_entry.tagged_slots());
v->VisitRootPointers(Root::kStackRoots, nullptr, frame_header_base + 1,
frame_header_limit);
IteratePc(v, constant_pool_address(), code);
}
void TurbofanStubWithContextFrame::Iterate(RootVisitor* v) const {
return IterateTurbofanJSOptimizedFrame(v);
}
void TurbofanJSFrame::Iterate(RootVisitor* v) const {
return IterateTurbofanJSOptimizedFrame(v);
}
Tagged<HeapObject> StubFrame::unchecked_code() const {
std::optional<Tagged<GcSafeCode>> code_lookup =
isolate()->heap()->GcSafeTryFindCodeForInnerPointer(pc());
if (!code_lookup.has_value()) return {};
return code_lookup.value();
}
int StubFrame::LookupExceptionHandlerInTable() {
Tagged<Code> code;
int pc_offset = -1;
std::tie(code, pc_offset) = LookupCodeAndOffset();
DCHECK(code->is_turbofanned());
DCHECK(code->has_handler_table());
HandlerTable table(code);
return table.LookupReturn(pc_offset);
}
FrameSummaries StubFrame::Summarize() const {
FrameSummaries summaries;
#if V8_ENABLE_WEBASSEMBLY
Tagged<Code> code = LookupCode();
if (code->kind() != CodeKind::BUILTIN) return summaries;
switch (code->builtin_id()) {
case Builtin::kThrowDataViewTypeError:
case Builtin::kThrowDataViewDetachedError:
case Builtin::kThrowDataViewOutOfBounds:
case Builtin::kThrowIndexOfCalledOnNull:
case Builtin::kThrowToLowerCaseCalledOnNull:
case Builtin::kWasmIntToString: {
DCHECK_NE(nullptr,
Builtins::NameForStackTrace(isolate(), code->builtin_id()));
FrameSummary::BuiltinFrameSummary summary(isolate(), code->builtin_id());
summaries.frames.push_back(summary);
break;
}
default:
break;
}
#endif
return summaries;
}
void JavaScriptFrame::SetParameterValue(int index, Tagged<Object> value) const {
Memory<Address>(GetParameterSlot(index)) = value.ptr();
}
bool JavaScriptFrame::IsConstructor() const {
return IsConstructFrame(caller_fp());
}
Tagged<HeapObject> CommonFrameWithJSLinkage::unchecked_code() const {
return function()->code(isolate());
}
int TurbofanJSFrame::ComputeParametersCount() const {
if (GcSafeLookupCode()->kind() == CodeKind::BUILTIN) {
return static_cast<int>(
Memory<intptr_t>(fp() + StandardFrameConstants::kArgCOffset)) -
kJSArgcReceiverSlots;
} else {
return JavaScriptFrame::ComputeParametersCount();
}
}
Address JavaScriptFrame::GetCallerStackPointer() const {
return fp() + StandardFrameConstants::kCallerSPOffset;
}
void JavaScriptFrame::GetFunctions(
std::vector<Tagged<SharedFunctionInfo>>* functions) const {
DCHECK(functions->empty());
functions->push_back(function()->shared());
}
void JavaScriptFrame::GetFunctions(
std::vector<Handle<SharedFunctionInfo>>* functions) const {
DCHECK(functions->empty());
std::vector<Tagged<SharedFunctionInfo>> raw_functions;
GetFunctions(&raw_functions);
Isolate* isolate = Isolate::Current();
for (const auto& raw_function : raw_functions) {
functions->push_back(Handle<SharedFunctionInfo>(raw_function, isolate));
}
}
std::tuple<Tagged<AbstractCode>, int> JavaScriptFrame::GetActiveCodeAndOffset()
const {
int code_offset = 0;
Tagged<AbstractCode> abstract_code;
if (is_interpreted()) {
const InterpretedFrame* iframe = InterpretedFrame::cast(this);
code_offset = iframe->GetBytecodeOffset();
abstract_code = Cast<AbstractCode>(iframe->GetBytecodeArray());
} else if (is_baseline()) {
const BaselineFrame* baseline_frame = BaselineFrame::cast(this);
code_offset = baseline_frame->GetBytecodeOffset();
abstract_code = Cast<AbstractCode>(baseline_frame->GetBytecodeArray());
} else {
Tagged<Code> code;
int pc_offset = -1;
std::tie(code, pc_offset) = LookupCodeAndOffset();
abstract_code = Cast<AbstractCode>(code);
}
return {abstract_code, code_offset};
}
bool CommonFrameWithJSLinkage::IsConstructor() const {
return IsConstructFrame(caller_fp());
}
FrameSummaries CommonFrameWithJSLinkage::Summarize() const {
Tagged<GcSafeCode> gcsafe_code;
int offset = -1;
std::tie(gcsafe_code, offset) = GcSafeLookupCodeAndOffset();
DirectHandle<Code> code(gcsafe_code->UnsafeCastToCode(), isolate());
#if V8_ENABLE_WEBASSEMBLY
if (code->kind() == CodeKind::BUILTIN &&
code->builtin_id() == Builtin::kWasmMethodWrapper) {
return FrameSummaries{};
}
#endif
DirectHandle<FixedArray> params = GetParameters();
FrameSummary::JavaScriptFrameSummary summary(
isolate(), receiver(), function(), *Cast<AbstractCode>(code), offset,
IsConstructor(), *params);
return FrameSummaries(summary);
}
Tagged<JSFunction> JavaScriptFrame::function() const {
return Cast<JSFunction>(function_slot_object());
}
Tagged<Object> JavaScriptFrame::unchecked_function() const {
DCHECK(IsJSFunction(function_slot_object()) ||
ReadOnlyRoots(isolate()).arguments_marker() == function_slot_object());
return function_slot_object();
}
Tagged<Object> CommonFrameWithJSLinkage::receiver() const {
return GetParameter(-1);
}
Tagged<Object> JavaScriptFrame::context() const {
const int offset = StandardFrameConstants::kContextOffset;
Tagged<Object> maybe_result(Memory<Address>(fp() + offset));
DCHECK(!IsSmi(maybe_result));
return maybe_result;
}
Tagged<Script> JavaScriptFrame::script() const {
return Cast<Script>(function()->shared()->script());
}
int CommonFrameWithJSLinkage::LookupExceptionHandlerInTable(
int* stack_depth, HandlerTable::CatchPrediction* prediction) {
if (DEBUG_BOOL) {
Tagged<Code> code_lookup_result = LookupCode();
CHECK(!code_lookup_result->has_handler_table());
CHECK(!code_lookup_result->is_optimized_code() ||
code_lookup_result->kind() == CodeKind::BASELINE);
}
return -1;
}
void JavaScriptFrame::PrintFunctionAndOffset(Isolate* isolate,
Tagged<JSFunction> function,
Tagged<AbstractCode> code,
int code_offset, FILE* file,
bool print_line_number) {
PtrComprCageBase cage_base = GetPtrComprCageBase(function);
PrintF(file, "%s",
CodeKindToMarker(code->kind(cage_base),
code->is_context_specialized(cage_base),
code->osr_offset(cage_base)));
function->PrintName(file);
PrintF(file, "+%d", code_offset);
if (print_line_number) {
Tagged<SharedFunctionInfo> shared = function->shared();
int source_pos = code->SourcePosition(isolate, code_offset);
Tagged<Object> maybe_script = shared->script();
if (IsScript(maybe_script)) {
Tagged<Script> script = Cast<Script>(maybe_script);
int line = script->GetLineNumber(source_pos) + 1;
Tagged<Object> script_name_raw = script->name();
if (IsString(script_name_raw)) {
Tagged<String> script_name = Cast<String>(script->name());
std::unique_ptr<char[]> c_script_name = script_name->ToCString();
PrintF(file, " at %s:%d", c_script_name.get(), line);
} else {
PrintF(file, " at <unknown>:%d", line);
}
} else {
PrintF(file, " at <unknown>:<unknown>");
}
}
}
void JavaScriptFrame::PrintTop(Isolate* isolate, FILE* file, bool print_args,
bool print_line_number) {
DisallowGarbageCollection no_gc;
JavaScriptStackFrameIterator it(isolate);
while (!it.done()) {
if (it.frame()->is_javascript()) {
JavaScriptFrame* frame = it.frame();
if (frame->IsConstructor()) PrintF(file, "new ");
Tagged<JSFunction> function = frame->function();
int code_offset = 0;
Tagged<AbstractCode> code;
std::tie(code, code_offset) = frame->GetActiveCodeAndOffset();
PrintFunctionAndOffset(isolate, function, code, code_offset, file,
print_line_number);
if (print_args) {
PrintF(file, "(this=");
ShortPrint(frame->receiver(), file);
const int length = frame->ComputeParametersCount();
for (int i = 0; i < length; i++) {
PrintF(file, ", ");
ShortPrint(frame->GetParameter(i), file);
}
PrintF(file, ")");
}
break;
}
it.Advance();
}
}
void JavaScriptFrame::CollectFunctionAndOffsetForICStats(
Isolate* isolate, Tagged<JSFunction> function, Tagged<AbstractCode> code,
int code_offset) {
auto ic_stats = ICStats::instance();
ICInfo& ic_info = ic_stats->Current();
PtrComprCageBase cage_base = GetPtrComprCageBase(function);
Tagged<SharedFunctionInfo> shared = function->shared(cage_base);
ic_info.function_name = ic_stats->GetOrCacheFunctionName(isolate, function);
ic_info.script_offset = code_offset;
int source_pos = code->SourcePosition(isolate, code_offset);
Tagged<Object> maybe_script = shared->script(cage_base, kAcquireLoad);
if (IsScript(maybe_script, cage_base)) {
Tagged<Script> script = Cast<Script>(maybe_script);
Script::PositionInfo info;
script->GetPositionInfo(source_pos, &info);
ic_info.line_num = info.line + 1;
ic_info.column_num = info.column + 1;
ic_info.script_name = ic_stats->GetOrCacheScriptName(script);
}
}
Tagged<Object> CommonFrameWithJSLinkage::GetParameter(int index) const {
return Tagged<Object>(Memory<Address>(GetParameterSlot(index)));
}
int CommonFrameWithJSLinkage::ComputeParametersCount() const {
DCHECK(!iterator_->IsStackFrameIteratorForProfiler() &&
isolate()->heap()->gc_state() == Heap::NOT_IN_GC);
int parameter_count;
if (is_interpreted()) {
const InterpretedFrame* iframe = InterpretedFrame::cast(this);
parameter_count =
iframe->GetBytecodeArray()->parameter_count_without_receiver();
} else {
Tagged<GcSafeCode> code = GcSafeLookupCode();
parameter_count = code->parameter_count_without_receiver();
}
DCHECK_EQ(
parameter_count,
function()->shared()->internal_formal_parameter_count_without_receiver());
return parameter_count;
}
int JavaScriptFrame::GetActualArgumentCount() const {
return static_cast<int>(
Memory<intptr_t>(fp() + StandardFrameConstants::kArgCOffset)) -
kJSArgcReceiverSlots;
}
DirectHandle<FixedArray> CommonFrameWithJSLinkage::GetParameters() const {
if (V8_LIKELY(!v8_flags.detailed_error_stack_trace)) {
return isolate()->factory()->empty_fixed_array();
}
int param_count = ComputeParametersCount();
DirectHandle<FixedArray> parameters =
isolate()->factory()->NewFixedArray(param_count);
for (int i = 0; i < param_count; i++) {
parameters->set(i, GetParameter(i));
}
return parameters;
}
Tagged<JSFunction> JavaScriptBuiltinContinuationFrame::function() const {
const int offset = BuiltinContinuationFrameConstants::kFunctionOffset;
return Cast<JSFunction>(Tagged<Object>(base::Memory<Address>(fp() + offset)));
}
int JavaScriptBuiltinContinuationFrame::ComputeParametersCount() const {
DCHECK_EQ(RegisterConfiguration::Default()->GetAllocatableGeneralCode(0),
kJavaScriptCallArgCountRegister.code());
Tagged<Object> argc_object(
Memory<Address>(fp() + BuiltinContinuationFrameConstants::kArgCOffset));
return Smi::ToInt(argc_object) - kJSArgcReceiverSlots;
}
intptr_t JavaScriptBuiltinContinuationFrame::GetSPToFPDelta() const {
Address height_slot =
fp() + BuiltinContinuationFrameConstants::kFrameSPtoFPDeltaAtDeoptimize;
intptr_t height = Smi::ToInt(Tagged<Smi>(Memory<Address>(height_slot)));
return height;
}
Tagged<Object> JavaScriptBuiltinContinuationFrame::context() const {
return Tagged<Object>(Memory<Address>(
fp() + BuiltinContinuationFrameConstants::kBuiltinContextOffset));
}
void JavaScriptBuiltinContinuationWithCatchFrame::SetException(
Tagged<Object> exception) {
int argc = ComputeParametersCount();
Address exception_argument_slot =
fp() + BuiltinContinuationFrameConstants::kFixedFrameSizeAboveFp +
(argc - 1) * kSystemPointerSize;
CHECK_EQ(ReadOnlyRoots(isolate()).the_hole_value(),
Tagged<Object>(Memory<Address>(exception_argument_slot)));
Memory<Address>(exception_argument_slot) = exception.ptr();
}
FrameSummary::JavaScriptFrameSummary::JavaScriptFrameSummary(
Isolate* isolate, Tagged<Object> receiver, Tagged<JSFunction> function,
Tagged<AbstractCode> abstract_code, int code_offset, bool is_constructor,
Tagged<FixedArray> parameters)
: FrameSummaryBase(isolate, FrameSummary::JAVASCRIPT),
receiver_(receiver, isolate),
function_(function, isolate),
abstract_code_(abstract_code, isolate),
code_offset_(code_offset),
is_constructor_(is_constructor),
parameters_(parameters, isolate) {
DCHECK_IMPLIES(CodeKindIsOptimizedJSFunction(abstract_code->kind(isolate)),
(v8_flags.maglev_inline_api_calls &&
abstract_code->kind(isolate) == CodeKind::MAGLEV &&
function->shared()->IsApiFunction()));
}
void FrameSummary::EnsureSourcePositionsAvailable() {
if (IsJavaScript()) {
javascript_summary_.EnsureSourcePositionsAvailable();
}
}
bool FrameSummary::AreSourcePositionsAvailable() const {
if (IsJavaScript()) {
return javascript_summary_.AreSourcePositionsAvailable();
}
return true;
}
void FrameSummary::JavaScriptFrameSummary::EnsureSourcePositionsAvailable() {
DirectHandle<SharedFunctionInfo> shared(function()->shared(), isolate());
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate(), shared);
}
bool FrameSummary::JavaScriptFrameSummary::AreSourcePositionsAvailable() const {
return !v8_flags.enable_lazy_source_positions ||
function()
->shared()
->GetBytecodeArray(isolate())
->HasSourcePositionTable();
}
bool FrameSummary::JavaScriptFrameSummary::is_subject_to_debugging() const {
return function()->shared()->IsSubjectToDebugging();
}
int FrameSummary::JavaScriptFrameSummary::SourcePosition() const {
return abstract_code()->SourcePosition(isolate(), code_offset());
}
int FrameSummary::JavaScriptFrameSummary::SourceStatementPosition() const {
return abstract_code()->SourceStatementPosition(isolate(), code_offset());
}
Handle<Object> FrameSummary::JavaScriptFrameSummary::script() const {
return handle(function_->shared()->script(), isolate());
}
DirectHandle<Context> FrameSummary::JavaScriptFrameSummary::native_context()
const {
return direct_handle(function_->native_context(), isolate());
}
DirectHandle<StackFrameInfo>
FrameSummary::JavaScriptFrameSummary::CreateStackFrameInfo() const {
DirectHandle<SharedFunctionInfo> shared(function_->shared(), isolate());
DirectHandle<Script> script(Cast<Script>(shared->script()), isolate());
DirectHandle<String> function_name =
JSFunction::GetDebugName(isolate(), function_);
if (function_name->length() == 0 &&
script->compilation_type() == Script::CompilationType::kEval) {
function_name = isolate()->factory()->eval_string();
}
int bytecode_offset = code_offset();
if (bytecode_offset == kFunctionEntryBytecodeOffset) {
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate(), shared);
int source_position =
abstract_code()->SourcePosition(isolate(), bytecode_offset);
return isolate()->factory()->NewStackFrameInfo(
script, source_position, function_name, is_constructor());
}
return isolate()->factory()->NewStackFrameInfo(
shared, bytecode_offset, function_name, is_constructor());
}
#if V8_ENABLE_WEBASSEMBLY
FrameSummary::WasmFrameSummary::WasmFrameSummary(
Isolate* isolate, Handle<WasmTrustedInstanceData> instance_data,
wasm::WasmCode* code, int byte_offset, int function_index,
bool at_to_number_conversion)
: FrameSummaryBase(isolate, WASM),
instance_data_(instance_data),
at_to_number_conversion_(at_to_number_conversion),
code_(code),
byte_offset_(byte_offset),
function_index_(function_index) {}
Handle<Object> FrameSummary::WasmFrameSummary::receiver() const {
return isolate()->global_proxy();
}
uint32_t FrameSummary::WasmFrameSummary::function_index() const {
return function_index_;
}
int FrameSummary::WasmFrameSummary::SourcePosition() const {
const wasm::WasmModule* module = wasm_trusted_instance_data()->module();
return GetSourcePosition(module, function_index(), code_offset(),
at_to_number_conversion());
}
Handle<Script> FrameSummary::WasmFrameSummary::script() const {
return handle(wasm_instance()->module_object()->script(), isolate());
}
DirectHandle<WasmInstanceObject> FrameSummary::WasmFrameSummary::wasm_instance()
const {
CHECK(instance_data_->has_instance_object());
return direct_handle(instance_data_->instance_object(), isolate());
}
DirectHandle<Context> FrameSummary::WasmFrameSummary::native_context() const {
return direct_handle(wasm_trusted_instance_data()->native_context(),
isolate());
}
DirectHandle<StackFrameInfo>
FrameSummary::WasmFrameSummary::CreateStackFrameInfo() const {
DirectHandle<String> function_name =
GetWasmFunctionDebugName(isolate(), instance_data_, function_index());
return isolate()->factory()->NewStackFrameInfo(script(), SourcePosition(),
function_name, false);
}
FrameSummary::WasmInlinedFrameSummary::WasmInlinedFrameSummary(
Isolate* isolate, Handle<WasmTrustedInstanceData> instance_data,
int function_index, int op_wire_bytes_offset)
: FrameSummaryBase(isolate, WASM_INLINED),
instance_data_(instance_data),
function_index_(function_index),
op_wire_bytes_offset_(op_wire_bytes_offset) {}
DirectHandle<WasmInstanceObject>
FrameSummary::WasmInlinedFrameSummary::wasm_instance() const {
CHECK(instance_data_->has_instance_object());
return direct_handle(instance_data_->instance_object(), isolate());
}
Handle<Object> FrameSummary::WasmInlinedFrameSummary::receiver() const {
return isolate()->global_proxy();
}
uint32_t FrameSummary::WasmInlinedFrameSummary::function_index() const {
return function_index_;
}
int FrameSummary::WasmInlinedFrameSummary::SourcePosition() const {
const wasm::WasmModule* module = instance_data_->module();
return GetSourcePosition(module, function_index(), code_offset(), false);
}
Handle<Script> FrameSummary::WasmInlinedFrameSummary::script() const {
return handle(wasm_instance()->module_object()->script(), isolate());
}
DirectHandle<Context> FrameSummary::WasmInlinedFrameSummary::native_context()
const {
return direct_handle(wasm_trusted_instance_data()->native_context(),
isolate());
}
DirectHandle<StackFrameInfo>
FrameSummary::WasmInlinedFrameSummary::CreateStackFrameInfo() const {
DirectHandle<String> function_name =
GetWasmFunctionDebugName(isolate(), instance_data_, function_index());
return isolate()->factory()->NewStackFrameInfo(script(), SourcePosition(),
function_name, false);
}
#if V8_ENABLE_DRUMBRAKE
FrameSummary::WasmInterpretedFrameSummary::WasmInterpretedFrameSummary(
Isolate* isolate, Handle<WasmInstanceObject> instance,
uint32_t function_index, int byte_offset)
: FrameSummaryBase(isolate, WASM_INTERPRETED),
wasm_instance_(instance),
function_index_(function_index),
byte_offset_(byte_offset) {}
Handle<Object> FrameSummary::WasmInterpretedFrameSummary::receiver() const {
return Isolate::Current()->global_proxy();
}
int FrameSummary::WasmInterpretedFrameSummary::SourcePosition() const {
const wasm::WasmModule* module = wasm_instance()->module();
return GetSourcePosition(module, function_index(), byte_offset(),
false );
}
Handle<WasmTrustedInstanceData>
FrameSummary::WasmInterpretedFrameSummary::instance_data() const {
return handle(wasm_instance_->trusted_data(isolate()), isolate());
}
Handle<Script> FrameSummary::WasmInterpretedFrameSummary::script() const {
return handle(wasm_instance()->module_object()->script(), Isolate::Current());
}
DirectHandle<Context>
FrameSummary::WasmInterpretedFrameSummary::native_context() const {
return handle(wasm_instance_->trusted_data(isolate())->native_context(),
isolate());
}
DirectHandle<StackFrameInfo>
FrameSummary::WasmInterpretedFrameSummary::CreateStackFrameInfo() const {
DirectHandle<String> function_name =
GetWasmFunctionDebugName(isolate(), instance_data(), function_index());
return isolate()->factory()->NewStackFrameInfo(script(), SourcePosition(),
function_name, false);
}
#endif
FrameSummary::BuiltinFrameSummary::BuiltinFrameSummary(Isolate* isolate,
Builtin builtin)
: FrameSummaryBase(isolate, FrameSummary::BUILTIN), builtin_(builtin) {}
Handle<Object> FrameSummary::BuiltinFrameSummary::receiver() const {
return isolate()->factory()->undefined_value();
}
Handle<Object> FrameSummary::BuiltinFrameSummary::script() const {
return isolate()->factory()->undefined_value();
}
DirectHandle<Context> FrameSummary::BuiltinFrameSummary::native_context()
const {
return isolate()->native_context();
}
DirectHandle<StackFrameInfo>
FrameSummary::BuiltinFrameSummary::CreateStackFrameInfo() const {
DirectHandle<String> name_str =
isolate()->factory()->NewStringFromAsciiChecked(
Builtins::NameForStackTrace(isolate(), builtin_));
return isolate()->factory()->NewStackFrameInfo(
Cast<Script>(script()), SourcePosition(), name_str, false);
}
#endif
FrameSummary::~FrameSummary() {
#define FRAME_SUMMARY_DESTR(kind, type, field, desc) \
case kind: \
field.~type(); \
break;
switch (base_.kind()) {
FRAME_SUMMARY_VARIANTS(FRAME_SUMMARY_DESTR)
default:
UNREACHABLE();
}
#undef FRAME_SUMMARY_DESTR
}
FrameSummary FrameSummary::GetTop(const CommonFrame* frame) {
FrameSummaries summaries = frame->Summarize();
DCHECK_LT(0, summaries.size());
return summaries.frames.back();
}
FrameSummary FrameSummary::GetBottom(const CommonFrame* frame) {
return Get(frame, 0);
}
FrameSummary FrameSummary::GetSingle(const CommonFrame* frame) {
FrameSummaries summaries = frame->Summarize();
DCHECK_EQ(1, summaries.size());
return summaries.frames.front();
}
FrameSummary FrameSummary::Get(const CommonFrame* frame, int index) {
DCHECK_LE(0, index);
FrameSummaries summaries = frame->Summarize();
DCHECK_GT(summaries.size(), index);
return summaries.frames[index];
}
#if V8_ENABLE_WEBASSEMBLY
#ifdef V8_ENABLE_DRUMBRAKE
#define CASE_WASM_INTERPRETED(name) \
case WASM_INTERPRETED: \
return wasm_interpreted_summary_.name();
#else
#define CASE_WASM_INTERPRETED(name)
#endif
#define FRAME_SUMMARY_DISPATCH(ret, name) \
ret FrameSummary::name() const { \
switch (base_.kind()) { \
case JAVASCRIPT: \
return javascript_summary_.name(); \
case WASM: \
return wasm_summary_.name(); \
case WASM_INLINED: \
return wasm_inlined_summary_.name(); \
case BUILTIN: \
return builtin_summary_.name(); \
CASE_WASM_INTERPRETED(name) \
default: \
UNREACHABLE(); \
} \
}
#else
#define FRAME_SUMMARY_DISPATCH(ret, name) \
ret FrameSummary::name() const { \
DCHECK_EQ(JAVASCRIPT, base_.kind()); \
return javascript_summary_.name(); \
}
#endif
FRAME_SUMMARY_DISPATCH(Handle<Object>, receiver)
FRAME_SUMMARY_DISPATCH(int, code_offset)
FRAME_SUMMARY_DISPATCH(bool, is_constructor)
FRAME_SUMMARY_DISPATCH(bool, is_subject_to_debugging)
FRAME_SUMMARY_DISPATCH(Handle<Object>, script)
FRAME_SUMMARY_DISPATCH(int, SourcePosition)
FRAME_SUMMARY_DISPATCH(int, SourceStatementPosition)
FRAME_SUMMARY_DISPATCH(DirectHandle<Context>, native_context)
FRAME_SUMMARY_DISPATCH(DirectHandle<StackFrameInfo>, CreateStackFrameInfo)
#undef CASE_WASM_INTERPRETED
#undef FRAME_SUMMARY_DISPATCH
FrameSummaries OptimizedJSFrame::Summarize() const {
DCHECK(is_optimized());
FrameSummaries summaries;
DirectHandle<Code> code(LookupCode(), isolate());
if (code->kind() == CodeKind::BUILTIN ||
code->kind() == CodeKind::FOR_TESTING_JS) {
return JavaScriptFrame::Summarize();
}
DCHECK_NE(code->kind(), CodeKind::FOR_TESTING);
int deopt_index = SafepointEntry::kNoDeoptIndex;
Tagged<DeoptimizationData> const data =
GetDeoptimizationData(*code, &deopt_index);
if (deopt_index == SafepointEntry::kNoDeoptIndex) {
if (code->is_maglevved()) {
DirectHandle<AbstractCode> abstract_code(
Cast<AbstractCode>(function()->shared()->GetBytecodeArray(isolate())),
isolate());
DirectHandle<FixedArray> params = GetParameters();
FrameSummary::JavaScriptFrameSummary summary(
isolate(), receiver(), function(), *abstract_code,
kFunctionEntryBytecodeOffset, IsConstructor(), *params);
summaries.frames.push_back(summary);
return summaries;
}
CHECK(data.is_null());
FATAL(
"Missing deoptimization information for OptimizedJSFrame::Summarize.");
}
TranslatedState translated(this);
translated.Prepare(fp());
bool is_constructor = IsConstructor();
for (auto it = translated.begin(); it != translated.end(); it++) {
if (TranslatedFrame::IsJavaScriptFrame(it->kind())) {
DirectHandle<SharedFunctionInfo> shared_info = it->shared_info();
TranslatedFrame::iterator translated_values = it->begin();
CHECK(!translated_values->IsMaterializedObject());
DirectHandle<JSFunction> function =
Cast<JSFunction>(translated_values->GetValue());
translated_values++;
static_assert(TranslatedFrame::kReceiverIsFirstParameterInJSFrames);
CHECK(!translated_values->IsMaterializedObject());
DirectHandle<Object> receiver = translated_values->GetValue();
translated_values++;
DirectHandle<AbstractCode> abstract_code;
unsigned code_offset;
if (TranslatedFrame::IsJavaScriptBuiltinContinuationFrame(it->kind())) {
code_offset = 0;
abstract_code = Cast<AbstractCode>(isolate()->builtins()->code_handle(
Builtins::GetBuiltinFromBytecodeOffset(it->bytecode_offset())));
} else {
DCHECK_EQ(it->kind(), TranslatedFrame::kUnoptimizedFunction);
code_offset = it->bytecode_offset().ToInt();
abstract_code =
direct_handle(shared_info->abstract_code(isolate()), isolate());
}
DirectHandle<FixedArray> params = GetParameters();
FrameSummary::JavaScriptFrameSummary summary(
isolate(), *receiver, *function, *abstract_code, code_offset,
is_constructor, *params);
summaries.frames.push_back(summary);
is_constructor = false;
} else if (it->kind() == TranslatedFrame::kConstructCreateStub ||
it->kind() == TranslatedFrame::kConstructInvokeStub) {
DCHECK(!is_constructor);
is_constructor = true;
#if V8_ENABLE_WEBASSEMBLY
} else if (it->kind() == TranslatedFrame::kWasmInlinedIntoJS) {
DirectHandle<SharedFunctionInfo> shared_info = it->shared_info();
DCHECK_NE(isolate()->heap()->gc_state(), Heap::MARK_COMPACT);
Tagged<WasmExportedFunctionData> function_data =
shared_info->wasm_exported_function_data();
Handle<WasmTrustedInstanceData> instance{function_data->instance_data(),
isolate()};
int func_index = function_data->function_index();
FrameSummary::WasmInlinedFrameSummary summary(
isolate(), instance, func_index, it->bytecode_offset().ToInt());
summaries.frames.push_back(summary);
#endif
}
}
if (is_constructor) {
summaries.top_frame_is_construct_call = true;
}
return summaries;
}
int OptimizedJSFrame::LookupExceptionHandlerInTable(
int* data, HandlerTable::CatchPrediction* prediction) {
DCHECK_NULL(prediction);
Tagged<Code> code;
int pc_offset = -1;
std::tie(code, pc_offset) = LookupCodeAndOffset();
HandlerTable table(code);
if (table.NumberOfReturnEntries() == 0) return -1;
DCHECK_NULL(data);
if (CodeKindCanDeoptimize(code->kind())) {
if (!code->marked_for_deoptimization()) {
int optimized_exception_handler = table.LookupReturn(pc_offset);
if (optimized_exception_handler != HandlerTable::kLazyDeopt) {
return optimized_exception_handler;
}
Deoptimizer::DeoptimizeFunction(
function(), LazyDeoptimizeReason::kExceptionCaught, code);
}
DCHECK(code->marked_for_deoptimization());
pc_offset = FindReturnPCForTrampoline(code, pc_offset);
}
return table.LookupReturn(pc_offset);
}
int MaglevFrame::FindReturnPCForTrampoline(Tagged<Code> code,
int trampoline_pc) const {
DCHECK_EQ(code->kind(), CodeKind::MAGLEV);
DCHECK(code->marked_for_deoptimization());
MaglevSafepointTable safepoints(isolate(), pc(), code);
return safepoints.find_return_pc(trampoline_pc);
}
int TurbofanJSFrame::FindReturnPCForTrampoline(Tagged<Code> code,
int trampoline_pc) const {
DCHECK_EQ(code->kind(), CodeKind::TURBOFAN_JS);
DCHECK(code->marked_for_deoptimization());
SafepointTable safepoints(isolate(), pc(), code);
return safepoints.find_return_pc(trampoline_pc);
}
Tagged<DeoptimizationData> OptimizedJSFrame::GetDeoptimizationData(
Tagged<Code> code, int* deopt_index) const {
DCHECK(is_optimized());
Address pc = maybe_unauthenticated_pc();
DCHECK(code->contains(isolate(), pc));
DCHECK(CodeKindCanDeoptimize(code->kind()));
if (code->is_maglevved()) {
MaglevSafepointEntry safepoint_entry =
code->GetMaglevSafepointEntry(isolate(), pc);
if (safepoint_entry.has_deoptimization_index()) {
*deopt_index = safepoint_entry.deoptimization_index();
return code->deoptimization_data();
}
} else {
SafepointEntry safepoint_entry = code->GetSafepointEntry(isolate(), pc);
if (safepoint_entry.has_deoptimization_index()) {
*deopt_index = safepoint_entry.deoptimization_index();
return code->deoptimization_data();
}
}
*deopt_index = SafepointEntry::kNoDeoptIndex;
return {};
}
void OptimizedJSFrame::GetFunctions(
std::vector<Tagged<SharedFunctionInfo>>* functions) const {
DCHECK(functions->empty());
DCHECK(is_optimized());
Tagged<Code> code = LookupCode();
if (code->kind() == CodeKind::BUILTIN) {
return JavaScriptFrame::GetFunctions(functions);
}
DisallowGarbageCollection no_gc;
int deopt_index = SafepointEntry::kNoDeoptIndex;
Tagged<DeoptimizationData> const data =
GetDeoptimizationData(code, &deopt_index);
DCHECK(!data.is_null());
DCHECK_NE(SafepointEntry::kNoDeoptIndex, deopt_index);
Tagged<DeoptimizationLiteralArray> const literal_array = data->LiteralArray();
DeoptimizationFrameTranslation::Iterator it(
data->FrameTranslation(), data->TranslationIndex(deopt_index).value());
int jsframe_count = it.EnterBeginOpcode().js_frame_count;
while (jsframe_count != 0) {
TranslationOpcode opcode = it.SeekNextJSFrame();
it.NextOperand();
jsframe_count--;
Tagged<Object> shared = literal_array->get(it.NextOperand());
functions->push_back(Cast<SharedFunctionInfo>(shared));
it.SkipOperands(TranslationOpcodeOperandCount(opcode) - 2);
}
}
int OptimizedJSFrame::StackSlotOffsetRelativeToFp(int slot_index) {
return StandardFrameConstants::kCallerSPOffset -
((slot_index + 1) * kSystemPointerSize);
}
int UnoptimizedJSFrame::position() const {
Tagged<BytecodeArray> code = GetBytecodeArray();
int code_offset = GetBytecodeOffset();
return code->SourcePosition(code_offset);
}
int UnoptimizedJSFrame::LookupExceptionHandlerInTable(
int* context_register, HandlerTable::CatchPrediction* prediction) {
HandlerTable table(GetBytecodeArray());
int handler_index = table.LookupHandlerIndexForRange(GetBytecodeOffset());
if (handler_index != HandlerTable::kNoHandlerFound) {
if (context_register) *context_register = table.GetRangeData(handler_index);
if (prediction) *prediction = table.GetRangePrediction(handler_index);
table.MarkHandlerUsed(handler_index);
return table.GetRangeHandler(handler_index);
}
return handler_index;
}
Tagged<BytecodeArray> UnoptimizedJSFrame::GetBytecodeArray() const {
const int index = UnoptimizedFrameConstants::kBytecodeArrayExpressionIndex;
DCHECK_EQ(UnoptimizedFrameConstants::kBytecodeArrayFromFp,
UnoptimizedFrameConstants::kExpressionsOffset -
index * kSystemPointerSize);
return TrustedCast<BytecodeArray>(GetExpression(index));
}
Tagged<Object> UnoptimizedJSFrame::ReadInterpreterRegister(
int register_index) const {
const int index = UnoptimizedFrameConstants::kRegisterFileExpressionIndex;
DCHECK_EQ(UnoptimizedFrameConstants::kRegisterFileFromFp,
UnoptimizedFrameConstants::kExpressionsOffset -
index * kSystemPointerSize);
return GetExpression(index + register_index);
}
FrameSummaries UnoptimizedJSFrame::Summarize() const {
DirectHandle<AbstractCode> abstract_code(
Cast<AbstractCode>(GetBytecodeArray()), isolate());
DirectHandle<FixedArray> params = GetParameters();
FrameSummary::JavaScriptFrameSummary summary(
isolate(), receiver(), function(), *abstract_code, GetBytecodeOffset(),
IsConstructor(), *params);
return FrameSummaries(summary);
}
int InterpretedFrame::GetBytecodeOffset() const {
const int index = InterpreterFrameConstants::kBytecodeOffsetExpressionIndex;
DCHECK_EQ(InterpreterFrameConstants::kBytecodeOffsetFromFp,
InterpreterFrameConstants::kExpressionsOffset -
index * kSystemPointerSize);
int raw_offset = Smi::ToInt(GetExpression(index));
return raw_offset - BytecodeArray::kHeaderSize + kHeapObjectTag;
}
void InterpretedFrame::PatchBytecodeOffset(int new_offset) {
const int index = InterpreterFrameConstants::kBytecodeOffsetExpressionIndex;
DCHECK_EQ(InterpreterFrameConstants::kBytecodeOffsetFromFp,
InterpreterFrameConstants::kExpressionsOffset -
index * kSystemPointerSize);
int raw_offset = BytecodeArray::kHeaderSize - kHeapObjectTag + new_offset;
SetExpression(index, Smi::FromInt(raw_offset));
}
void InterpretedFrame::PatchBytecodeArray(
Tagged<BytecodeArray> bytecode_array) {
const int index = InterpreterFrameConstants::kBytecodeArrayExpressionIndex;
DCHECK_EQ(InterpreterFrameConstants::kBytecodeArrayFromFp,
InterpreterFrameConstants::kExpressionsOffset -
index * kSystemPointerSize);
SetExpression(index, bytecode_array);
}
int BaselineFrame::GetBytecodeOffset() const {
Tagged<Code> code = LookupCode();
return code->GetBytecodeOffsetForBaselinePC(this->pc(), GetBytecodeArray());
}
intptr_t BaselineFrame::GetPCForBytecodeOffset(int bytecode_offset) const {
Tagged<Code> code = LookupCode();
return code->GetBaselineStartPCForBytecodeOffset(bytecode_offset,
GetBytecodeArray());
}
void BaselineFrame::PatchContext(Tagged<Context> value) {
base::Memory<Address>(fp() + BaselineFrameConstants::kContextOffset) =
value.ptr();
}
Tagged<JSFunction> BuiltinFrame::function() const {
const int offset = BuiltinFrameConstants::kFunctionOffset;
return Cast<JSFunction>(Tagged<Object>(base::Memory<Address>(fp() + offset)));
}
int BuiltinFrame::ComputeParametersCount() const {
const int offset = BuiltinFrameConstants::kLengthOffset;
return Smi::ToInt(Tagged<Object>(base::Memory<Address>(fp() + offset))) -
kJSArgcReceiverSlots;
}
#if V8_ENABLE_WEBASSEMBLY
void WasmFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
PrintIndex(accumulator, mode, index);
#if V8_ENABLE_DRUMBRAKE
if (v8_flags.wasm_jitless) {
DCHECK(is_wasm_to_js());
accumulator->Add("Wasm-to-JS");
if (mode != OVERVIEW) accumulator->Add("\n");
return;
}
#endif
if (function_index() == wasm::kAnonymousFuncIndex) {
accumulator->Add("Anonymous wasm wrapper [pc: %p]\n",
reinterpret_cast<void*>(pc()));
return;
}
wasm::WasmCodeRefScope code_ref_scope;
accumulator->Add(is_wasm_to_js() ? "Wasm-to-JS [" : "Wasm [");
accumulator->PrintName(script()->name());
Address instruction_start = wasm_code()->instruction_start();
base::Vector<const uint8_t> raw_func_name =
module_object()->GetRawFunctionName(function_index());
const int kMaxPrintedFunctionName = 64;
char func_name[kMaxPrintedFunctionName + 1];
int func_name_len = std::min(kMaxPrintedFunctionName, raw_func_name.length());
memcpy(func_name, raw_func_name.begin(), func_name_len);
func_name[func_name_len] = '\0';
int pos = position();
const wasm::WasmModule* module = trusted_instance_data()->module();
int func_index = function_index();
int func_code_offset = module->functions[func_index].code.offset();
accumulator->Add(
"], function #%u ('%s'), pc=%p (+0x%x), pos=%d (+%d) instance=%p\n",
func_index, func_name, reinterpret_cast<void*>(pc()),
static_cast<int>(pc() - instruction_start), pos, pos - func_code_offset,
reinterpret_cast<void*>(trusted_instance_data()->ptr()));
if (mode != OVERVIEW) accumulator->Add("\n");
}
wasm::WasmCode* WasmFrame::wasm_code() const {
return wasm::GetWasmCodeManager()->LookupCode(isolate(),
maybe_unauthenticated_pc());
}
Tagged<WasmInstanceObject> WasmFrame::wasm_instance() const {
CHECK(trusted_instance_data()->has_instance_object());
return trusted_instance_data()->instance_object();
}
Tagged<WasmTrustedInstanceData> WasmFrame::trusted_instance_data() const {
Tagged<Object> trusted_data(
Memory<Address>(fp() + WasmFrameConstants::kWasmInstanceDataOffset));
return TrustedCast<WasmTrustedInstanceData>(trusted_data);
}
wasm::NativeModule* WasmFrame::native_module() const {
return trusted_instance_data()->native_module();
}
Tagged<WasmModuleObject> WasmFrame::module_object() const {
return trusted_instance_data()->module_object();
}
int WasmFrame::function_index() const { return wasm_code()->index(); }
Tagged<Script> WasmFrame::script() const { return module_object()->script(); }
int WasmFrame::position() const {
const wasm::WasmModule* module = trusted_instance_data()->module();
return GetSourcePosition(module, function_index(), generated_code_offset(),
at_to_number_conversion());
}
int WasmFrame::generated_code_offset() const {
wasm::WasmCode* code = wasm_code();
int offset = static_cast<int>(pc() - code->instruction_start());
return code->GetSourceOffsetBefore(offset);
}
bool WasmFrame::is_inspectable() const { return wasm_code()->is_inspectable(); }
Tagged<Object> WasmFrame::context() const {
return trusted_instance_data()->native_context();
}
FrameSummaries WasmFrame::Summarize() const {
FrameSummaries summaries;
wasm::WasmCode* code = wasm_code();
int offset =
static_cast<int>(maybe_unauthenticated_pc() - code->instruction_start());
Handle<WasmTrustedInstanceData> instance_data{trusted_instance_data(),
isolate()};
SourcePosition pos = code->GetSourcePositionBefore(offset);
bool at_conversion = at_to_number_conversion();
bool child_was_tail_call = false;
while (pos.isInlined()) {
const auto [func_index, was_tail_call, caller_pos] =
code->GetInliningPosition(pos.InliningId());
if (!child_was_tail_call) {
FrameSummary::WasmFrameSummary summary(isolate(), instance_data, code,
pos.ScriptOffset(), func_index,
at_conversion);
summaries.frames.push_back(summary);
}
pos = caller_pos;
at_conversion = false;
child_was_tail_call = was_tail_call;
}
if (!child_was_tail_call) {
int func_index = code->index();
FrameSummary::WasmFrameSummary summary(isolate(), instance_data, code,
pos.ScriptOffset(), func_index,
at_conversion);
summaries.frames.push_back(summary);
}
std::reverse(summaries.frames.begin(), summaries.frames.end());
return summaries;
}
bool WasmFrame::at_to_number_conversion() const {
if (callee_pc() == kNullAddress) return false;
wasm::WasmCode* wasm_code =
wasm::GetWasmCodeManager()->LookupCode(isolate(), callee_pc());
if (wasm_code) {
if (wasm_code->kind() != wasm::WasmCode::kWasmToJsWrapper) return false;
int offset = static_cast<int>(callee_pc() - wasm_code->instruction_start());
int pos = wasm_code->GetSourceOffsetBefore(offset);
DCHECK(pos == wasm::kNoCodePosition || pos == 0 || pos == 1);
return pos == 1;
}
if (!IsWasmToJsWrapperCSA(callee_pc())) {
return false;
}
Address maybe_sig =
Memory<Address>(callee_fp() + WasmToJSWrapperConstants::kSignatureOffset);
return static_cast<intptr_t>(maybe_sig) == -1;
}
int WasmFrame::LookupExceptionHandlerInTable() {
wasm::WasmCode* code =
wasm::GetWasmCodeManager()->LookupCode(isolate(), pc());
if (!code->IsAnonymous() && code->handler_table_size() > 0) {
HandlerTable table(code);
int pc_offset = static_cast<int>(pc() - code->instruction_start());
return table.LookupReturn(pc_offset);
}
return -1;
}
void WasmDebugBreakFrame::Iterate(RootVisitor* v) const {
DCHECK(caller_pc());
auto pair = wasm::GetWasmCodeManager()->LookupCodeAndSafepoint(isolate(),
caller_pc());
SafepointEntry safepoint_entry = pair.second;
uint32_t tagged_register_indexes = safepoint_entry.tagged_register_indexes();
while (tagged_register_indexes != 0) {
int reg_code = base::bits::CountTrailingZeros(tagged_register_indexes);
tagged_register_indexes &= ~(1 << reg_code);
FullObjectSlot spill_slot(&Memory<Address>(
fp() +
WasmDebugBreakFrameConstants::GetPushedGpRegisterOffset(reg_code)));
v->VisitRootPointer(Root::kStackRoots, nullptr, spill_slot);
}
}
void WasmDebugBreakFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
PrintIndex(accumulator, mode, index);
accumulator->Add("WasmDebugBreak");
if (mode != OVERVIEW) accumulator->Add("\n");
}
Tagged<WasmInstanceObject> WasmToJsFrame::wasm_instance() const {
Tagged<WasmImportData> import_data =
TrustedCast<WasmImportData>(Tagged<Object>{
Memory<Address>(fp() + WasmFrameConstants::kWasmInstanceDataOffset)});
CHECK(import_data->instance_data()->has_instance_object());
return import_data->instance_data()->instance_object();
}
Tagged<WasmTrustedInstanceData> WasmToJsFrame::trusted_instance_data() const {
return wasm_instance()->trusted_data(isolate());
}
void JsToWasmFrame::Iterate(RootVisitor* v) const {
#if V8_ENABLE_DRUMBRAKE
if (v8_flags.wasm_jitless) {
DCHECK(GetContainingCode(isolate(), pc()).value()->builtin_id() ==
Builtin::kGenericJSToWasmInterpreterWrapper);
intptr_t scan_count = *reinterpret_cast<intptr_t*>(
fp() + BuiltinWasmInterpreterWrapperConstants::kGCScanSlotCountOffset);
FullObjectSlot spill_slot_base(&Memory<Address>(sp()));
FullObjectSlot spill_slot_limit(
&Memory<Address>(sp() + scan_count * kSystemPointerSize));
v->VisitRootPointers(Root::kStackRoots, nullptr, spill_slot_base,
spill_slot_limit);
uint32_t signature_data = *reinterpret_cast<uint32_t*>(
fp() + BuiltinWasmInterpreterWrapperConstants::kSignatureDataOffset);
bool has_ref_args =
signature_data & wasm::WasmInterpreterRuntime::HasRefArgsField::kMask;
bool has_ref_rets =
signature_data & wasm::WasmInterpreterRuntime::HasRefRetsField::kMask;
bool is_args = *reinterpret_cast<intptr_t*>(
fp() + BuiltinWasmInterpreterWrapperConstants::kArgRetsIsArgsOffset);
if ((is_args && !has_ref_args) || (!is_args && !has_ref_rets)) return;
size_t return_count = *reinterpret_cast<size_t*>(
fp() + BuiltinWasmInterpreterWrapperConstants::kReturnCountOffset);
size_t param_count = *reinterpret_cast<size_t*>(
fp() + BuiltinWasmInterpreterWrapperConstants::kParamCountOffset);
const wasm::ValueType* reps = *reinterpret_cast<const wasm::ValueType**>(
fp() + BuiltinWasmInterpreterWrapperConstants::kSigRepsOffset);
wasm::FunctionSig sig(return_count, param_count, reps);
intptr_t slot_ptr = *reinterpret_cast<intptr_t*>(
fp() + BuiltinWasmInterpreterWrapperConstants::kArgRetsAddressOffset);
if (is_args) {
size_t current_index = *reinterpret_cast<size_t*>(
fp() + BuiltinWasmInterpreterWrapperConstants::kCurrentIndexOffset);
DCHECK_LE(current_index, param_count);
for (size_t i = 0; i < current_index; i++) {
wasm::ValueType type = sig.GetParam(i);
if (type.is_reference()) {
slot_ptr += (slot_ptr & 0x04);
FullObjectSlot array_slot(&Memory<Address>(slot_ptr));
v->VisitRootPointer(Root::kStackRoots, nullptr, array_slot);
slot_ptr += kSystemPointerSize;
} else {
switch (type.kind()) {
case wasm::kI32:
case wasm::kF32:
slot_ptr += sizeof(int32_t);
break;
case wasm::kI64:
case wasm::kF64:
slot_ptr += sizeof(int64_t);
break;
case wasm::kS128:
default:
UNREACHABLE();
}
}
}
} else {
for (size_t i = 0; i < return_count; i++) {
wasm::ValueType type = sig.GetReturn(i);
if (type.is_reference()) {
slot_ptr += (slot_ptr & 0x04);
FullObjectSlot array_slot(&Memory<Address>(slot_ptr));
v->VisitRootPointer(Root::kStackRoots, nullptr, array_slot);
slot_ptr += kSystemPointerSize;
} else {
switch (type.kind()) {
case wasm::kI32:
case wasm::kF32:
slot_ptr += sizeof(int32_t);
break;
case wasm::kI64:
case wasm::kF64:
slot_ptr += sizeof(int64_t);
break;
case wasm::kS128:
default:
UNREACHABLE();
}
}
}
}
}
#endif
}
#if V8_ENABLE_DRUMBRAKE
void WasmToJsFrame::Iterate(RootVisitor* v) const {
if (v8_flags.wasm_jitless) {
CHECK(v8_flags.jitless);
Address limit_sp = *reinterpret_cast<intptr_t*>(
fp() + WasmToJSInterpreterFrameConstants::kGCScanSlotLimitOffset);
FullObjectSlot spill_slot_base(&Memory<Address>(sp()));
FullObjectSlot spill_slot_limit(limit_sp);
v->VisitRootPointers(Root::kStackRoots, nullptr, spill_slot_base,
spill_slot_limit);
return;
}
WasmFrame::Iterate(v);
}
#endif
void WasmJspiFrame::Iterate(RootVisitor* v) const {
intptr_t scan_count =
Memory<intptr_t>(fp() + WasmJspiFrameConstants::kGCScanSlotCountOffset);
FullObjectSlot spill_slot_base(&Memory<Address>(sp()));
FullObjectSlot spill_slot_limit(
&Memory<Address>(sp() + scan_count * kSystemPointerSize));
v->VisitRootPointers(Root::kStackRoots, nullptr, spill_slot_base,
spill_slot_limit);
FullObjectSlot instance_slot(
&Memory<Address>(fp() + WasmJspiFrameConstants::kImplicitArgOffset));
v->VisitRootPointer(Root::kStackRoots, nullptr, instance_slot);
FullObjectSlot result_array_slot(
&Memory<Address>(fp() + WasmJspiFrameConstants::kResultArrayOffset));
v->VisitRootPointer(Root::kStackRoots, nullptr, result_array_slot);
}
#if V8_ENABLE_DRUMBRAKE
void WasmInterpreterEntryFrame::Iterate(RootVisitor* v) const {
static constexpr int kWasmInstanceObjOffset = -2 * kSystemPointerSize;
FullObjectSlot slot_base(&Memory<Address>(fp() + kWasmInstanceObjOffset));
FullObjectSlot slot_limit(
&Memory<Address>(fp() + kWasmInstanceObjOffset + kSystemPointerSize));
v->VisitRootPointers(Root::kStackRoots, nullptr, slot_base, slot_limit);
}
void WasmInterpreterEntryFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
PrintIndex(accumulator, mode, index);
accumulator->Add("WASM INTERPRETER ENTRY [");
Tagged<Script> script = this->script();
accumulator->PrintName(script->name());
accumulator->Add("]");
if (mode != OVERVIEW) accumulator->Add("\n");
}
FrameSummaries WasmInterpreterEntryFrame::Summarize() const {
FrameSummaries summaries;
Handle<WasmInstanceObject> instance(wasm_instance(), isolate());
std::vector<WasmInterpreterStackEntry> interpreted_stack =
WasmInterpreterObject::GetInterpretedStack(
trusted_instance_data()->interpreter_object(), fp());
for (auto& e : interpreted_stack) {
FrameSummary::WasmInterpretedFrameSummary summary(
isolate(), instance, e.function_index, e.byte_offset);
summaries.frames.push_back(summary);
}
return summaries;
}
Tagged<HeapObject> WasmInterpreterEntryFrame::unchecked_code() const {
return InstructionStream();
}
Tagged<WasmInstanceObject> WasmInterpreterEntryFrame::wasm_instance() const {
Tagged<Object> instance(Memory<Address>(
fp() + WasmInterpreterFrameConstants::kWasmInstanceObjectOffset));
return Cast<WasmInstanceObject>(instance);
}
Tagged<WasmTrustedInstanceData>
WasmInterpreterEntryFrame::trusted_instance_data() const {
return wasm_instance()->trusted_data(isolate());
}
Tagged<Tuple2> WasmInterpreterEntryFrame::interpreter_object() const {
return trusted_instance_data()->interpreter_object();
}
Tagged<WasmModuleObject> WasmInterpreterEntryFrame::module_object() const {
return trusted_instance_data()->module_object();
}
int WasmInterpreterEntryFrame::function_index(
int inlined_function_index) const {
return WasmInterpreterObject::GetFunctionIndex(
trusted_instance_data()->interpreter_object(), fp(),
inlined_function_index);
}
int WasmInterpreterEntryFrame::position() const {
return FrameSummary::GetBottom(this).AsWasmInterpreted().SourcePosition();
}
Tagged<Object> WasmInterpreterEntryFrame::context() const {
return trusted_instance_data()->native_context();
}
Address WasmInterpreterEntryFrame::GetCallerStackPointer() const {
return fp() + CommonFrameConstants::kCallerSPOffset;
}
#endif
void WasmJspiFrame::GetStateForJumpBuffer(wasm::JumpBuffer* jmpbuf,
State* state) {
DCHECK_NE(jmpbuf->fp, kNullAddress);
#ifdef DEBUG
StackFrame::Type type = ComputeFrameType(jmpbuf->fp);
DCHECK(type == WASM_JSPI || type == WASM_STACK_EXIT);
#endif
FillState(jmpbuf->fp, jmpbuf->sp, state);
state->pc_address = &jmpbuf->pc;
state->is_stack_exit_frame = true;
DCHECK_NE(*state->pc_address, kNullAddress);
}
int WasmLiftoffSetupFrame::GetDeclaredFunctionIndex() const {
Tagged<Object> func_index(Memory<Address>(
sp() + WasmLiftoffSetupFrameConstants::kDeclaredFunctionIndexOffset));
return Smi::ToInt(func_index);
}
wasm::NativeModule* WasmLiftoffSetupFrame::GetNativeModule() const {
return Memory<wasm::NativeModule*>(
sp() + WasmLiftoffSetupFrameConstants::kNativeModuleOffset);
}
FullObjectSlot WasmLiftoffSetupFrame::wasm_instance_data_slot() const {
return FullObjectSlot(&Memory<Address>(
sp() + WasmLiftoffSetupFrameConstants::kWasmInstanceDataOffset));
}
void WasmLiftoffSetupFrame::Iterate(RootVisitor* v) const {
FullObjectSlot spilled_instance_slot(&Memory<Address>(
fp() + WasmLiftoffSetupFrameConstants::kInstanceSpillOffset));
v->VisitRootPointer(Root::kStackRoots, "spilled wasm instance",
spilled_instance_slot);
v->VisitRootPointer(Root::kStackRoots, "wasm instance data",
wasm_instance_data_slot());
wasm::NativeModule* native_module = GetNativeModule();
int func_index = GetDeclaredFunctionIndex() +
native_module->module()->num_imported_functions;
int num_int_params = 0;
int num_ref_params = 0;
const wasm::FunctionSig* sig =
native_module->module()->functions[func_index].sig;
for (auto param : sig->parameters()) {
if (param == wasm::kWasmI32) {
num_int_params++;
} else if (param == wasm::kWasmI64) {
num_int_params += kSystemPointerSize == 8 ? 1 : 2;
} else if (param.is_reference()) {
num_ref_params++;
}
}
if (num_ref_params == 0) return;
int num_int_params_in_registers =
std::min(num_int_params,
WasmLiftoffSetupFrameConstants::kNumberOfSavedGpParamRegs);
int num_ref_params_in_registers =
std::min(num_ref_params,
WasmLiftoffSetupFrameConstants::kNumberOfSavedGpParamRegs -
num_int_params_in_registers);
for (int i = 0; i < num_ref_params_in_registers; ++i) {
FullObjectSlot spill_slot(
fp() + WasmLiftoffSetupFrameConstants::kParameterSpillsOffset
[num_int_params_in_registers + i]);
v->VisitRootPointer(Root::kStackRoots, "register parameter", spill_slot);
}
wasm::WasmCode* wasm_code = native_module->GetCode(func_index);
uint32_t first_tagged_stack_slot = wasm_code->first_tagged_parameter_slot();
uint32_t num_tagged_stack_slots = wasm_code->num_tagged_parameter_slots();
if (num_tagged_stack_slots > 0) {
FullObjectSlot tagged_parameter_base(&Memory<Address>(caller_sp()));
tagged_parameter_base += first_tagged_stack_slot;
FullObjectSlot tagged_parameter_limit =
tagged_parameter_base + num_tagged_stack_slots;
v->VisitRootPointers(Root::kStackRoots, "stack parameter",
tagged_parameter_base, tagged_parameter_limit);
}
}
#endif
namespace {
void PrintFunctionSource(StringStream* accumulator,
Tagged<SharedFunctionInfo> shared) {
if (v8_flags.max_stack_trace_source_length != 0) {
std::ostringstream os;
os << "--------- s o u r c e c o d e ---------\n"
<< SourceCodeOf(shared, v8_flags.max_stack_trace_source_length)
<< "\n-----------------------------------------\n";
accumulator->Add(os.str().c_str());
}
}
}
void JavaScriptFrame::Print(StringStream* accumulator, PrintMode mode,
int index) const {
DirectHandle<SharedFunctionInfo> shared(function()->shared(), isolate());
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate(), shared);
DisallowGarbageCollection no_gc;
Tagged<Object> receiver = this->receiver();
Tagged<JSFunction> function = this->function();
accumulator->PrintSecurityTokenIfChanged(isolate(), function);
PrintIndex(accumulator, mode, index);
PrintFrameKind(accumulator);
if (IsConstructor()) accumulator->Add("new ");
accumulator->PrintFunction(isolate(), function, receiver);
accumulator->Add(" [%p]", function);
Tagged<ScopeInfo> scope_info = shared->scope_info();
Tagged<Object> script_obj = shared->script();
if (IsScript(script_obj)) {
Tagged<Script> script = Cast<Script>(script_obj);
accumulator->Add(" [");
accumulator->PrintName(script->name());
if (is_interpreted()) {
const InterpretedFrame* iframe = InterpretedFrame::cast(this);
Tagged<BytecodeArray> bytecodes = iframe->GetBytecodeArray();
int offset = iframe->GetBytecodeOffset();
int source_pos = bytecodes->SourcePosition(offset);
int line = script->GetLineNumber(source_pos) + 1;
accumulator->Add(":%d] [bytecode=%p offset=%d]", line,
reinterpret_cast<void*>(bytecodes.ptr()), offset);
} else {
int function_start_pos = shared->StartPosition();
int line = script->GetLineNumber(function_start_pos) + 1;
accumulator->Add(":~%d] [pc=%p]", line,
reinterpret_cast<void*>(maybe_unauthenticated_pc()));
}
}
accumulator->Add("(this=%o", receiver);
int parameters_count = ComputeParametersCount();
for (int i = 0; i < parameters_count; i++) {
accumulator->Add(",");
accumulator->Add("%o", GetParameter(i));
}
accumulator->Add(")");
if (mode == OVERVIEW) {
accumulator->Add("\n");
return;
}
if (is_optimized()) {
accumulator->Add(" {\n// optimized frame\n");
PrintFunctionSource(accumulator, *shared);
accumulator->Add("}\n");
return;
}
accumulator->Add(" {\n");
int heap_locals_count = scope_info->ContextLocalCount();
int expressions_count = ComputeExpressionsCount();
Tagged<Context> context;
if (IsContext(this->context())) {
context = Cast<Context>(this->context());
while (context->IsWithContext()) {
context = context->previous();
DCHECK(!context.is_null());
}
}
if (heap_locals_count > 0) {
accumulator->Add(" // heap-allocated locals\n");
}
for (auto it : ScopeInfo::IterateLocalNames(scope_info, no_gc)) {
accumulator->Add(" var ");
accumulator->PrintName(it->name());
accumulator->Add(" = ");
if (!context.is_null()) {
int slot_index = Context::MIN_CONTEXT_SLOTS + it->index();
if (slot_index < context->length()) {
accumulator->Add("%o", context->get(slot_index, kRelaxedLoad));
} else {
accumulator->Add(
"// warning: missing context slot - inconsistent frame?");
}
} else {
accumulator->Add("// warning: no context found - inconsistent frame?");
}
accumulator->Add("\n");
}
if (0 < expressions_count) {
accumulator->Add(" // expression stack (top to bottom)\n");
}
for (int i = expressions_count - 1; i >= 0; i--) {
accumulator->Add(" [%02d] : %o\n", i, GetExpression(i));
}
PrintFunctionSource(accumulator, *shared);
accumulator->Add("}\n\n");
}
void EntryFrame::Iterate(RootVisitor* v) const {
IteratePc(v, constant_pool_address(), GcSafeLookupCode());
}
void CommonFrame::IterateExpressions(RootVisitor* v) const {
const int last_object_offset = StandardFrameConstants::kLastObjectOffset;
intptr_t marker =
Memory<intptr_t>(fp() + CommonFrameConstants::kContextOrFrameTypeOffset);
FullObjectSlot base(&Memory<Address>(sp()));
FullObjectSlot limit(&Memory<Address>(fp() + last_object_offset) + 1);
CHECK(StackFrame::IsTypeMarker(marker));
v->VisitRootPointers(Root::kStackRoots, nullptr, base, limit);
}
void JavaScriptFrame::Iterate(RootVisitor* v) const {
FullObjectSlot argc(
&Memory<Address>(fp() + StandardFrameConstants::kArgCOffset));
const int last_object_offset = StandardFrameConstants::kLastObjectOffset;
FullObjectSlot base(&Memory<Address>(sp()));
FullObjectSlot limit(&Memory<Address>(fp() + last_object_offset) + 1);
v->VisitRootPointers(Root::kStackRoots, nullptr, base, argc);
v->VisitRootPointers(Root::kStackRoots, nullptr, argc + 1, limit);
IteratePc(v, constant_pool_address(), GcSafeLookupCode());
}
void InternalFrame::Iterate(RootVisitor* v) const {
Tagged<GcSafeCode> code = GcSafeLookupCode();
IteratePc(v, constant_pool_address(), code);
if (code->has_tagged_outgoing_params()) IterateExpressions(v);
}
namespace {
uint32_t PcAddressForHashing(Isolate* isolate, Address address) {
uint32_t hashable_address;
if (OffHeapInstructionStream::TryGetAddressForHashing(isolate, address,
&hashable_address)) {
return hashable_address;
}
return ObjectAddressForHashing(address);
}
}
InnerPointerToCodeCache::InnerPointerToCodeCacheEntry*
InnerPointerToCodeCache::GetCacheEntry(Address inner_pointer) {
DCHECK(base::bits::IsPowerOfTwo(kInnerPointerToCodeCacheSize));
uint32_t hash =
ComputeUnseededHash(PcAddressForHashing(isolate_, inner_pointer));
uint32_t index = hash & (kInnerPointerToCodeCacheSize - 1);
InnerPointerToCodeCacheEntry* entry = cache(index);
if (entry->inner_pointer == inner_pointer) {
DCHECK_EQ(entry->code,
isolate_->heap()->GcSafeFindCodeForInnerPointer(inner_pointer));
} else {
entry->code =
isolate_->heap()->GcSafeFindCodeForInnerPointer(inner_pointer);
if (entry->code.value()->is_maglevved()) {
entry->maglev_safepoint_entry.Reset();
} else {
entry->safepoint_entry.Reset();
}
entry->inner_pointer = inner_pointer;
}
return entry;
}
namespace {
constexpr int TopOfStackRegisterPaddingSlots() {
return ArgumentPaddingSlots(1);
}
bool BuiltinContinuationModeIsWithCatch(BuiltinContinuationMode mode) {
switch (mode) {
case BuiltinContinuationMode::STUB:
case BuiltinContinuationMode::JAVASCRIPT:
return false;
case BuiltinContinuationMode::JAVASCRIPT_WITH_CATCH:
case BuiltinContinuationMode::JAVASCRIPT_HANDLE_EXCEPTION:
return true;
}
UNREACHABLE();
}
}
UnoptimizedFrameInfo::UnoptimizedFrameInfo(int parameters_count_with_receiver,
int translation_height,
bool is_topmost, bool pad_arguments,
FrameInfoKind frame_info_kind) {
const int locals_count = translation_height;
register_stack_slot_count_ =
UnoptimizedFrameConstants::RegisterStackSlotCount(locals_count);
static constexpr int kTheAccumulator = 1;
static constexpr int kTopOfStackPadding = TopOfStackRegisterPaddingSlots();
int maybe_additional_slots =
(is_topmost || frame_info_kind == FrameInfoKind::kConservative)
? (kTheAccumulator + kTopOfStackPadding)
: 0;
frame_size_in_bytes_without_fixed_ =
(register_stack_slot_count_ + maybe_additional_slots) *
kSystemPointerSize;
const int parameter_padding_slots =
pad_arguments ? ArgumentPaddingSlots(parameters_count_with_receiver) : 0;
const int fixed_frame_size =
InterpreterFrameConstants::kFixedFrameSize +
(parameters_count_with_receiver + parameter_padding_slots) *
kSystemPointerSize;
frame_size_in_bytes_ = frame_size_in_bytes_without_fixed_ + fixed_frame_size;
}
uint32_t UnoptimizedFrameInfo::GetStackSizeForAdditionalArguments(
int parameters_count) {
return (parameters_count + ArgumentPaddingSlots(parameters_count)) *
kSystemPointerSize;
}
ConstructStubFrameInfo::ConstructStubFrameInfo(int translation_height,
bool is_topmost,
FrameInfoKind frame_info_kind) {
const int parameters_count = translation_height;
static constexpr int kTopOfStackPadding = TopOfStackRegisterPaddingSlots();
static constexpr int kTheResult = 1;
const int argument_padding = ArgumentPaddingSlots(parameters_count);
const int adjusted_height =
(is_topmost || frame_info_kind == FrameInfoKind::kConservative)
? parameters_count + argument_padding + kTheResult +
kTopOfStackPadding
: parameters_count + argument_padding;
frame_size_in_bytes_without_fixed_ = adjusted_height * kSystemPointerSize;
frame_size_in_bytes_ = frame_size_in_bytes_without_fixed_ +
ConstructFrameConstants::kFixedFrameSize;
}
FastConstructStubFrameInfo::FastConstructStubFrameInfo(bool is_topmost) {
static constexpr int kTopOfStackPadding = TopOfStackRegisterPaddingSlots();
static constexpr int kTheResult = 1;
const int adjusted_height =
ArgumentPaddingSlots(1) +
(is_topmost ? kTheResult + kTopOfStackPadding : 0);
frame_size_in_bytes_without_fixed_ = adjusted_height * kSystemPointerSize;
frame_size_in_bytes_ = frame_size_in_bytes_without_fixed_ +
FastConstructFrameConstants::kFixedFrameSize;
}
BuiltinContinuationFrameInfo::BuiltinContinuationFrameInfo(
int translation_height,
const CallInterfaceDescriptor& continuation_descriptor,
const RegisterConfiguration* register_config, bool is_topmost,
DeoptimizeKind deopt_kind, BuiltinContinuationMode continuation_mode,
FrameInfoKind frame_info_kind) {
const bool is_conservative = frame_info_kind == FrameInfoKind::kConservative;
const int parameters_count = translation_height;
frame_has_result_stack_slot_ =
!is_topmost || deopt_kind == DeoptimizeKind::kLazy;
const int result_slot_count =
(frame_has_result_stack_slot_ || is_conservative) ? 1 : 0;
const int exception_slot_count =
(BuiltinContinuationModeIsWithCatch(continuation_mode) || is_conservative)
? 1
: 0;
const int allocatable_register_count =
register_config->num_allocatable_general_registers();
const int padding_slot_count =
BuiltinContinuationFrameConstants::PaddingSlotCount(
allocatable_register_count);
const int register_parameter_count =
continuation_descriptor.GetRegisterParameterCount();
translated_stack_parameter_count_ =
parameters_count - register_parameter_count;
stack_parameter_count_ = translated_stack_parameter_count_ +
result_slot_count + exception_slot_count;
const int stack_param_pad_count =
ArgumentPaddingSlots(stack_parameter_count_);
static constexpr int kTopOfStackPadding = TopOfStackRegisterPaddingSlots();
static constexpr int kTheResult = 1;
const int push_result_count =
(is_topmost || is_conservative) ? kTheResult + kTopOfStackPadding : 0;
frame_size_in_bytes_ =
kSystemPointerSize * (stack_parameter_count_ + stack_param_pad_count +
allocatable_register_count + padding_slot_count +
push_result_count) +
BuiltinContinuationFrameConstants::kFixedFrameSize;
frame_size_in_bytes_above_fp_ =
kSystemPointerSize * (allocatable_register_count + padding_slot_count +
push_result_count) +
(BuiltinContinuationFrameConstants::kFixedFrameSize -
BuiltinContinuationFrameConstants::kFixedFrameSizeAboveFp);
}
}
}