| Index: runtime/vm/dwarf.cc
 | 
| diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..508bb9de13f20ba1bce97ae006ebee0677272ef6
 | 
| --- /dev/null
 | 
| +++ b/runtime/vm/dwarf.cc
 | 
| @@ -0,0 +1,643 @@
 | 
| +// 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) {}
 | 
| +
 | 
| +  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, WriteStream* 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) {
 | 
| +  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) {
 | 
| +  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) {
 | 
| +  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) {
 | 
| +  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) {
 | 
| +  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(HOST_OS_MACOS)
 | 
| +  Print(".section __DWARF,__debug_abbrev,regular,debug\n");
 | 
| +#else
 | 
| +  Print(".section .debug_abbrev,\"\"\n");
 | 
| +#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(HOST_OS_MACOS)
 | 
| +  Print(".section __DWARF,__debug_info,regular,debug\n");
 | 
| +#else
 | 
| +  Print(".section .debug_info,\"\"\n");
 | 
| +#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]);
 | 
| +    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::Handle(zone_, code.function());
 | 
| +
 | 
| +  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::Handle(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;
 | 
| +      }
 | 
| +      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(HOST_OS_MACOS)
 | 
| +  Print(".section __DWARF,__debug_line,regular,debug\n");
 | 
| +#else
 | 
| +  Print(".section .debug_line,\"\"\n");
 | 
| +#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. 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;
 | 
| +
 | 
| +          // 4. Emit LNP row.
 | 
| +          u1(DW_LNS_copy);
 | 
| +
 | 
| +          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;
 | 
| +        }
 | 
| +        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
 | 
| 
 |