From 55b67e9eae9a9edc9fbbc408a74dbb0a7d3f44e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Crelier?= Date: Fri, 12 Jul 2019 00:18:21 +0000 Subject: [PATCH] [vm/bytecode] Ignore source positions not denoting a debug point in debugger. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CheckStack bytecode does not denote a debug point anymore after the introduction of the DebugCheck bytecode. Fix caller/callee determination in mixed mode. Factorize some code. Change-Id: Ide1e0bbad022a83e6113243dc996396f9f5d2f3c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/108565 Commit-Queue: Régis Crelier Reviewed-by: Alexander Markov --- runtime/vm/constants_kbc.h | 3 +- runtime/vm/debugger.cc | 278 +++++++++++++++++++++-------------- runtime/vm/debugger.h | 10 ++ runtime/vm/interpreter.cc | 2 +- runtime/vm/object.cc | 30 ++++ runtime/vm/object.h | 4 + runtime/vm/source_report.cc | 18 ++- runtime/vm/stack_frame.cc | 2 +- runtime/vm/stack_frame.h | 8 + runtime/vm/stack_frame_dbc.h | 6 + 10 files changed, 245 insertions(+), 116 deletions(-) diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h index f377c1a39b6..f877351fd74 100644 --- a/runtime/vm/constants_kbc.h +++ b/runtime/vm/constants_kbc.h @@ -959,8 +959,7 @@ class KernelBytecode { // The interpreter checks for a debug break at each instruction with listed // opcode and the bytecode generator emits a source position at each // instruction with listed opcode. - DART_FORCE_INLINE static bool IsDebugBreakCheckedOpcode( - const KBCInstr* instr) { + DART_FORCE_INLINE static bool IsDebugCheckedOpcode(const KBCInstr* instr) { switch (DecodeOpcode(instr)) { case KernelBytecode::kAllocate: case KernelBytecode::kPopLocal: diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index c2f04fec70b..3e71ebbe2ac 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -552,6 +552,61 @@ void Debugger::PrintSettingsToJSONObject(JSONObject* jsobj) const { } } +// If the current top Dart frame is interpreted, returns the fp of the caller +// in compiled code that invoked the interpreter, or 0 if not found. +// If the current top Dart frame is compiled, returns the fp of the caller in +// interpreted bytecode that invoked compiled code, or ULONG_MAX if not found. +// Returning compiled code fp 0 (or bytecode fp ULONG_MAX) as fp value insures +// that the fp will compare as a callee of any valid frame pointer of the same +// mode (compiled code or bytecode). +static uword CrossCallerFp() { + StackFrameIterator iterator(ValidationPolicy::kDontValidateFrames, + Thread::Current(), + StackFrameIterator::kNoCrossThreadIteration); + StackFrame* frame; + do { + frame = iterator.NextFrame(); + RELEASE_ASSERT(frame != nullptr); + } while (!frame->IsDartFrame()); + const bool top_is_interpreted = frame->is_interpreted(); + do { + frame = iterator.NextFrame(); + if (frame == nullptr) { + return top_is_interpreted ? 0 : ULONG_MAX; + } + if (!frame->IsDartFrame()) { + continue; + } + } while (top_is_interpreted == frame->is_interpreted()); + return frame->fp(); +} + +ActivationFrame::Relation ActivationFrame::CompareTo( + uword other_fp, + bool other_is_interpreted) const { + if (fp() == other_fp) { + ASSERT(IsInterpreted() == other_is_interpreted); + return kSelf; + } + if (IsInterpreted()) { + if (!other_is_interpreted) { + // Instead of fp(), use the fp of the compiled frame that called into the + // interpreter (CrossCallerFp). + // Note that if CrossCallerFp == other_fp, it must compare as a caller. + return IsCalleeFrameOf(other_fp, CrossCallerFp()) ? kCallee : kCaller; + } + return IsBytecodeCalleeFrameOf(other_fp, fp()) ? kCallee : kCaller; + } + if (other_is_interpreted) { + // Instead of fp(), use the fp of the interpreted frame that called into + // compiled code (CrossCallerFp). + // Note that if CrossCallerFp == other_fp, it must compare as a caller. + return IsBytecodeCalleeFrameOf(other_fp, CrossCallerFp()) ? kCallee + : kCaller; + } + return IsCalleeFrameOf(other_fp, fp()) ? kCallee : kCaller; +} + RawString* ActivationFrame::QualifiedFunctionName() { return String::New(Debugger::QualifiedFunctionName(function())); } @@ -1841,7 +1896,9 @@ Debugger::Debugger(Isolate* isolate) async_causal_stack_trace_(NULL), awaiter_stack_trace_(NULL), stepping_fp_(0), + interpreted_stepping_(false), async_stepping_fp_(0), + interpreted_async_stepping_(false), top_frame_awaiter_(Object::null()), skip_next_step_(false), needs_breakpoint_cleanup_(false), @@ -2770,6 +2827,49 @@ void Debugger::PauseException(const Instance& exc) { ClearCachedStackTraces(); } +// Helper to refine the resolved token pos. +static void RefineBreakpointPos(const Script& script, + TokenPosition pos, + TokenPosition next_closest_token_position, + TokenPosition requested_token_pos, + TokenPosition last_token_pos, + intptr_t requested_column, + TokenPosition exact_token_pos, + TokenPosition* best_fit_pos, + intptr_t* best_column, + intptr_t* best_line, + TokenPosition* best_token_pos) { + intptr_t token_start_column = -1; + intptr_t token_line = -1; + if (requested_column >= 0) { + TokenPosition ignored; + TokenPosition end_of_line_pos; + script.GetTokenLocation(pos, &token_line, &token_start_column); + script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos); + TokenPosition token_end_pos = + (end_of_line_pos < next_closest_token_position) + ? end_of_line_pos + : next_closest_token_position; + + if ((token_end_pos < exact_token_pos) || + (token_start_column > *best_column)) { + // Prefer the token with the lowest column number compatible + // with the requested column. + return; + } + } + + // Prefer the lowest (first) token pos. + if (pos < *best_fit_pos) { + *best_fit_pos = pos; + *best_line = token_line; + *best_column = token_start_column; + // best_token_pos is only used when column number is specified. + *best_token_pos = TokenPosition(exact_token_pos.value() - + (requested_column - *best_column)); + } +} + // Returns the best fit token position for a breakpoint. // // Takes a range of tokens [requested_token_pos, last_token_pos] and @@ -2875,53 +2975,49 @@ TokenPosition Debugger::ResolveBreakpointPos(bool in_bytecode, if (in_bytecode) { #if !defined(DART_PRECOMPILED_RUNTIME) kernel::BytecodeSourcePositionsIterator iter(zone, bytecode); + uword pc_offset = kUwordMax; + TokenPosition pos = TokenPosition::kNoSource; while (iter.MoveNext()) { - const TokenPosition pos = iter.TokenPos(); + if (pc_offset != kUwordMax) { + uword pc = bytecode.GetDebugCheckedOpcodePc(pc_offset, iter.PcOffset()); + pc_offset = kUwordMax; + if (pc != 0) { + TokenPosition next_closest_token_position = TokenPosition::kMaxSource; + if (requested_column >= 0) { + kernel::BytecodeSourcePositionsIterator iter2(zone, bytecode); + TokenPosition next_closest_token_position = + TokenPosition::kMaxSource; + while (iter2.MoveNext()) { + const TokenPosition next = iter2.TokenPos(); + if (next < next_closest_token_position && next > pos) { + next_closest_token_position = next; + } + } + } + RefineBreakpointPos(script, pos, next_closest_token_position, + requested_token_pos, last_token_pos, + requested_column, exact_token_pos, &best_fit_pos, + &best_column, &best_line, &best_token_pos); + } + } + pos = iter.TokenPos(); if ((!pos.IsReal()) || (pos < requested_token_pos) || (pos > last_token_pos)) { // Token is not in the target range. continue; } - - intptr_t token_start_column = -1; - intptr_t token_line = -1; - if (requested_column >= 0) { - kernel::BytecodeSourcePositionsIterator iter2(zone, bytecode); - TokenPosition next_closest_token_position = TokenPosition::kMaxSource; - while (iter2.MoveNext()) { - const TokenPosition next = iter2.TokenPos(); - if (next < next_closest_token_position && next > pos) { - next_closest_token_position = next; - } - } - - TokenPosition ignored; - TokenPosition end_of_line_pos; - script.GetTokenLocation(pos, &token_line, &token_start_column); - script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos); - TokenPosition token_end_pos = - (end_of_line_pos < next_closest_token_position) - ? end_of_line_pos - : next_closest_token_position; - - if ((token_end_pos < exact_token_pos) || - (token_start_column > best_column)) { - // Prefer the token with the lowest column number compatible - // with the requested column. - continue; - } - } - - // Prefer the lowest (first) token pos. - if (pos < best_fit_pos) { - best_fit_pos = pos; - best_line = token_line; - best_column = token_start_column; - // best_token_pos is only used when column number is specified. - best_token_pos = TokenPosition(exact_token_pos.value() - - (requested_column - best_column)); + pc_offset = iter.PcOffset(); + } + if (pc_offset != kUwordMax) { + uword pc = bytecode.GetDebugCheckedOpcodePc(pc_offset, bytecode.Size()); + if (pc != 0) { + RefineBreakpointPos(script, pos, TokenPosition::kMaxSource, + requested_token_pos, last_token_pos, + requested_column, exact_token_pos, &best_fit_pos, + &best_column, &best_line, &best_token_pos); } } + #else UNREACHABLE(); #endif // !defined(DART_PRECOMPILED_RUNTIME) @@ -2934,46 +3030,21 @@ TokenPosition Debugger::ResolveBreakpointPos(bool in_bytecode, // Token is not in the target range. continue; } - - intptr_t token_start_column = -1; - intptr_t token_line = -1; + TokenPosition next_closest_token_position = TokenPosition::kMaxSource; if (requested_column >= 0) { // Find next closest safepoint PcDescriptors::Iterator iter2(desc, kSafepointKind); - TokenPosition next_closest_token_position = TokenPosition::kMaxSource; while (iter2.MoveNext()) { const TokenPosition next = iter2.TokenPos(); if (next < next_closest_token_position && next > pos) { next_closest_token_position = next; } } - - TokenPosition ignored; - TokenPosition end_of_line_pos; - script.GetTokenLocation(pos, &token_line, &token_start_column); - script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos); - TokenPosition token_end_pos = - (end_of_line_pos < next_closest_token_position) - ? end_of_line_pos - : next_closest_token_position; - - if ((token_end_pos < exact_token_pos) || - (token_start_column > best_column)) { - // Prefer the token with the lowest column number compatible - // with the requested column. - continue; - } - } - - // Prefer the lowest (first) token pos. - if (pos < best_fit_pos) { - best_fit_pos = pos; - best_line = token_line; - best_column = token_start_column; - // best_token_pos is only used when column number is specified. - best_token_pos = TokenPosition(exact_token_pos.value() - - (requested_column - best_column)); } + RefineBreakpointPos(script, pos, next_closest_token_position, + requested_token_pos, last_token_pos, requested_column, + exact_token_pos, &best_fit_pos, &best_column, + &best_line, &best_token_pos); } } @@ -3059,25 +3130,6 @@ TokenPosition Debugger::ResolveBreakpointPos(bool in_bytecode, return TokenPosition::kNoSource; } -#if !defined(DART_PRECOMPILED_RUNTIME) -// Find a 'debug break checked' bytecode in the range [pc..end_pc[ and return -// the pc after it or 0 if not found. -static uword FindBreakpointCheckedInstr(uword pc, uword end_pc) { - while ((pc < end_pc) && !KernelBytecode::IsDebugBreakCheckedOpcode( - reinterpret_cast(pc))) { - pc = KernelBytecode::Next(pc); - } - if (pc < end_pc) { - ASSERT(KernelBytecode::IsDebugBreakCheckedOpcode( - reinterpret_cast(pc))); - // The checked debug break pc must point to the next bytecode. - return KernelBytecode::Next(pc); - } - // No 'debug break checked' bytecode in the range. - return 0; -} -#endif // !defined(DART_PRECOMPILED_RUNTIME) - void Debugger::MakeCodeBreakpointAt(const Function& func, BreakpointLocation* loc) { ASSERT(loc->token_pos_.IsReal()); @@ -3092,29 +3144,23 @@ void Debugger::MakeCodeBreakpointAt(const Function& func, if (bytecode.HasSourcePositions()) { kernel::BytecodeSourcePositionsIterator iter(Thread::Current()->zone(), bytecode); - bool check_range = false; + uword pc_offset = kUwordMax; + // TODO(regis): We should ignore all possible breakpoint positions until + // the first DebugCheck opcode of the function. while (iter.MoveNext()) { - if (check_range) { - uword end_pc = bytecode.PayloadStart() + iter.PcOffset(); - check_range = false; - // Find a 'debug break checked' bytecode in the range [pc..end_pc[. - pc = FindBreakpointCheckedInstr(pc, end_pc); - if (pc != 0) { - // TODO(regis): We may want to find all PCs for a token position, - // e.g. in the case of duplicated bytecode in finally clauses. - break; - } + if (pc_offset != kUwordMax) { + pc = bytecode.GetDebugCheckedOpcodePc(pc_offset, iter.PcOffset()); + pc_offset = kUwordMax; + // TODO(regis): We may want to find all PCs for a token position, + // e.g. in the case of duplicated bytecode in finally clauses. + break; } if (iter.TokenPos() == loc->token_pos_) { - pc = bytecode.PayloadStart() + iter.PcOffset(); - check_range = true; + pc_offset = iter.PcOffset(); } } - if (check_range) { - ASSERT(pc != 0); - // Use the end of the bytecode as the end of the range to check. - pc = FindBreakpointCheckedInstr( - pc, bytecode.PayloadStart() + bytecode.Size()); + if (pc_offset != kUwordMax) { + pc = bytecode.GetDebugCheckedOpcodePc(pc_offset, bytecode.Size()); } } if (pc != 0) { @@ -3793,7 +3839,9 @@ void Debugger::EnterSingleStepMode() { void Debugger::ResetSteppingFramePointers() { stepping_fp_ = 0; + interpreted_stepping_ = false; async_stepping_fp_ = 0; + interpreted_async_stepping_ = false; } bool Debugger::SteppedForSyntheticAsyncBreakpoint() const { @@ -3826,16 +3874,20 @@ void Debugger::SetAsyncSteppingFramePointer(DebuggerStackTrace* stack_trace) { (stack_trace->FrameAt(0)->function().IsAsyncClosure() || stack_trace->FrameAt(0)->function().IsAsyncGenClosure())) { async_stepping_fp_ = stack_trace->FrameAt(0)->fp(); + interpreted_async_stepping_ = stack_trace->FrameAt(0)->IsInterpreted(); } else { async_stepping_fp_ = 0; + interpreted_async_stepping_ = false; } } void Debugger::SetSyncSteppingFramePointer(DebuggerStackTrace* stack_trace) { if (stack_trace->Length() > 0) { stepping_fp_ = stack_trace->FrameAt(0)->fp(); + interpreted_stepping_ = stack_trace->FrameAt(0)->IsInterpreted(); } else { stepping_fp_ = 0; + interpreted_stepping_ = false; } } @@ -3888,6 +3940,7 @@ void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace, ActivationFrame* frame = stack_trace->FrameAt(i); if (frame->IsDebuggable()) { stepping_fp_ = frame->fp(); + interpreted_stepping_ = frame->IsInterpreted(); break; } } @@ -4249,10 +4302,11 @@ RawError* Debugger::PauseStepping() { // an awaiter. The first check handles the case of calling into the // async machinery as we finish the async function. The second check // handles the case of returning from an async function. + const ActivationFrame::Relation relation = + frame->CompareTo(async_stepping_fp_, interpreted_async_stepping_); const bool exited_async_function = - (IsCalleeFrameOf(async_stepping_fp_, frame->fp()) && - frame->IsAsyncMachinery()) || - IsCalleeFrameOf(frame->fp(), async_stepping_fp_); + (relation == ActivationFrame::kCallee && frame->IsAsyncMachinery()) || + relation == ActivationFrame::kCaller; if (exited_async_function) { // Step to the top frame awaiter. const Object& async_op = Object::Handle(top_frame_awaiter_); @@ -4266,11 +4320,13 @@ RawError* Debugger::PauseStepping() { if (stepping_fp_ != 0) { // There is an "interesting frame" set. Only pause at appropriate // locations in this frame. - if (IsCalleeFrameOf(stepping_fp_, frame->fp())) { + const ActivationFrame::Relation relation = + frame->CompareTo(stepping_fp_, interpreted_stepping_); + if (relation == ActivationFrame::kCallee) { // We are in a callee of the frame we're interested in. // Ignore this stepping break. return Error::null(); - } else if (IsCalleeFrameOf(frame->fp(), stepping_fp_)) { + } else if (relation == ActivationFrame::kCaller) { // We returned from the "interesting frame", there can be no more // stepping breaks for it. Pause at the next appropriate location // and let the user set the "interesting" frame again. diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h index 9b8d539a61a..d78ce46b74b 100644 --- a/runtime/vm/debugger.h +++ b/runtime/vm/debugger.h @@ -309,6 +309,14 @@ class ActivationFrame : public ZoneAllocated { } bool IsInterpreted() const { return !bytecode_.IsNull(); } + enum Relation { + kCallee, + kSelf, + kCaller, + }; + + Relation CompareTo(uword other_fp, bool other_is_interpreted) const; + RawString* QualifiedFunctionName(); RawString* SourceUrl(); RawScript* SourceScript(); @@ -821,8 +829,10 @@ class Debugger { // frame corresponds to this fp value, or if the top frame is // lower on the stack. uword stepping_fp_; + bool interpreted_stepping_; // Used to track the current async/async* function. uword async_stepping_fp_; + bool interpreted_async_stepping_; RawObject* top_frame_awaiter_; // If we step while at a breakpoint, we would hit the same pc twice. diff --git a/runtime/vm/interpreter.cc b/runtime/vm/interpreter.cc index ce1db362bf4..b437ea46326 100644 --- a/runtime/vm/interpreter.cc +++ b/runtime/vm/interpreter.cc @@ -1146,7 +1146,7 @@ DART_FORCE_INLINE bool Interpreter::InstanceCall2(Thread* thread, #define DEBUG_CHECK #else // The DEBUG_CHECK macro must only be called from bytecodes listed in -// KernelBytecode::IsDebugBreakCheckedOpcode. +// KernelBytecode::IsDebugCheckedOpcode. #define DEBUG_CHECK \ if (is_debugging()) { \ /* Check for debug breakpoint or if single stepping. */ \ diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index 746a4757ab0..3346ae0cd16 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -15395,6 +15395,26 @@ intptr_t Bytecode::GetTryIndexAtPc(uword return_address) const { #endif } +uword Bytecode::GetDebugCheckedOpcodePc(uword from_offset, + uword to_offset) const { +#if defined(DART_PRECOMPILED_RUNTIME) + UNREACHABLE(); +#else + uword pc = PayloadStart() + from_offset; + uword end_pc = pc + (to_offset - from_offset); + while (pc < end_pc) { + uword next_pc = KernelBytecode::Next(pc); + if (KernelBytecode::IsDebugCheckedOpcode( + reinterpret_cast(pc))) { + // Return the pc after the opcode, i.e. its 'return address'. + return next_pc; + } + pc = next_pc; + } + return 0; +#endif +} + const char* Bytecode::ToCString() const { return Thread::Current()->zone()->PrintToString("Bytecode(%s)", QualifiedName()); @@ -15440,6 +15460,16 @@ const char* Bytecode::QualifiedName() const { return zone->PrintToString("[Bytecode] %s", function_name); } +const char* Bytecode::FullyQualifiedName() const { + Zone* zone = Thread::Current()->zone(); + const Function& fun = Function::Handle(zone, function()); + if (fun.IsNull()) { + return BytecodeStubName(*this); + } + const char* function_name = fun.ToFullyQualifiedCString(); + return zone->PrintToString("[Bytecode] %s", function_name); +} + bool Bytecode::SlowFindRawBytecodeVisitor::FindObject( RawObject* raw_obj) const { return RawBytecode::ContainsPC(raw_obj, pc_); diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 43bfeac1fad..5363b5e1b0c 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -5729,6 +5729,9 @@ class Bytecode : public Object { TokenPosition GetTokenIndexOfPC(uword return_address) const; intptr_t GetTryIndexAtPc(uword return_address) const; + // Return the pc after the first 'debug checked' opcode in the range. + uword GetDebugCheckedOpcodePc(uword from_offset, uword to_offset) const; + intptr_t instructions_binary_offset() const { return raw_ptr()->instructions_binary_offset_; } @@ -5780,6 +5783,7 @@ class Bytecode : public Object { const char* Name() const; const char* QualifiedName() const; + const char* FullyQualifiedName() const; class SlowFindRawBytecodeVisitor : public FindObjectVisitor { public: diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc index 0a79bd9a5e5..c25545bc47f 100644 --- a/runtime/vm/source_report.cc +++ b/runtime/vm/source_report.cc @@ -340,13 +340,29 @@ void SourceReport::PrintPossibleBreakpointsData(JSONObject* jsobj, const Bytecode& bytecode = Bytecode::Handle(func.bytecode()); ASSERT(!bytecode.IsNull()); kernel::BytecodeSourcePositionsIterator iter(zone(), bytecode); + intptr_t token_offset = -1; + uword pc_offset = kUwordMax; + // TODO(regis): We should ignore all possible breakpoint positions until + // the first DebugCheck opcode of the function. while (iter.MoveNext()) { + if (pc_offset != kUwordMax) { + // Check that there is at least one 'debug checked' opcode in the last + // source position range. + if (bytecode.GetDebugCheckedOpcodePc(pc_offset, iter.PcOffset()) != 0) { + possible[token_offset] = true; + } + pc_offset = kUwordMax; + } const TokenPosition token_pos = iter.TokenPos(); if ((token_pos < begin_pos) || (token_pos > end_pos)) { // Does not correspond to a valid source position. continue; } - intptr_t token_offset = token_pos.Pos() - begin_pos.Pos(); + pc_offset = iter.PcOffset(); + token_offset = token_pos.Pos() - begin_pos.Pos(); + } + if (pc_offset != kUwordMax && + bytecode.GetDebugCheckedOpcodePc(pc_offset, bytecode.Size()) != 0) { possible[token_offset] = true; } } else { diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc index bcb9f602611..0e61b72989a 100644 --- a/runtime/vm/stack_frame.cc +++ b/runtime/vm/stack_frame.cc @@ -184,7 +184,7 @@ const char* StackFrame::ToCString() const { " offset:0x%" Px ") %s ]", GetName(), sp(), fp(), pc(), pc() - bytecode.PayloadStart(), - bytecode.Name()); + bytecode.FullyQualifiedName()); } const Code& code = Code::Handle(zone, LookupDartCode()); ASSERT(!code.IsNull()); diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h index 7753a4f4726..fba676ee54f 100644 --- a/runtime/vm/stack_frame.h +++ b/runtime/vm/stack_frame.h @@ -456,10 +456,18 @@ DART_FORCE_INLINE static uword ParamAddress(uword fp, intptr_t reverse_index) { return fp + (kParamEndSlotFromFp * kWordSize) + (reverse_index * kWordSize); } +// Both fp and other_fp are compiled code frame pointers. +// See stack_frame_dbc.h for the DBC version. DART_FORCE_INLINE static bool IsCalleeFrameOf(uword fp, uword other_fp) { return other_fp < fp; } +// Both fp and other_fp are bytecode frame pointers. +DART_FORCE_INLINE static bool IsBytecodeCalleeFrameOf(uword fp, + uword other_fp) { + return other_fp > fp; +} + // Value for stack limit that is used to cause an interrupt. // Note that on DBC stack is growing upwards so interrupt limit is 0 unlike // on all other architectures. diff --git a/runtime/vm/stack_frame_dbc.h b/runtime/vm/stack_frame_dbc.h index e3d8395904d..42a1b79789b 100644 --- a/runtime/vm/stack_frame_dbc.h +++ b/runtime/vm/stack_frame_dbc.h @@ -74,6 +74,12 @@ DART_FORCE_INLINE static bool IsCalleeFrameOf(uword fp, uword other_fp) { return other_fp > fp; } +DART_FORCE_INLINE static bool IsBytecodeCalleeFrameOf(uword fp, + uword other_fp) { + UNREACHABLE(); + return false; +} + static const int kExitLinkSlotFromEntryFp = 0; // Value for stack limit that is used to cause an interrupt.