Add local variables to DWARF.

Change-Id: Ic915643a898e45380710de86a7944cc27403bd7f
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 9dc6565..7696b94 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -293,12 +293,130 @@
   }
 }
 
-struct CompilationUnit {
-  std::vector<const MethodDebugInfo*> methods_;
-  size_t debug_line_offset_ = 0;
-  uint32_t low_pc_ = 0xFFFFFFFFU;
-  uint32_t high_pc_ = 0;
-};
+namespace {
+  struct CompilationUnit {
+    std::vector<const MethodDebugInfo*> methods_;
+    size_t debug_line_offset_ = 0;
+    uint32_t low_pc_ = 0xFFFFFFFFU;
+    uint32_t high_pc_ = 0;
+  };
+
+  struct LocalVariable {
+    uint16_t vreg;
+    uint32_t dex_pc_low;
+    uint32_t dex_pc_high;
+    const char* name;
+    const char* type;
+    const char* sig;
+  };
+
+  struct DebugInfoCallback {
+    static void NewLocal(void* ctx,
+                         uint16_t vreg,
+                         uint32_t start,
+                         uint32_t end,
+                         const char* name,
+                         const char* type,
+                         const char* sig) {
+      auto* context = static_cast<DebugInfoCallback*>(ctx);
+      if (name != nullptr && type != nullptr) {
+        context->local_variables_.push_back({vreg, start, end, name, type, sig});
+      }
+    }
+    std::vector<LocalVariable> local_variables_;
+  };
+
+  std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
+    std::vector<const char*> names;
+    const uint8_t* stream = mi->dex_file_->GetDebugInfoStream(mi->code_item_);
+    if (stream != nullptr) {
+      DecodeUnsignedLeb128(&stream);  // line.
+      uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+      for (uint32_t i = 0; i < parameters_size; ++i) {
+        uint32_t id = DecodeUnsignedLeb128P1(&stream);
+        names.push_back(mi->dex_file_->StringDataByIdx(id));
+      }
+    }
+    return names;
+  }
+
+  struct VariableLocation {
+    uint32_t low_pc;
+    uint32_t high_pc;
+    DexRegisterLocation reg_lo;  // May be None if the location is unknown.
+    DexRegisterLocation reg_hi;  // Most significant bits of 64-bit value.
+  };
+
+  // Get the location of given dex register (e.g. stack or machine register).
+  // Note that the location might be different based on the current pc.
+  // The result will cover all ranges where the variable is in scope.
+  std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
+                                                     uint16_t vreg,
+                                                     bool is64bitValue,
+                                                     uint32_t dex_pc_low,
+                                                     uint32_t dex_pc_high) {
+    std::vector<VariableLocation> variable_locations;
+
+    // Get stack maps sorted by pc (they might not be sorted internally).
+    const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
+    const StackMapEncoding encoding = code_info.ExtractEncoding();
+    std::map<uint32_t, StackMap> stack_maps;
+    for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+      StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+      DCHECK(stack_map.IsValid());
+      const uint32_t low_pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
+      DCHECK_LE(low_pc, method_info->high_pc_);
+      stack_maps.emplace(low_pc, stack_map);
+    }
+
+    // Create entries for the requested register based on stack map data.
+    for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
+      const StackMap& stack_map = it->second;
+      const uint32_t low_pc = it->first;
+      auto next_it = it;
+      next_it++;
+      const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
+                                                           : method_info->high_pc_;
+      DCHECK_LE(low_pc, high_pc);
+      if (low_pc == high_pc) {
+        continue;  // Ignore if the address range is empty.
+      }
+
+      // Check that the stack map is in the requested range.
+      uint32_t dex_pc = stack_map.GetDexPc(encoding);
+      if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
+        continue;
+      }
+
+      // Find the location of the dex register.
+      DexRegisterLocation reg_lo = DexRegisterLocation::None();
+      DexRegisterLocation reg_hi = DexRegisterLocation::None();
+      if (stack_map.HasDexRegisterMap(encoding)) {
+        DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
+            stack_map, encoding, method_info->code_item_->registers_size_);
+        reg_lo = dex_register_map.GetDexRegisterLocation(
+            vreg, method_info->code_item_->registers_size_, code_info, encoding);
+        if (is64bitValue) {
+          reg_hi = dex_register_map.GetDexRegisterLocation(
+              vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
+        }
+      }
+
+      // Add location entry for this address range.
+      if (!variable_locations.empty() &&
+          variable_locations.back().reg_lo == reg_lo &&
+          variable_locations.back().reg_hi == reg_hi &&
+          variable_locations.back().high_pc == low_pc) {
+        // Merge with the previous entry (extend its range).
+        variable_locations.back().high_pc = high_pc;
+      } else {
+        variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
+      }
+    }
+
+    return variable_locations;
+  }
+}  // namespace
 
 // Helper class to write .debug_info and its supporting sections.
 template<typename ElfTypes>
@@ -332,6 +450,7 @@
         const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
         const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
         const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method);
+        const bool is_static = (mi->access_flags_ & kAccStatic) != 0;
 
         // Enclose the method in correct class definition.
         if (last_dex_class_desc != dex_class_desc) {
@@ -346,17 +465,17 @@
           last_dex_class_desc = dex_class_desc;
         }
 
+        // Collect information about local variables and parameters.
+        DebugInfoCallback debug_info_callback;
         std::vector<const char*> param_names;
         if (mi->code_item_ != nullptr) {
-          const uint8_t* stream = dex->GetDebugInfoStream(mi->code_item_);
-          if (stream != nullptr) {
-            DecodeUnsignedLeb128(&stream);  // line.
-            uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
-            for (uint32_t i = 0; i < parameters_size; ++i) {
-              uint32_t id = DecodeUnsignedLeb128P1(&stream);
-              param_names.push_back(mi->dex_file_->StringDataByIdx(id));
-            }
-          }
+          dex->DecodeDebugInfo(mi->code_item_,
+                               is_static,
+                               mi->dex_method_index_,
+                               nullptr,
+                               DebugInfoCallback::NewLocal,
+                               &debug_info_callback);
+          param_names = GetParamNames(mi);
         }
 
         int start_depth = info_.Depth();
@@ -367,19 +486,19 @@
         uint8_t frame_base[] = { DW_OP_call_frame_cfa };
         info_.WriteExprLoc(DW_AT_frame_base, &frame_base, sizeof(frame_base));
         WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto));
+        uint32_t vreg = mi->code_item_ == nullptr ? 0 :
+            mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
+        if (!is_static) {
+          info_.StartTag(DW_TAG_formal_parameter);
+          WriteName("this");
+          info_.WriteFlag(DW_AT_artificial, true);
+          WriteLazyType(dex_class_desc);
+          const bool is64bitValue = false;
+          WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
+          vreg++;
+          info_.EndTag();
+        }
         if (dex_params != nullptr) {
-          uint32_t vreg = mi->code_item_ == nullptr ? 0 :
-              mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
-          if ((mi->access_flags_ & kAccStatic) == 0) {
-            info_.StartTag(DW_TAG_formal_parameter);
-            WriteName("this");
-            info_.WriteFlag(DW_AT_artificial, true);
-            WriteLazyType(dex_class_desc);
-            const bool is64bitValue = false;
-            WriteRegLocation(mi, vreg, is64bitValue, compilation_unit.low_pc_);
-            vreg++;
-            info_.EndTag();
-          }
           for (uint32_t i = 0; i < dex_params->Size(); ++i) {
             info_.StartTag(DW_TAG_formal_parameter);
             // Parameter names may not be always available.
@@ -399,6 +518,18 @@
             CHECK_EQ(vreg, mi->code_item_->registers_size_);
           }
         }
+        for (const LocalVariable& var : debug_info_callback.local_variables_) {
+          const uint32_t first_arg = mi->code_item_->registers_size_ - mi->code_item_->ins_size_;
+          if (var.vreg < first_arg) {
+            info_.StartTag(DW_TAG_variable);
+            WriteName(var.name);
+            WriteLazyType(var.type);
+            bool is64bitValue = var.type[0] == 'D' || var.type[0] == 'J';
+            WriteRegLocation(mi, var.vreg, is64bitValue, compilation_unit.low_pc_,
+                             var.dex_pc_low, var.dex_pc_high);
+            info_.EndTag();
+          }
+        }
         info_.EndTag();
         CHECK_EQ(info_.Depth(), start_depth);  // Balanced start/end.
       }
@@ -420,8 +551,12 @@
     // Write table into .debug_loc which describes location of dex register.
     // The dex register might be valid only at some points and it might
     // move between machine registers and stack.
-    void WriteRegLocation(const MethodDebugInfo* method_info, uint16_t vreg,
-                          bool is64bitValue, uint32_t compilation_unit_low_pc) {
+    void WriteRegLocation(const MethodDebugInfo* method_info,
+                          uint16_t vreg,
+                          bool is64bitValue,
+                          uint32_t compilation_unit_low_pc,
+                          uint32_t dex_pc_low = 0,
+                          uint32_t dex_pc_high = 0xFFFFFFFF) {
       using Kind = DexRegisterLocation::Kind;
       bool is_optimizing = method_info->compiled_method_->GetQuickCode().size() > 0 &&
                            method_info->compiled_method_->GetVmapTable().size() > 0 &&
@@ -431,46 +566,29 @@
         return;
       }
 
-      Writer<> writer(&owner_->debug_loc_);
-      info_.WriteSecOffset(DW_AT_location, writer.size());
+      Writer<> debug_loc(&owner_->debug_loc_);
+      Writer<> debug_ranges(&owner_->debug_ranges_);
+      info_.WriteSecOffset(DW_AT_location, debug_loc.size());
+      info_.WriteSecOffset(DW_AT_start_scope, debug_ranges.size());
 
+      std::vector<VariableLocation> variable_locations = GetVariableLocations(
+          method_info,
+          vreg,
+          is64bitValue,
+          dex_pc_low,
+          dex_pc_high);
+
+      // Write .debug_loc entries.
       const InstructionSet isa = owner_->builder_->GetIsa();
       const bool is64bit = Is64BitInstructionSet(isa);
-      const CodeInfo code_info(method_info->compiled_method_->GetVmapTable().data());
-      const StackMapEncoding encoding = code_info.ExtractEncoding();
-      DexRegisterLocation last_reg_lo = DexRegisterLocation::None();
-      DexRegisterLocation last_reg_hi = DexRegisterLocation::None();
-      size_t offset_of_last_end_address = 0;
-      for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
-        StackMap stack_map = code_info.GetStackMapAt(s, encoding);
-        DCHECK(stack_map.IsValid());
-
-        // Find the location of the dex register.
-        DexRegisterLocation reg_lo = DexRegisterLocation::None();
-        DexRegisterLocation reg_hi = DexRegisterLocation::None();
-        if (stack_map.HasDexRegisterMap(encoding)) {
-          DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
-              stack_map, encoding, method_info->code_item_->registers_size_);
-          reg_lo = dex_register_map.GetDexRegisterLocation(
-              vreg, method_info->code_item_->registers_size_, code_info, encoding);
-          if (is64bitValue) {
-            reg_hi = dex_register_map.GetDexRegisterLocation(
-                vreg + 1, method_info->code_item_->registers_size_, code_info, encoding);
-          }
-        }
-        if ((reg_lo == last_reg_lo && reg_hi == last_reg_hi) ||
-            reg_lo.GetKind() == Kind::kNone) {
-          // Skip identical or undefined locations.
-          continue;
-        }
-        last_reg_lo = reg_lo;
-        last_reg_hi = reg_hi;
-
+      for (const VariableLocation& variable_location : variable_locations) {
         // Translate dex register location to DWARF expression.
         // Note that 64-bit value might be split to two distinct locations.
         // (for example, two 32-bit machine registers, or even stack and register)
         uint8_t buffer[64];
         uint8_t* pos = buffer;
+        DexRegisterLocation reg_lo = variable_location.reg_lo;
+        DexRegisterLocation reg_hi = variable_location.reg_hi;
         for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
           DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
           const Kind kind = reg_loc.GetKind();
@@ -529,43 +647,56 @@
           }
         }
 
-        // Write end address for previous entry.
-        const uint32_t pc = method_info->low_pc_ + stack_map.GetNativePcOffset(encoding);
-        if (offset_of_last_end_address != 0) {
-          if (is64bit) {
-            writer.UpdateUint64(offset_of_last_end_address, pc - compilation_unit_low_pc);
-          } else {
-            writer.UpdateUint32(offset_of_last_end_address, pc - compilation_unit_low_pc);
-          }
-        }
-        offset_of_last_end_address = 0;
-
-        DCHECK_LE(static_cast<size_t>(pos - buffer), sizeof(buffer));
+        // Check that the buffer is large enough; keep half of it empty for safety.
+        DCHECK_LE(static_cast<size_t>(pos - buffer), sizeof(buffer) / 2);
         if (pos > buffer) {
-          // Write start/end address.
           if (is64bit) {
-            writer.PushUint64(pc - compilation_unit_low_pc);
-            offset_of_last_end_address = writer.size();
-            writer.PushUint64(method_info->high_pc_ - compilation_unit_low_pc);
+            debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
+            debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
           } else {
-            writer.PushUint32(pc - compilation_unit_low_pc);
-            offset_of_last_end_address = writer.size();
-            writer.PushUint32(method_info->high_pc_ - compilation_unit_low_pc);
+            debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
+            debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
           }
           // Write the expression.
-          writer.PushUint16(pos - buffer);
-          writer.PushData(buffer, pos - buffer);
+          debug_loc.PushUint16(pos - buffer);
+          debug_loc.PushData(buffer, pos - buffer);
         } else {
-          // Otherwise leave the address range undefined.
+          // Do not generate .debug_loc if the location is not known.
         }
       }
       // Write end-of-list entry.
       if (is64bit) {
-        writer.PushUint64(0);
-        writer.PushUint64(0);
+        debug_loc.PushUint64(0);
+        debug_loc.PushUint64(0);
       } else {
-        writer.PushUint32(0);
-        writer.PushUint32(0);
+        debug_loc.PushUint32(0);
+        debug_loc.PushUint32(0);
+      }
+
+      // Write .debug_ranges entries.
+      // This includes ranges where the variable is in scope but the location is not known.
+      for (size_t i = 0; i < variable_locations.size(); i++) {
+        uint32_t low_pc = variable_locations[i].low_pc;
+        uint32_t high_pc = variable_locations[i].high_pc;
+        while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
+          // Merge address range with the next entry.
+          high_pc = variable_locations[++i].high_pc;
+        }
+        if (is64bit) {
+          debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
+          debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
+        } else {
+          debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
+          debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
+        }
+      }
+      // Write end-of-list entry.
+      if (is64bit) {
+        debug_ranges.PushUint64(0);
+        debug_ranges.PushUint64(0);
+      } else {
+        debug_ranges.PushUint32(0);
+        debug_ranges.PushUint32(0);
       }
     }
 
@@ -748,6 +879,7 @@
     builder_->WriteSection(".debug_abbrev", &debug_abbrev_.Data());
     builder_->WriteSection(".debug_str", &debug_str_.Data());
     builder_->WriteSection(".debug_loc", &debug_loc_);
+    builder_->WriteSection(".debug_ranges", &debug_ranges_);
   }
 
  private:
@@ -760,6 +892,7 @@
   DedupVector debug_abbrev_;
   DedupVector debug_str_;
   std::vector<uint8_t> debug_loc_;
+  std::vector<uint8_t> debug_ranges_;
 
   std::unordered_set<const char*> defined_dex_classes_;  // For CHECKs only.
 };
@@ -820,7 +953,7 @@
 
       struct DebugInfoCallbacks {
         static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
-          auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx);
+          auto* context = static_cast<DebugInfoCallbacks*>(ctx);
           context->dex2line_.push_back({address, static_cast<int32_t>(line)});
           return false;
         }