Files
sdk/runtime/vm/code_descriptors.cc
T
Teagan Strickland c9f6e669f9 [vm/compiler] Reland "Further compress the information... in StackMaps."
Fixes an assumption that CompressedStackMapsIterator::Find() is never
passed a PC offset of 0. Adds back an ASSERT that was dropped which checks
for specific cases where a given PC offset does not have a stack map entry.

Original commit message:

Lifting the PC offset in a2bb730 was a small, lightweight change that
gave us big gains, at least on 32-bit architectures. Here, we make
much more invasive changes that will improve the amount of memory used
by the information previously stored in StackMap objects.

Instead of allocating separate objects for StackMaps, we instead compress
all StackMap information for a given Code object into a single object
(CompressedStackMaps, or CSM for short). This replaces the Array used to
store PC offsets (as Smis) and the individual StackMap objects.

While we lose all canonicalization for individual StackMap entries, the
drop in space required to store stack map information more than offsets that.

-----

The impact on AOT snapshot size when compiling the Flutter Gallery
in release mode:

   armv7: Total size -2.58% (Isolate RO: +14.46%, Isolate snapshot: -22.93%)
   armv8: Total size -1.85% (Isolate RO: +15.69%, Isolate snapshot: -22.97%)

The impact on in-memory, not on-disk, size for the Flutter Gallery as seen
in the Observatory while running a profile (not release) build:

   armv7: Drops from 7.1 MB to 6.2MB (-0.9 MB)
   armv8: Drops from 13.5MB to 11.7MB (-1.8 MB)

-----

Bug: https://github.com/dart-lang/sdk/issues/35274, https://github.com/dart-lang/sdk/issues/38873
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-mac-debug-simdbc64-try
Change-Id: I111b129b0ed64f03184370bceb7cda69d5d4b3c9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121700
Commit-Queue: Teagan Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2019-10-16 08:25:53 +00:00

759 lines
25 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.
#include "vm/code_descriptors.h"
#include "vm/compiler/compiler_state.h"
#include "vm/log.h"
namespace dart {
void DescriptorList::AddDescriptor(RawPcDescriptors::Kind kind,
intptr_t pc_offset,
intptr_t deopt_id,
TokenPosition token_pos,
intptr_t try_index) {
ASSERT((kind == RawPcDescriptors::kRuntimeCall) ||
(kind == RawPcDescriptors::kBSSRelocation) ||
(kind == RawPcDescriptors::kOther) || (deopt_id != DeoptId::kNone));
// When precompiling, we only use pc descriptors for exceptions and
// relocations.
if (!FLAG_precompiled_mode || try_index != -1 ||
kind == RawPcDescriptors::kBSSRelocation) {
int32_t merged_kind_try =
RawPcDescriptors::MergedKindTry::Encode(kind, try_index);
PcDescriptors::EncodeInteger(&encoded_data_, merged_kind_try);
PcDescriptors::EncodeInteger(&encoded_data_, pc_offset - prev_pc_offset);
prev_pc_offset = pc_offset;
if (!FLAG_precompiled_mode) {
PcDescriptors::EncodeInteger(&encoded_data_, deopt_id - prev_deopt_id);
PcDescriptors::EncodeInteger(&encoded_data_,
token_pos.value() - prev_token_pos);
prev_deopt_id = deopt_id;
prev_token_pos = token_pos.value();
}
}
}
RawPcDescriptors* DescriptorList::FinalizePcDescriptors(uword entry_point) {
if (encoded_data_.length() == 0) {
return Object::empty_descriptors().raw();
}
return PcDescriptors::New(&encoded_data_);
}
// Encode unsigned integer |value| in LEB128 format and store into |data|.
static void EncodeLEB128(GrowableArray<uint8_t>* data, uintptr_t value) {
while (true) {
uint8_t part = value & 0x7f;
value >>= 7;
if (value != 0) part |= 0x80;
data->Add(part);
if (value == 0) break;
}
}
void CompressedStackMapsBuilder::AddEntry(intptr_t pc_offset,
BitmapBuilder* bitmap,
intptr_t spill_slot_bit_count) {
ASSERT(bitmap != nullptr);
ASSERT(pc_offset > last_pc_offset_);
ASSERT(spill_slot_bit_count >= 0 && spill_slot_bit_count <= bitmap->Length());
auto const pc_delta = pc_offset - last_pc_offset_;
auto const non_spill_slot_bit_count = bitmap->Length() - spill_slot_bit_count;
EncodeLEB128(&encoded_bytes_, pc_delta);
EncodeLEB128(&encoded_bytes_, spill_slot_bit_count);
EncodeLEB128(&encoded_bytes_, non_spill_slot_bit_count);
bitmap->AppendAsBytesTo(&encoded_bytes_);
last_pc_offset_ = pc_offset;
}
RawCompressedStackMaps* CompressedStackMapsBuilder::Finalize() const {
if (encoded_bytes_.length() == 0) return CompressedStackMaps::null();
return CompressedStackMaps::New(encoded_bytes_);
}
// Decode unsigned integer in LEB128 format from |data| and update |byte_index|.
static uintptr_t DecodeLEB128(const uint8_t* data,
const intptr_t data_length,
intptr_t* byte_index) {
ASSERT(*byte_index < data_length);
uword shift = 0;
uintptr_t value = 0;
uint8_t part = 0;
do {
part = data[(*byte_index)++];
value |= static_cast<uintptr_t>(part & 0x7f) << shift;
shift += 7;
} while ((part & 0x80) != 0);
return value;
}
bool CompressedStackMapsIterator::MoveNext() {
// Empty CompressedStackMaps are represented as null values.
if (maps_.IsNull() || next_offset_ >= maps_.payload_size()) return false;
intptr_t offset = next_offset_;
// We decode three LEB128 encoded integers after this, so there should be
// at least three bytes remaining in the payload.
ASSERT(offset <= maps_.payload_size() - 3);
auto const pc_delta =
DecodeLEB128(maps_.Payload(), maps_.payload_size(), &offset);
ASSERT(pc_delta <= kIntptrMax);
ASSERT(offset <= maps_.payload_size() - 2);
auto const spill_slot_bit_count =
DecodeLEB128(maps_.Payload(), maps_.payload_size(), &offset);
ASSERT(spill_slot_bit_count <= kIntptrMax);
ASSERT(offset <= maps_.payload_size() - 1);
auto const non_spill_slot_bit_count =
DecodeLEB128(maps_.Payload(), maps_.payload_size(), &offset);
ASSERT(non_spill_slot_bit_count <= kIntptrMax);
const auto stackmap_bits = spill_slot_bit_count + non_spill_slot_bit_count;
const intptr_t stackmap_size =
Utils::RoundUp(stackmap_bits, kBitsPerByte) >> kBitsPerByteLog2;
const intptr_t space_remaining = maps_.payload_size() - offset;
if (stackmap_size > space_remaining) return false;
// Now that the current entry has been completely decoded without errors, set
// the fields appropriately.
ASSERT(pc_delta <= (kMaxUint32 - current_pc_offset_));
current_pc_offset_ += pc_delta;
current_spill_slot_bit_count_ = spill_slot_bit_count;
current_non_spill_slot_bit_count_ = non_spill_slot_bit_count;
current_bits_offset_ = offset;
next_offset_ = offset + stackmap_size;
return true;
}
bool CompressedStackMapsIterator::IsObject(intptr_t bit_index) const {
ASSERT(HasLoadedEntry());
ASSERT(bit_index >= 0 && bit_index < length());
const intptr_t byte_index = bit_index >> kBitsPerByteLog2;
const intptr_t bit_remainder = bit_index & (kBitsPerByte - 1);
uint8_t byte_mask = 1U << bit_remainder;
uint8_t byte = maps_.Payload()[current_bits_offset_ + byte_index];
return (byte & byte_mask) != 0;
}
RawExceptionHandlers* ExceptionHandlerList::FinalizeExceptionHandlers(
uword entry_point) const {
intptr_t num_handlers = Length();
if (num_handlers == 0) {
return Object::empty_exception_handlers().raw();
}
const ExceptionHandlers& handlers =
ExceptionHandlers::Handle(ExceptionHandlers::New(num_handlers));
for (intptr_t i = 0; i < num_handlers; i++) {
// Assert that every element in the array has been initialized.
if (list_[i].handler_types == NULL) {
// Unreachable handler, entry not computed.
// Initialize it to some meaningful value.
const bool has_catch_all = false;
// Check it is uninitialized.
ASSERT((list_[i].outer_try_index == -1) &&
(list_[i].pc_offset == ExceptionHandlers::kInvalidPcOffset));
handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
list_[i].needs_stacktrace, has_catch_all,
list_[i].is_generated);
handlers.SetHandledTypes(i, Array::empty_array());
} else {
const bool has_catch_all = ContainsDynamic(*list_[i].handler_types);
handlers.SetHandlerInfo(i, list_[i].outer_try_index, list_[i].pc_offset,
list_[i].needs_stacktrace, has_catch_all,
list_[i].is_generated);
handlers.SetHandledTypes(i, *list_[i].handler_types);
}
}
return handlers.raw();
}
static uint8_t* ZoneAllocator(uint8_t* ptr,
intptr_t old_size,
intptr_t new_size) {
Zone* zone = Thread::Current()->zone();
return zone->Realloc<uint8_t>(ptr, old_size, new_size);
}
#if !defined(DART_PRECOMPILED_RUNTIME)
class CatchEntryMovesMapBuilder::TrieNode : public ZoneAllocated {
public:
TrieNode() : move_(), entry_state_offset_(-1) {}
TrieNode(CatchEntryMove move, intptr_t index)
: move_(move), entry_state_offset_(index) {}
intptr_t Offset() { return entry_state_offset_; }
TrieNode* Insert(TrieNode* node) {
children_.Add(node);
return node;
}
TrieNode* Follow(CatchEntryMove next) {
for (intptr_t i = 0; i < children_.length(); i++) {
if (children_[i]->move_ == next) return children_[i];
}
return NULL;
}
private:
CatchEntryMove move_;
const intptr_t entry_state_offset_;
GrowableArray<TrieNode*> children_;
};
CatchEntryMovesMapBuilder::CatchEntryMovesMapBuilder()
: zone_(Thread::Current()->zone()),
root_(new TrieNode()),
current_pc_offset_(0),
buffer_(NULL),
stream_(&buffer_, ZoneAllocator, 64) {}
void CatchEntryMovesMapBuilder::Append(const CatchEntryMove& move) {
moves_.Add(move);
}
void CatchEntryMovesMapBuilder::NewMapping(intptr_t pc_offset) {
moves_.Clear();
current_pc_offset_ = pc_offset;
}
void CatchEntryMovesMapBuilder::EndMapping() {
intptr_t suffix_length = 0;
TrieNode* suffix = root_;
// Find the largest common suffix, get the last node of the path.
for (intptr_t i = moves_.length() - 1; i >= 0; i--) {
TrieNode* n = suffix->Follow(moves_[i]);
if (n == NULL) break;
suffix_length++;
suffix = n;
}
intptr_t length = moves_.length() - suffix_length;
intptr_t current_offset = stream_.bytes_written();
typedef WriteStream::Raw<sizeof(intptr_t), intptr_t> Writer;
Writer::Write(&stream_, current_pc_offset_);
Writer::Write(&stream_, length);
Writer::Write(&stream_, suffix_length);
Writer::Write(&stream_, suffix->Offset());
// Write the unshared part, adding it to the trie.
TrieNode* node = suffix;
for (intptr_t i = length - 1; i >= 0; i--) {
moves_[i].WriteTo(&stream_);
TrieNode* child = new (zone_) TrieNode(moves_[i], current_offset);
node->Insert(child);
node = child;
}
}
RawTypedData* CatchEntryMovesMapBuilder::FinalizeCatchEntryMovesMap() {
TypedData& td = TypedData::Handle(TypedData::New(
kTypedDataInt8ArrayCid, stream_.bytes_written(), Heap::kOld));
NoSafepointScope no_safepoint;
uint8_t* dest = reinterpret_cast<uint8_t*>(td.DataAddr(0));
uint8_t* src = stream_.buffer();
for (intptr_t i = 0; i < stream_.bytes_written(); i++) {
dest[i] = src[i];
}
return td.raw();
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
const TokenPosition CodeSourceMapBuilder::kInitialPosition =
TokenPosition(TokenPosition::kDartCodeProloguePos);
CodeSourceMapBuilder::CodeSourceMapBuilder(
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)
: buffered_pc_offset_(0),
buffered_inline_id_stack_(),
buffered_token_pos_stack_(),
written_pc_offset_(0),
written_inline_id_stack_(),
written_token_pos_stack_(),
caller_inline_id_(caller_inline_id),
inline_id_to_token_pos_(inline_id_to_token_pos),
inline_id_to_function_(inline_id_to_function),
inlined_functions_(
GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld))),
buffer_(NULL),
stream_(&buffer_, ZoneAllocator, 64),
stack_traces_only_(stack_traces_only) {
buffered_inline_id_stack_.Add(0);
buffered_token_pos_stack_.Add(kInitialPosition);
written_inline_id_stack_.Add(0);
written_token_pos_stack_.Add(kInitialPosition);
}
void CodeSourceMapBuilder::FlushBuffer() {
FlushBufferStack();
FlushBufferPosition();
FlushBufferPC();
}
void CodeSourceMapBuilder::FlushBufferStack() {
for (intptr_t i = buffered_inline_id_stack_.length() - 1; i >= 0; i--) {
intptr_t buffered_id = buffered_inline_id_stack_[i];
if (i < written_inline_id_stack_.length()) {
intptr_t written_id = written_inline_id_stack_[i];
if (buffered_id == written_id) {
// i is the top-most position where the buffered and written stack
// match.
while (written_inline_id_stack_.length() > i + 1) {
WritePop();
}
for (intptr_t j = i + 1; j < buffered_inline_id_stack_.length(); j++) {
TokenPosition buffered_pos = buffered_token_pos_stack_[j - 1];
TokenPosition written_pos = written_token_pos_stack_[j - 1];
if (buffered_pos != written_pos) {
WriteChangePosition(buffered_pos);
}
WritePush(buffered_inline_id_stack_[j]);
}
return;
}
}
}
UNREACHABLE();
}
void CodeSourceMapBuilder::FlushBufferPosition() {
ASSERT(buffered_token_pos_stack_.length() ==
written_token_pos_stack_.length());
intptr_t top = buffered_token_pos_stack_.length() - 1;
TokenPosition buffered_pos = buffered_token_pos_stack_[top];
TokenPosition written_pos = written_token_pos_stack_[top];
if (buffered_pos != written_pos) {
WriteChangePosition(buffered_pos);
}
}
void CodeSourceMapBuilder::FlushBufferPC() {
if (buffered_pc_offset_ != written_pc_offset_) {
WriteAdvancePC(buffered_pc_offset_ - written_pc_offset_);
}
}
void CodeSourceMapBuilder::StartInliningInterval(int32_t pc_offset,
intptr_t inline_id) {
if (buffered_inline_id_stack_.Last() == inline_id) {
// No change in function stack.
return;
}
if (inline_id == -1) {
// Basic blocking missing an inline_id.
return;
}
if (!stack_traces_only_) {
FlushBuffer();
}
// Find a minimal set of pops and pushes to bring us to the new function
// stack.
// Pop to a common ancestor.
intptr_t common_parent = inline_id;
while (!IsOnBufferedStack(common_parent)) {
common_parent = caller_inline_id_[common_parent];
}
while (buffered_inline_id_stack_.Last() != common_parent) {
BufferPop();
}
// Push to the new top-of-stack function.
GrowableArray<intptr_t> to_push;
intptr_t id = inline_id;
while (id != common_parent) {
to_push.Add(id);
id = caller_inline_id_[id];
}
for (intptr_t i = to_push.length() - 1; i >= 0; i--) {
intptr_t callee_id = to_push[i];
TokenPosition call_token;
if (callee_id != 0) {
// TODO(rmacnak): Should make this array line up with the others.
call_token = inline_id_to_token_pos_[callee_id - 1];
} else {
UNREACHABLE();
}
// Report caller as at the position of the call.
BufferChangePosition(call_token);
BufferPush(callee_id);
}
}
void CodeSourceMapBuilder::BeginCodeSourceRange(int32_t pc_offset) {}
void CodeSourceMapBuilder::EndCodeSourceRange(int32_t pc_offset,
TokenPosition pos) {
if (pc_offset == buffered_pc_offset_) {
return; // Empty intermediate instruction.
}
if (pos != buffered_token_pos_stack_.Last()) {
if (!stack_traces_only_) {
FlushBuffer();
}
BufferChangePosition(pos);
}
BufferAdvancePC(pc_offset - buffered_pc_offset_);
}
void CodeSourceMapBuilder::NoteDescriptor(RawPcDescriptors::Kind kind,
int32_t pc_offset,
TokenPosition pos) {
const uint8_t kCanThrow =
RawPcDescriptors::kIcCall | RawPcDescriptors::kUnoptStaticCall |
RawPcDescriptors::kRuntimeCall | RawPcDescriptors::kOther;
if (stack_traces_only_ && ((kind & kCanThrow) != 0)) {
BufferChangePosition(pos);
BufferAdvancePC(pc_offset - buffered_pc_offset_);
FlushBuffer();
}
}
void CodeSourceMapBuilder::NoteNullCheck(int32_t pc_offset,
TokenPosition pos,
intptr_t name_index) {
BufferChangePosition(pos);
BufferAdvancePC(pc_offset - buffered_pc_offset_);
FlushBuffer();
WriteNullCheck(name_index);
}
intptr_t CodeSourceMapBuilder::GetFunctionId(intptr_t inline_id) {
const Function& function = *inline_id_to_function_[inline_id];
for (intptr_t i = 0; i < inlined_functions_.Length(); i++) {
if (inlined_functions_.At(i) == function.raw()) {
return i;
}
}
RELEASE_ASSERT(!function.IsNull());
inlined_functions_.Add(function, Heap::kOld);
return inlined_functions_.Length() - 1;
}
RawArray* CodeSourceMapBuilder::InliningIdToFunction() {
if (inlined_functions_.Length() == 0) {
return Object::empty_array().raw();
}
return Array::MakeFixedLength(inlined_functions_);
}
RawCodeSourceMap* CodeSourceMapBuilder::Finalize() {
if (!stack_traces_only_) {
FlushBuffer();
}
intptr_t length = stream_.bytes_written();
const CodeSourceMap& map = CodeSourceMap::Handle(CodeSourceMap::New(length));
NoSafepointScope no_safepoint;
memmove(map.Data(), buffer_, length);
return map.raw();
}
void CodeSourceMapBuilder::WriteChangePosition(TokenPosition pos) {
stream_.Write<uint8_t>(kChangePosition);
if (FLAG_precompiled_mode) {
intptr_t line = -1;
intptr_t inline_id = buffered_inline_id_stack_.Last();
if (inline_id < inline_id_to_function_.length()) {
const Function* function = inline_id_to_function_[inline_id];
Script& script = Script::Handle(function->script());
line = script.GetTokenLineUsingLineStarts(pos.SourcePosition());
}
stream_.Write<int32_t>(static_cast<int32_t>(line));
} else {
stream_.Write<int32_t>(static_cast<int32_t>(pos.value()));
}
written_token_pos_stack_.Last() = pos;
}
void CodeSourceMapReader::GetInlinedFunctionsAt(
int32_t pc_offset,
GrowableArray<const Function*>* function_stack,
GrowableArray<TokenPosition>* token_positions) {
function_stack->Clear();
token_positions->Clear();
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack->Add(&root_);
token_positions->Add(CodeSourceMapBuilder::kInitialPosition);
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
int32_t position = stream.Read<int32_t>();
(*token_positions)[token_positions->length() - 1] =
TokenPosition(position);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
current_pc_offset += delta;
if (current_pc_offset > pc_offset) {
return;
}
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack->Add(
&Function::Handle(Function::RawCast(functions_.At(func))));
token_positions->Add(CodeSourceMapBuilder::kInitialPosition);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(function_stack->length() > 1);
ASSERT(token_positions->length() > 1);
function_stack->RemoveLast();
token_positions->RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
}
#ifndef PRODUCT
void CodeSourceMapReader::PrintJSONInlineIntervals(JSONObject* jsobj) {
{
JSONArray inlined_functions(jsobj, "_inlinedFunctions");
Function& function = Function::Handle();
for (intptr_t i = 0; i < functions_.Length(); i++) {
function ^= functions_.At(i);
ASSERT(!function.IsNull());
inlined_functions.AddValue(function);
}
}
GrowableArray<intptr_t> function_stack;
JSONArray inline_intervals(jsobj, "_inlinedIntervals");
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack.Add(0);
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
stream.Read<int32_t>();
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
// Format: [start, end, inline functions...]
JSONArray inline_interval(&inline_intervals);
inline_interval.AddValue(static_cast<intptr_t>(current_pc_offset));
inline_interval.AddValue(
static_cast<intptr_t>(current_pc_offset + delta - 1));
for (intptr_t i = 0; i < function_stack.length(); i++) {
inline_interval.AddValue(function_stack[i]);
}
current_pc_offset += delta;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack.Add(func);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(function_stack.length() > 1);
function_stack.RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
}
#endif // !PRODUCT
void CodeSourceMapReader::DumpInlineIntervals(uword start) {
GrowableArray<const Function*> function_stack;
LogBlock lb;
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack.Add(&root_);
THR_Print("Inline intervals {\n");
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
stream.Read<int32_t>();
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
start + current_pc_offset + delta - 1);
for (intptr_t i = 0; i < function_stack.length(); i++) {
THR_Print("%s ", function_stack[i]->ToCString());
}
THR_Print("\n");
current_pc_offset += delta;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack.Add(
&Function::Handle(Function::RawCast(functions_.At(func))));
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(function_stack.length() > 1);
function_stack.RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
stream.Read<int32_t>();
break;
}
default:
UNREACHABLE();
}
}
THR_Print("}\n");
}
void CodeSourceMapReader::DumpSourcePositions(uword start) {
GrowableArray<const Function*> function_stack;
GrowableArray<TokenPosition> token_positions;
LogBlock lb;
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
function_stack.Add(&root_);
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
THR_Print("Source positions {\n");
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
int32_t position = stream.Read<int32_t>();
token_positions[token_positions.length() - 1] = TokenPosition(position);
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
start + current_pc_offset + delta - 1);
for (intptr_t i = 0; i < function_stack.length(); i++) {
THR_Print("%s@%" Pd " ", function_stack[i]->ToCString(),
token_positions[i].value());
}
THR_Print("\n");
current_pc_offset += delta;
break;
}
case CodeSourceMapBuilder::kPushFunction: {
int32_t func = stream.Read<int32_t>();
function_stack.Add(
&Function::Handle(Function::RawCast(functions_.At(func))));
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
break;
}
case CodeSourceMapBuilder::kPopFunction: {
// We never pop the root function.
ASSERT(function_stack.length() > 1);
ASSERT(token_positions.length() > 1);
function_stack.RemoveLast();
token_positions.RemoveLast();
break;
}
case CodeSourceMapBuilder::kNullCheck: {
const intptr_t name_index = stream.Read<int32_t>();
THR_Print("%" Px "-%" Px ": null check PP#%" Pd "\n",
start + current_pc_offset, start + current_pc_offset,
name_index);
break;
}
default:
UNREACHABLE();
}
}
THR_Print("}\n");
}
intptr_t CodeSourceMapReader::GetNullCheckNameIndexAt(int32_t pc_offset) {
NoSafepointScope no_safepoint;
ReadStream stream(map_.Data(), map_.Length());
int32_t current_pc_offset = 0;
while (stream.PendingBytes() > 0) {
uint8_t opcode = stream.Read<uint8_t>();
switch (opcode) {
case CodeSourceMapBuilder::kChangePosition: {
stream.Read<int32_t>();
break;
}
case CodeSourceMapBuilder::kAdvancePC: {
int32_t delta = stream.Read<int32_t>();
current_pc_offset += delta;
RELEASE_ASSERT(current_pc_offset <= pc_offset);
break;
}
case CodeSourceMapBuilder::kPushFunction: {
stream.Read<int32_t>();
break;
}
case CodeSourceMapBuilder::kPopFunction: {
break;
}
case CodeSourceMapBuilder::kNullCheck: {
const int32_t name_index = stream.Read<int32_t>();
if (current_pc_offset == pc_offset) {
return name_index;
}
break;
}
default:
UNREACHABLE();
}
}
UNREACHABLE();
return -1;
}
} // namespace dart