2786b9ff43
This CL fixes error message in NoSuchMethodError thrown by null checks and line numbers in corresponding stack traces (in AOT). Name of the called function is placed into object pool, and metadata for null check site is generated in CodeSourceMap. Size of flutter gallery in release mode: Before After RW 2165286 2201642 (+1.68%) RO 2122192 2168224 (+2.17%) RX 6871072 6871072 (+0.00%) Total 11163079 11245467 (+0.74%) Closes https://github.com/dart-lang/sdk/issues/32863 Change-Id: I5ad1190f2ec9452a669863f7dd114ea5f9092d52 Reviewed-on: https://dart-review.googlesource.com/52703 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
653 lines
21 KiB
C++
653 lines
21 KiB
C++
// Copyright (c) 2017, 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/dwarf.h"
|
|
|
|
#include "vm/code_descriptors.h"
|
|
#include "vm/object_store.h"
|
|
|
|
namespace dart {
|
|
|
|
#ifdef DART_PRECOMPILER
|
|
|
|
#if defined(ARCH_IS_32_BIT)
|
|
#define FORM_ADDR ".4byte"
|
|
#elif defined(ARCH_IS_64_BIT)
|
|
#define FORM_ADDR ".8byte"
|
|
#endif
|
|
|
|
class InliningNode : public ZoneAllocated {
|
|
public:
|
|
InliningNode(const Function& function,
|
|
TokenPosition call_pos,
|
|
int32_t start_pc_offset)
|
|
: function(function),
|
|
call_pos(call_pos),
|
|
start_pc_offset(start_pc_offset),
|
|
end_pc_offset(-1),
|
|
children_head(NULL),
|
|
children_tail(NULL),
|
|
children_next(NULL) {
|
|
RELEASE_ASSERT(!function.IsNull());
|
|
RELEASE_ASSERT(function.IsNotTemporaryScopedHandle());
|
|
}
|
|
|
|
void AppendChild(InliningNode* child) {
|
|
if (children_tail == NULL) {
|
|
children_head = children_tail = child;
|
|
} else {
|
|
children_tail->children_next = child;
|
|
children_tail = child;
|
|
}
|
|
}
|
|
|
|
const Function& function;
|
|
TokenPosition call_pos;
|
|
int32_t start_pc_offset;
|
|
int32_t end_pc_offset;
|
|
InliningNode* children_head;
|
|
InliningNode* children_tail;
|
|
InliningNode* children_next;
|
|
};
|
|
|
|
Dwarf::Dwarf(Zone* zone, StreamingWriteStream* stream)
|
|
: zone_(zone),
|
|
stream_(stream),
|
|
codes_(zone, 1024),
|
|
code_to_index_(zone),
|
|
functions_(zone, 1024),
|
|
function_to_index_(zone),
|
|
scripts_(zone, 1024),
|
|
script_to_index_(zone),
|
|
temp_(0) {}
|
|
|
|
intptr_t Dwarf::AddCode(const Code& code) {
|
|
RELEASE_ASSERT(!code.IsNull());
|
|
CodeIndexPair* pair = code_to_index_.Lookup(&code);
|
|
if (pair != NULL) {
|
|
return pair->index_;
|
|
}
|
|
intptr_t index = codes_.length();
|
|
const Code& zone_code = Code::ZoneHandle(zone_, code.raw());
|
|
code_to_index_.Insert(CodeIndexPair(&zone_code, index));
|
|
codes_.Add(&zone_code);
|
|
if (code.IsFunctionCode()) {
|
|
const Function& function = Function::Handle(zone_, code.function());
|
|
AddFunction(function);
|
|
}
|
|
const Array& inline_functions =
|
|
Array::Handle(zone_, code.inlined_id_to_function());
|
|
if (!inline_functions.IsNull()) {
|
|
Function& function = Function::Handle(zone_);
|
|
for (intptr_t i = 0; i < inline_functions.Length(); i++) {
|
|
function ^= inline_functions.At(i);
|
|
AddFunction(function);
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
intptr_t Dwarf::AddFunction(const Function& function) {
|
|
RELEASE_ASSERT(!function.IsNull());
|
|
FunctionIndexPair* pair = function_to_index_.Lookup(&function);
|
|
if (pair != NULL) {
|
|
return pair->index_;
|
|
}
|
|
intptr_t index = functions_.length();
|
|
const Function& zone_func = Function::ZoneHandle(zone_, function.raw());
|
|
function_to_index_.Insert(FunctionIndexPair(&zone_func, index));
|
|
functions_.Add(&zone_func);
|
|
const Script& script = Script::Handle(zone_, function.script());
|
|
AddScript(script);
|
|
return index;
|
|
}
|
|
|
|
intptr_t Dwarf::AddScript(const Script& script) {
|
|
RELEASE_ASSERT(!script.IsNull());
|
|
ScriptIndexPair* pair = script_to_index_.Lookup(&script);
|
|
if (pair != NULL) {
|
|
return pair->index_;
|
|
}
|
|
// DWARF file numbers start from 1.
|
|
intptr_t index = scripts_.length() + 1;
|
|
const Script& zone_script = Script::ZoneHandle(zone_, script.raw());
|
|
script_to_index_.Insert(ScriptIndexPair(&zone_script, index));
|
|
scripts_.Add(&zone_script);
|
|
return index;
|
|
}
|
|
|
|
intptr_t Dwarf::LookupFunction(const Function& function) {
|
|
RELEASE_ASSERT(!function.IsNull());
|
|
FunctionIndexPair* pair = function_to_index_.Lookup(&function);
|
|
if (pair == NULL) {
|
|
FATAL1("Function detected too late during DWARF generation: %s",
|
|
function.ToCString());
|
|
}
|
|
return pair->index_;
|
|
}
|
|
|
|
intptr_t Dwarf::LookupScript(const Script& script) {
|
|
RELEASE_ASSERT(!script.IsNull());
|
|
ScriptIndexPair* pair = script_to_index_.Lookup(&script);
|
|
if (pair == NULL) {
|
|
FATAL1("Script detected too late during DWARF generation: %s",
|
|
script.ToCString());
|
|
}
|
|
return pair->index_;
|
|
}
|
|
|
|
void Dwarf::Print(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
stream_->VPrint(format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void Dwarf::WriteAbbreviations() {
|
|
// Dwarf data mostly takes the form of a tree, whose nodes are called
|
|
// DIEs. Each DIE begins with an abbreviation code, and the abbreviation
|
|
// describes the attributes of that DIE and their representation.
|
|
|
|
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
|
Print(".section __DWARF,__debug_abbrev,regular,debug\n");
|
|
#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
|
defined(TARGET_OS_FUCHSIA)
|
|
Print(".section .debug_abbrev,\"\"\n");
|
|
#else
|
|
UNIMPLEMENTED();
|
|
#endif
|
|
|
|
uleb128(kCompilationUnit); // Abbrev code.
|
|
uleb128(DW_TAG_compile_unit); // Type.
|
|
u1(DW_CHILDREN_yes);
|
|
uleb128(DW_AT_name); // Start of attributes.
|
|
uleb128(DW_FORM_string);
|
|
uleb128(DW_AT_producer);
|
|
uleb128(DW_FORM_string);
|
|
uleb128(DW_AT_comp_dir);
|
|
uleb128(DW_FORM_string);
|
|
uleb128(DW_AT_low_pc);
|
|
uleb128(DW_FORM_addr);
|
|
uleb128(DW_AT_high_pc);
|
|
uleb128(DW_FORM_addr);
|
|
uleb128(DW_AT_stmt_list);
|
|
uleb128(DW_FORM_sec_offset);
|
|
uleb128(0);
|
|
uleb128(0); // End of attributes.
|
|
|
|
uleb128(kAbstractFunction); // Abbrev code.
|
|
uleb128(DW_TAG_subprogram); // Type.
|
|
u1(DW_CHILDREN_yes);
|
|
uleb128(DW_AT_name); // Start of attributes.
|
|
uleb128(DW_FORM_string);
|
|
uleb128(DW_AT_decl_file);
|
|
uleb128(DW_FORM_udata);
|
|
uleb128(DW_AT_decl_line);
|
|
uleb128(DW_FORM_udata);
|
|
uleb128(DW_AT_inline);
|
|
uleb128(DW_FORM_udata);
|
|
uleb128(0);
|
|
uleb128(0); // End of attributes.
|
|
|
|
uleb128(kConcreteFunction); // Abbrev code.
|
|
uleb128(DW_TAG_subprogram); // Type.
|
|
u1(DW_CHILDREN_yes);
|
|
uleb128(DW_AT_abstract_origin); // Start of attributes.
|
|
uleb128(DW_FORM_ref4);
|
|
uleb128(DW_AT_low_pc);
|
|
uleb128(DW_FORM_addr);
|
|
uleb128(DW_AT_high_pc);
|
|
uleb128(DW_FORM_addr);
|
|
uleb128(0);
|
|
uleb128(0); // End of attributes.
|
|
|
|
uleb128(kInlinedFunction); // Abbrev code.
|
|
uleb128(DW_TAG_inlined_subroutine); // Type.
|
|
u1(DW_CHILDREN_yes);
|
|
uleb128(DW_AT_abstract_origin); // Start of attributes.
|
|
uleb128(DW_FORM_ref4);
|
|
uleb128(DW_AT_low_pc);
|
|
uleb128(DW_FORM_addr);
|
|
uleb128(DW_AT_high_pc);
|
|
uleb128(DW_FORM_addr);
|
|
uleb128(DW_AT_call_file);
|
|
uleb128(DW_FORM_udata);
|
|
uleb128(DW_AT_call_line);
|
|
uleb128(DW_FORM_udata);
|
|
uleb128(0);
|
|
uleb128(0); // End of attributes.
|
|
|
|
uleb128(0); // End of abbreviations.
|
|
}
|
|
|
|
void Dwarf::WriteCompilationUnit() {
|
|
// 7.5.1.1 Compilation Unit Header
|
|
|
|
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
|
Print(".section __DWARF,__debug_info,regular,debug\n");
|
|
#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
|
defined(TARGET_OS_FUCHSIA)
|
|
Print(".section .debug_info,\"\"\n");
|
|
#else
|
|
UNIMPLEMENTED();
|
|
#endif
|
|
Print(".Ldebug_info:\n");
|
|
|
|
// Unit length. Assignment to temp works around buggy Mac assembler.
|
|
Print("Lcu_size = .Lcu_end - .Lcu_start\n");
|
|
Print(".4byte Lcu_size\n");
|
|
Print(".Lcu_start:\n");
|
|
|
|
u2(2); // DWARF version 2
|
|
u4(0); // debug_abbrev_offset
|
|
u1(sizeof(void*)); // address_size
|
|
|
|
// Compilation Unit DIE. We describe the entire Dart program as a single
|
|
// compilation unit. Note we write attributes in the same order we declared
|
|
// them in our abbreviation above in WriteAbbreviations.
|
|
uleb128(kCompilationUnit);
|
|
const Library& root_library = Library::Handle(
|
|
zone_, Isolate::Current()->object_store()->root_library());
|
|
const String& root_uri = String::Handle(zone_, root_library.url());
|
|
Print(".string \"%s\"\n", root_uri.ToCString()); // DW_AT_name
|
|
Print(".string \"Dart VM\"\n"); // DW_AT_producer
|
|
Print(".string \"\"\n"); // DW_AT_comp_dir
|
|
|
|
// DW_AT_low_pc
|
|
// The lowest instruction address in this object file that is part of our
|
|
// compilation unit. Dwarf consumers use this to quickly decide which
|
|
// compilation unit DIE to consult for a given pc.
|
|
Print(FORM_ADDR " _kDartIsolateSnapshotInstructions\n");
|
|
|
|
// DW_AT_high_pc
|
|
// The highest instruction address in this object file that is part of our
|
|
// compilation unit. Dwarf consumers use this to quickly decide which
|
|
// compilation unit DIE to consult for a given pc.
|
|
intptr_t last_code_index = codes_.length() - 1;
|
|
const Code& last_code = *(codes_[last_code_index]);
|
|
Print(FORM_ADDR " .Lcode%" Pd " + %" Pd "\n", last_code_index,
|
|
last_code.Size());
|
|
|
|
// DW_AT_stmt_list (offset into .debug_line)
|
|
// Indicates which line number program is associated with this compilation
|
|
// unit. We only emit a single line number program.
|
|
u4(0);
|
|
|
|
WriteAbstractFunctions();
|
|
WriteConcreteFunctions();
|
|
|
|
uleb128(0); // End of children.
|
|
|
|
uleb128(0); // End of entries.
|
|
Print(".Lcu_end:\n");
|
|
}
|
|
|
|
void Dwarf::WriteAbstractFunctions() {
|
|
Script& script = Script::Handle(zone_);
|
|
String& name = String::Handle(zone_);
|
|
for (intptr_t i = 0; i < functions_.length(); i++) {
|
|
const Function& function = *(functions_[i]);
|
|
name = function.QualifiedUserVisibleName();
|
|
script = function.script();
|
|
intptr_t file = LookupScript(script);
|
|
intptr_t line = 0; // Not known. Script has already lost its token stream.
|
|
|
|
Print(".Lfunc%" Pd ":\n", i); // Label for DW_AT_abstract_origin references
|
|
uleb128(kAbstractFunction);
|
|
Print(".string \"%s\"\n", name.ToCString()); // DW_AT_name
|
|
uleb128(file); // DW_AT_decl_file
|
|
uleb128(line); // DW_AT_decl_line
|
|
uleb128(DW_INL_inlined); // DW_AT_inline
|
|
uleb128(0); // End of children.
|
|
}
|
|
}
|
|
|
|
void Dwarf::WriteConcreteFunctions() {
|
|
Function& function = Function::Handle(zone_);
|
|
Script& script = Script::Handle(zone_);
|
|
for (intptr_t i = 0; i < codes_.length(); i++) {
|
|
const Code& code = *(codes_[i]);
|
|
RELEASE_ASSERT(!code.IsNull());
|
|
if (!code.IsFunctionCode()) {
|
|
continue;
|
|
}
|
|
function = code.function();
|
|
intptr_t function_index = LookupFunction(function);
|
|
script = function.script();
|
|
|
|
uleb128(kConcreteFunction);
|
|
// DW_AT_abstract_origin
|
|
// References a node written above in WriteAbstractFunctions.
|
|
// Assignment to temp works around buggy Mac assembler.
|
|
intptr_t temp = temp_++;
|
|
Print("Ltemp%" Pd " = .Lfunc%" Pd " - .Ldebug_info\n", temp,
|
|
function_index);
|
|
Print(".4byte Ltemp%" Pd "\n", temp);
|
|
|
|
// DW_AT_low_pc
|
|
Print(FORM_ADDR " .Lcode%" Pd "\n", i);
|
|
// DW_AT_high_pc
|
|
Print(FORM_ADDR " .Lcode%" Pd " + %" Pd "\n", i, code.Size());
|
|
|
|
InliningNode* node = ExpandInliningTree(code);
|
|
if (node != NULL) {
|
|
for (InliningNode* child = node->children_head; child != NULL;
|
|
child = child->children_next) {
|
|
WriteInliningNode(child, i, script);
|
|
}
|
|
}
|
|
|
|
uleb128(0); // End of children.
|
|
}
|
|
}
|
|
|
|
// Our state machine encodes position metadata such that we don't know the
|
|
// end pc for an inlined function until it is popped, but DWARF DIEs encode
|
|
// it where the function is pushed. We expand the state transitions into
|
|
// an in-memory tree to do the conversion.
|
|
InliningNode* Dwarf::ExpandInliningTree(const Code& code) {
|
|
const CodeSourceMap& map =
|
|
CodeSourceMap::Handle(zone_, code.code_source_map());
|
|
if (map.IsNull()) {
|
|
return NULL;
|
|
}
|
|
const Array& functions = Array::Handle(zone_, code.inlined_id_to_function());
|
|
const Function& root_function = Function::ZoneHandle(zone_, code.function());
|
|
if (root_function.IsNull()) {
|
|
FATAL1("Wherefore art thou functionless code, %s?\n", code.ToCString());
|
|
}
|
|
|
|
GrowableArray<InliningNode*> node_stack(zone_, 4);
|
|
GrowableArray<TokenPosition> token_positions(zone_, 4);
|
|
|
|
NoSafepointScope no_safepoint;
|
|
ReadStream stream(map.Data(), map.Length());
|
|
|
|
int32_t current_pc_offset = 0;
|
|
InliningNode* root_node =
|
|
new (zone_) InliningNode(root_function, TokenPosition(), 0);
|
|
root_node->end_pc_offset = code.Size();
|
|
node_stack.Add(root_node);
|
|
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;
|
|
break;
|
|
}
|
|
case CodeSourceMapBuilder::kPushFunction: {
|
|
int32_t func = stream.Read<int32_t>();
|
|
const Function& child_func =
|
|
Function::ZoneHandle(zone_, Function::RawCast(functions.At(func)));
|
|
TokenPosition call_pos = token_positions.Last();
|
|
InliningNode* child_node =
|
|
new (zone_) InliningNode(child_func, call_pos, current_pc_offset);
|
|
node_stack.Last()->AppendChild(child_node);
|
|
node_stack.Add(child_node);
|
|
token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
|
|
break;
|
|
}
|
|
case CodeSourceMapBuilder::kPopFunction: {
|
|
// We never pop the root function.
|
|
ASSERT(node_stack.length() > 1);
|
|
ASSERT(token_positions.length() > 1);
|
|
node_stack.Last()->end_pc_offset = current_pc_offset;
|
|
node_stack.RemoveLast();
|
|
token_positions.RemoveLast();
|
|
break;
|
|
}
|
|
case CodeSourceMapBuilder::kNullCheck: {
|
|
stream.Read<int32_t>();
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
while (node_stack.length() > 1) {
|
|
node_stack.Last()->end_pc_offset = current_pc_offset;
|
|
node_stack.RemoveLast();
|
|
token_positions.RemoveLast();
|
|
}
|
|
ASSERT(node_stack[0] == root_node);
|
|
return root_node;
|
|
}
|
|
|
|
void Dwarf::WriteInliningNode(InliningNode* node,
|
|
intptr_t root_code_index,
|
|
const Script& parent_script) {
|
|
intptr_t file = LookupScript(parent_script);
|
|
intptr_t line = node->call_pos.value();
|
|
intptr_t function_index = LookupFunction(node->function);
|
|
const Script& script = Script::Handle(zone_, node->function.script());
|
|
|
|
uleb128(kInlinedFunction);
|
|
// DW_AT_abstract_origin
|
|
// References a node written above in WriteAbstractFunctions.
|
|
// Assignment to temp works around buggy Mac assembler.
|
|
intptr_t temp = temp_++;
|
|
Print("Ltemp%" Pd " = .Lfunc%" Pd " - .Ldebug_info\n", temp, function_index);
|
|
Print(".4byte Ltemp%" Pd "\n", temp);
|
|
// DW_AT_low_pc
|
|
Print(FORM_ADDR " .Lcode%" Pd " + %" Pd "\n", root_code_index,
|
|
node->start_pc_offset);
|
|
// DW_AT_high_pc
|
|
Print(FORM_ADDR " .Lcode%" Pd " + %" Pd "\n", root_code_index,
|
|
node->end_pc_offset);
|
|
// DW_AT_call_file
|
|
uleb128(file);
|
|
// DW_AT_call_line
|
|
uleb128(line);
|
|
|
|
for (InliningNode* child = node->children_head; child != NULL;
|
|
child = child->children_next) {
|
|
WriteInliningNode(child, root_code_index, script);
|
|
}
|
|
|
|
uleb128(0); // End of children.
|
|
}
|
|
|
|
void Dwarf::WriteLines() {
|
|
#if defined(TARGET_OS_MACOS) || defined(TARGET_OS_MACOS_IOS)
|
|
Print(".section __DWARF,__debug_line,regular,debug\n");
|
|
#elif defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
|
defined(TARGET_OS_FUCHSIA)
|
|
Print(".section .debug_line,\"\"\n");
|
|
#else
|
|
UNIMPLEMENTED();
|
|
#endif
|
|
|
|
// 6.2.4 The Line Number Program Header
|
|
|
|
// 1. unit_length. This encoding implies 32-bit DWARF.
|
|
Print("Lline_size = .Lline_end - .Lline_start\n");
|
|
Print(".4byte Lline_size\n");
|
|
|
|
Print(".Lline_start:\n");
|
|
|
|
u2(2); // 2. DWARF version 2
|
|
|
|
// 3. header_length
|
|
// Assignment to temp works around buggy Mac assembler.
|
|
Print("Llineheader_size = .Llineheader_end - .Llineheader_start\n");
|
|
Print(".4byte Llineheader_size\n");
|
|
Print(".Llineheader_start:\n");
|
|
|
|
u1(1); // 4. minimum_instruction_length
|
|
u1(0); // 5. default_is_stmt
|
|
u1(0); // 6. line_base
|
|
u1(1); // 7. line_range
|
|
u1(13); // 8. opcode_base (12 standard opcodes in Dwarf 2)
|
|
|
|
// 9. standard_opcode_lengths
|
|
u1(0); // DW_LNS_copy, 0 operands
|
|
u1(1); // DW_LNS_advance_pc, 1 operands
|
|
u1(1); // DW_LNS_advance_list, 1 operands
|
|
u1(1); // DW_LNS_set_file, 1 operands
|
|
u1(1); // DW_LNS_set_column, 1 operands
|
|
u1(0); // DW_LNS_negate_stmt, 0 operands
|
|
u1(0); // DW_LNS_set_basic_block, 0 operands
|
|
u1(0); // DW_LNS_const_add_pc, 0 operands
|
|
u1(1); // DW_LNS_fixed_advance_pc, 1 operands
|
|
u1(0); // DW_LNS_set_prolog_end, 0 operands
|
|
u1(0); // DW_LNS_set_epligoue_begin, 0 operands
|
|
u1(1); // DW_LNS_set_isa, 1 operands
|
|
|
|
// 10. include_directories (sequence of path names)
|
|
// We don't emit any because we use full paths below.
|
|
u1(0);
|
|
|
|
// 11. file_names (sequence of file entries)
|
|
String& uri = String::Handle(zone_);
|
|
for (intptr_t i = 0; i < scripts_.length(); i++) {
|
|
const Script& script = *(scripts_[i]);
|
|
uri ^= script.url();
|
|
Print(".string \"%s\"\n", uri.ToCString());
|
|
uleb128(0); // Include directory index.
|
|
uleb128(0); // File modification time.
|
|
uleb128(0); // File length.
|
|
}
|
|
u1(0); // End of file names.
|
|
|
|
Print(".Llineheader_end:\n");
|
|
|
|
// 6.2.5 The Line Number Program
|
|
|
|
intptr_t previous_file = 1;
|
|
intptr_t previous_line = 1;
|
|
intptr_t previous_code_index = -1;
|
|
intptr_t previous_pc_offset = 0;
|
|
|
|
Function& root_function = Function::Handle(zone_);
|
|
Script& script = Script::Handle(zone_);
|
|
CodeSourceMap& map = CodeSourceMap::Handle(zone_);
|
|
Array& functions = Array::Handle(zone_);
|
|
GrowableArray<const Function*> function_stack(zone_, 8);
|
|
GrowableArray<TokenPosition> token_positions(zone_, 8);
|
|
|
|
for (intptr_t i = 0; i < codes_.length(); i++) {
|
|
const Code& code = *(codes_[i]);
|
|
map = code.code_source_map();
|
|
if (map.IsNull()) {
|
|
continue;
|
|
}
|
|
root_function = code.function();
|
|
functions = code.inlined_id_to_function();
|
|
|
|
NoSafepointScope no_safepoint;
|
|
ReadStream stream(map.Data(), map.Length());
|
|
|
|
function_stack.Clear();
|
|
token_positions.Clear();
|
|
|
|
int32_t current_pc_offset = 0;
|
|
function_stack.Add(&root_function);
|
|
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;
|
|
|
|
const Function& function = *(function_stack.Last());
|
|
script = function.script();
|
|
intptr_t file = LookupScript(script);
|
|
|
|
// 1. Update LNP file.
|
|
if (file != previous_file) {
|
|
u1(DW_LNS_set_file);
|
|
uleb128(file);
|
|
previous_file = file;
|
|
}
|
|
|
|
// 2. Update LNP line.
|
|
TokenPosition pos = token_positions.Last();
|
|
intptr_t line = pos.value();
|
|
if (line != previous_line) {
|
|
u1(DW_LNS_advance_line);
|
|
sleb128(line - previous_line);
|
|
previous_line = line;
|
|
}
|
|
|
|
// 3. Emit LNP row.
|
|
u1(DW_LNS_copy);
|
|
|
|
// 4. Update LNP pc.
|
|
if (previous_code_index == -1) {
|
|
// This variant is relocatable.
|
|
u1(0); // This is an extended opcode
|
|
u1(1 + sizeof(void*)); // that is 5 or 9 bytes long
|
|
u1(DW_LNE_set_address);
|
|
Print(FORM_ADDR " .Lcode%" Pd " + %" Pd "\n", i, current_pc_offset);
|
|
} else {
|
|
u1(DW_LNS_advance_pc);
|
|
Print(".uleb128 .Lcode%" Pd " - .Lcode%" Pd " + %" Pd "\n", i,
|
|
previous_code_index, current_pc_offset - previous_pc_offset);
|
|
}
|
|
previous_code_index = i;
|
|
previous_pc_offset = current_pc_offset;
|
|
break;
|
|
}
|
|
case CodeSourceMapBuilder::kPushFunction: {
|
|
int32_t func_index = stream.Read<int32_t>();
|
|
const Function& child_func = Function::Handle(
|
|
zone_, Function::RawCast(functions.At(func_index)));
|
|
function_stack.Add(&child_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();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Advance pc to end of the compilation unit.
|
|
intptr_t last_code_index = codes_.length() - 1;
|
|
const Code& last_code = *(codes_[last_code_index]);
|
|
u1(DW_LNS_advance_pc);
|
|
Print(".uleb128 .Lcode%" Pd " - .Lcode%" Pd " + %" Pd "\n", last_code_index,
|
|
previous_code_index, last_code.Size() - previous_pc_offset);
|
|
|
|
// End of contiguous machine code.
|
|
u1(0); // This is an extended opcode
|
|
u1(1); // that is 1 byte long
|
|
u1(DW_LNE_end_sequence);
|
|
|
|
Print(".Lline_end:\n");
|
|
}
|
|
|
|
#endif // DART_PRECOMPILER
|
|
|
|
} // namespace dart
|