e2df4d30a0
Instead, split each old use into the following cases: * If the TokenPosition value is expected to be a real token position, then use TokenPosition::Pos(). * If the TokenPosition is being serialized in some way, then use TokenPosition::Serialize() and change the place where the TokenPosition is recreated to use TokenPosition::Deserialize(). * If the value of the TokenPosition is being printed for debugging purposes, then just use TokenPosition::ToCString() instead. That is, we try to pin down when token positions are expected to be real vs. when other types of token positions can be found. Another source of possible error when using token positions is to convert between synthetic and real token positions. In the past, synthetic token positions may have been based off real token positions, but that is no longer the case. Thus, all methods that allow that conversion have been removed, and instead there is a new static method for constructing synthetic tokens from valid nonces. This CL also makes it so that Pos() and relational operators on token positions are only defined on real token positions, to avoid any assumptions about what the value encoded in synthetic positions mean. To help with cases where non-real token positions may occur, four helper methods are added: * TokenPosition::Min(a, b): A static method that returns the smallest real token position provided. If neither `a` or `b` are real, returns `a`. * TokenPosition::Max(a, b): A static method that returns the largest real token position provided. If neither `a` or `b` are real, returns `a`. * TokenPosition::IsWithin(start, end): Determines whether `this` falls between `start` and `end` (inclusive). If `this` is non-real, then it must be either `start` or `end` if synthetic, otherwise false. Otherwise, we mimic the old style of range checking, which means that non-real starts and ends are treated as less than every real token. * TokenPosition::CompareForSorting(other): Unlike the relational operators, provides a comparison between any types of token positions for purposes such as sorting. Currently only used in the profiler. It also changes TokenPosition::ToCString() to tag synthetic token positions, so they can be distinguished from real ones at a glance. TEST=Existing test suite on trybots, especially the observatory tests which make heavy use of the debugger and the unit tests for the profiler/source report modules. Bug: https://github.com/dart-lang/sdk/issues/44436 Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-linux-debug-x64-try,vm-kernel-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try Change-Id: Ic06aa0bc7a1f0fbac7257ed22ca5e7e0ccd7f3f2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/174924 Commit-Queue: Tess Strickland <sstrickl@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com>
309 lines
9.6 KiB
C++
309 lines
9.6 KiB
C++
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_CODE_DESCRIPTORS_H_
|
|
#define RUNTIME_VM_CODE_DESCRIPTORS_H_
|
|
|
|
#include "vm/datastream.h"
|
|
#include "vm/globals.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/log.h"
|
|
#include "vm/object.h"
|
|
#include "vm/runtime_entry.h"
|
|
|
|
namespace dart {
|
|
|
|
static const intptr_t kInvalidTryIndex = -1;
|
|
|
|
class DescriptorList : public ZoneAllocated {
|
|
public:
|
|
explicit DescriptorList(Zone* zone)
|
|
: encoded_data_(zone, kInitialStreamSize),
|
|
prev_pc_offset(0),
|
|
prev_deopt_id(0),
|
|
prev_token_pos(0) {}
|
|
|
|
~DescriptorList() {}
|
|
|
|
void AddDescriptor(PcDescriptorsLayout::Kind kind,
|
|
intptr_t pc_offset,
|
|
intptr_t deopt_id,
|
|
TokenPosition token_pos,
|
|
intptr_t try_index,
|
|
intptr_t yield_index);
|
|
|
|
PcDescriptorsPtr FinalizePcDescriptors(uword entry_point);
|
|
|
|
private:
|
|
static constexpr intptr_t kInitialStreamSize = 64;
|
|
|
|
ZoneWriteStream encoded_data_;
|
|
|
|
intptr_t prev_pc_offset;
|
|
intptr_t prev_deopt_id;
|
|
int32_t prev_token_pos;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DescriptorList);
|
|
};
|
|
|
|
class CompressedStackMapsBuilder : public ZoneAllocated {
|
|
public:
|
|
explicit CompressedStackMapsBuilder(Zone* zone)
|
|
: encoded_bytes_(zone, kInitialStreamSize) {}
|
|
|
|
void AddEntry(intptr_t pc_offset,
|
|
BitmapBuilder* bitmap,
|
|
intptr_t spill_slot_bit_count);
|
|
|
|
CompressedStackMapsPtr Finalize() const;
|
|
|
|
private:
|
|
static constexpr intptr_t kInitialStreamSize = 16;
|
|
|
|
ZoneWriteStream encoded_bytes_;
|
|
intptr_t last_pc_offset_ = 0;
|
|
DISALLOW_COPY_AND_ASSIGN(CompressedStackMapsBuilder);
|
|
};
|
|
|
|
class ExceptionHandlerList : public ZoneAllocated {
|
|
public:
|
|
struct HandlerDesc {
|
|
intptr_t outer_try_index; // Try block in which this try block is nested.
|
|
intptr_t pc_offset; // Handler PC offset value.
|
|
bool is_generated; // False if this is directly from Dart code.
|
|
const Array* handler_types; // Catch clause guards.
|
|
bool needs_stacktrace;
|
|
};
|
|
|
|
ExceptionHandlerList() : list_() {}
|
|
|
|
intptr_t Length() const { return list_.length(); }
|
|
|
|
void AddPlaceHolder() {
|
|
struct HandlerDesc data;
|
|
data.outer_try_index = -1;
|
|
data.pc_offset = ExceptionHandlers::kInvalidPcOffset;
|
|
data.is_generated = true;
|
|
data.handler_types = NULL;
|
|
data.needs_stacktrace = false;
|
|
list_.Add(data);
|
|
}
|
|
|
|
void AddHandler(intptr_t try_index,
|
|
intptr_t outer_try_index,
|
|
intptr_t pc_offset,
|
|
bool is_generated,
|
|
const Array& handler_types,
|
|
bool needs_stacktrace) {
|
|
ASSERT(try_index >= 0);
|
|
while (Length() <= try_index) {
|
|
AddPlaceHolder();
|
|
}
|
|
list_[try_index].outer_try_index = outer_try_index;
|
|
ASSERT(list_[try_index].pc_offset == ExceptionHandlers::kInvalidPcOffset);
|
|
list_[try_index].pc_offset = pc_offset;
|
|
list_[try_index].is_generated = is_generated;
|
|
ASSERT(handler_types.IsZoneHandle());
|
|
list_[try_index].handler_types = &handler_types;
|
|
list_[try_index].needs_stacktrace |= needs_stacktrace;
|
|
}
|
|
|
|
// Called by rethrows, to mark their enclosing handlers.
|
|
void SetNeedsStackTrace(intptr_t try_index) {
|
|
// Rethrows can be generated outside a try by the compiler.
|
|
if (try_index == kInvalidTryIndex) {
|
|
return;
|
|
}
|
|
ASSERT(try_index >= 0);
|
|
while (Length() <= try_index) {
|
|
AddPlaceHolder();
|
|
}
|
|
list_[try_index].needs_stacktrace = true;
|
|
}
|
|
|
|
static bool ContainsCatchAllType(const Array& array) {
|
|
auto& type = AbstractType::Handle();
|
|
for (intptr_t i = 0; i < array.Length(); i++) {
|
|
type ^= array.At(i);
|
|
if (type.IsCatchAllType()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ExceptionHandlersPtr FinalizeExceptionHandlers(uword entry_point) const;
|
|
|
|
private:
|
|
GrowableArray<struct HandlerDesc> list_;
|
|
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerList);
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
// Used to construct CatchEntryMoves for the AOT mode of compilation.
|
|
class CatchEntryMovesMapBuilder : public ZoneAllocated {
|
|
public:
|
|
CatchEntryMovesMapBuilder();
|
|
|
|
void NewMapping(intptr_t pc_offset);
|
|
void Append(const CatchEntryMove& move);
|
|
void EndMapping();
|
|
TypedDataPtr FinalizeCatchEntryMovesMap();
|
|
|
|
private:
|
|
class TrieNode;
|
|
|
|
Zone* zone_;
|
|
TrieNode* root_;
|
|
intptr_t current_pc_offset_;
|
|
GrowableArray<CatchEntryMove> moves_;
|
|
ZoneWriteStream stream_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CatchEntryMovesMapBuilder);
|
|
};
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
// A CodeSourceMap maps from pc offsets to a stack of inlined functions and
|
|
// their positions. This is encoded as a little bytecode that pushes and pops
|
|
// functions and changes the top function's position as the PC advances.
|
|
// Decoding happens by running this bytecode until we reach the desired PC.
|
|
//
|
|
// The implementation keeps track of two sets of state: one written to the byte
|
|
// stream and one that is buffered. On the JIT, this buffering effectively gives
|
|
// us a peephole optimization that merges adjacent advance PC bytecodes. On AOT,
|
|
// this allows to skip encoding our position until we reach a PC where we might
|
|
// throw.
|
|
class CodeSourceMapBuilder : public ZoneAllocated {
|
|
public:
|
|
CodeSourceMapBuilder(
|
|
Zone* zone,
|
|
bool stack_traces_only,
|
|
const GrowableArray<intptr_t>& caller_inline_id,
|
|
const GrowableArray<TokenPosition>& inline_id_to_token_pos,
|
|
const GrowableArray<const Function*>& inline_id_to_function);
|
|
|
|
// The position at which a function implicitly starts, for both the root and
|
|
// after a push bytecode. We use the classifying position kDartCodePrologue
|
|
// since it is the most common.
|
|
static const TokenPosition& kInitialPosition;
|
|
|
|
static const uint8_t kChangePosition = 0;
|
|
static const uint8_t kAdvancePC = 1;
|
|
static const uint8_t kPushFunction = 2;
|
|
static const uint8_t kPopFunction = 3;
|
|
static const uint8_t kNullCheck = 4;
|
|
|
|
void StartInliningInterval(int32_t pc_offset, intptr_t inline_id);
|
|
void BeginCodeSourceRange(int32_t pc_offset);
|
|
void EndCodeSourceRange(int32_t pc_offset, TokenPosition pos);
|
|
void NoteDescriptor(PcDescriptorsLayout::Kind kind,
|
|
int32_t pc_offset,
|
|
TokenPosition pos);
|
|
void NoteNullCheck(int32_t pc_offset, TokenPosition pos, intptr_t name_index);
|
|
|
|
ArrayPtr InliningIdToFunction();
|
|
CodeSourceMapPtr Finalize();
|
|
|
|
private:
|
|
intptr_t GetFunctionId(intptr_t inline_id);
|
|
|
|
void BufferChangePosition(TokenPosition pos) {
|
|
buffered_token_pos_stack_.Last() = pos;
|
|
}
|
|
void WriteChangePosition(TokenPosition pos);
|
|
void BufferAdvancePC(int32_t distance) { buffered_pc_offset_ += distance; }
|
|
void WriteAdvancePC(int32_t distance) {
|
|
stream_.Write<uint8_t>(kAdvancePC);
|
|
stream_.Write<int32_t>(distance);
|
|
written_pc_offset_ += distance;
|
|
}
|
|
void BufferPush(intptr_t inline_id) {
|
|
buffered_inline_id_stack_.Add(inline_id);
|
|
buffered_token_pos_stack_.Add(kInitialPosition);
|
|
}
|
|
void WritePush(intptr_t inline_id) {
|
|
stream_.Write<uint8_t>(kPushFunction);
|
|
stream_.Write<int32_t>(GetFunctionId(inline_id));
|
|
written_inline_id_stack_.Add(inline_id);
|
|
written_token_pos_stack_.Add(kInitialPosition);
|
|
}
|
|
void BufferPop() {
|
|
buffered_inline_id_stack_.RemoveLast();
|
|
buffered_token_pos_stack_.RemoveLast();
|
|
}
|
|
void WritePop() {
|
|
stream_.Write<uint8_t>(kPopFunction);
|
|
written_inline_id_stack_.RemoveLast();
|
|
written_token_pos_stack_.RemoveLast();
|
|
}
|
|
void WriteNullCheck(int32_t name_index) {
|
|
stream_.Write<uint8_t>(kNullCheck);
|
|
stream_.Write<int32_t>(name_index);
|
|
}
|
|
|
|
void FlushBuffer();
|
|
void FlushBufferStack();
|
|
void FlushBufferPosition();
|
|
void FlushBufferPC();
|
|
|
|
bool IsOnBufferedStack(intptr_t inline_id) {
|
|
for (intptr_t i = 0; i < buffered_inline_id_stack_.length(); i++) {
|
|
if (buffered_inline_id_stack_[i] == inline_id) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
intptr_t buffered_pc_offset_;
|
|
GrowableArray<intptr_t> buffered_inline_id_stack_;
|
|
GrowableArray<TokenPosition> buffered_token_pos_stack_;
|
|
|
|
intptr_t written_pc_offset_;
|
|
GrowableArray<intptr_t> written_inline_id_stack_;
|
|
GrowableArray<TokenPosition> written_token_pos_stack_;
|
|
|
|
const GrowableArray<intptr_t>& caller_inline_id_;
|
|
const GrowableArray<TokenPosition>& inline_id_to_token_pos_;
|
|
const GrowableArray<const Function*>& inline_id_to_function_;
|
|
|
|
const GrowableObjectArray& inlined_functions_;
|
|
|
|
ZoneWriteStream stream_;
|
|
|
|
const bool stack_traces_only_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CodeSourceMapBuilder);
|
|
};
|
|
|
|
class CodeSourceMapReader : public ValueObject {
|
|
public:
|
|
CodeSourceMapReader(const CodeSourceMap& map,
|
|
const Array& functions,
|
|
const Function& root)
|
|
: map_(map), functions_(functions), root_(root) {}
|
|
|
|
void GetInlinedFunctionsAt(int32_t pc_offset,
|
|
GrowableArray<const Function*>* function_stack,
|
|
GrowableArray<TokenPosition>* token_positions);
|
|
NOT_IN_PRODUCT(void PrintJSONInlineIntervals(JSONObject* jsobj));
|
|
void DumpInlineIntervals(uword start);
|
|
void DumpSourcePositions(uword start);
|
|
|
|
intptr_t GetNullCheckNameIndexAt(int32_t pc_offset);
|
|
|
|
private:
|
|
// Reads a TokenPosition value from a CSM, handling the different encoding for
|
|
// when non-symbolic stack traces are enabled.
|
|
static TokenPosition ReadPosition(ReadStream* stream);
|
|
|
|
const CodeSourceMap& map_;
|
|
const Array& functions_;
|
|
const Function& root_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(CodeSourceMapReader);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_CODE_DESCRIPTORS_H_
|