[vm] Compress CodeSourceMaps by folding opcodes and arguments.

Also delta encode the position/line argument for ChangePosition.

Code size changes on Flutter gallery in release mode from compression:

* ARM7: total -1.39%, readonly -6.47%
* ARM8: total -1.28%, readonly -4.81%

Additional code size changes on Flutter gallery in release mode from
delta encoding position/line:

* ARM7: total -0.48%, readonly -2.37%
* ARM8: total -0.54%, readonly -2.09%

TEST=Existing test on trybots, including stack trace tests and
DWARF decoding tests.

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,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-linux-release-simarm64-try,vm-kernel-linux-release-simarm64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-ia32-try
Change-Id: Ibaf96b1d6c1916ce2f7c71942e333ca7a0b86c3e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175725
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index 12bb667..eecab24 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -361,15 +361,17 @@
     return ((-0x20000000000000LL <= value) && (value <= 0x20000000000000LL));
   }
 
+  static constexpr uword NBitMaskUnsafe(uint32_t n) {
+    static_assert((sizeof(uword) * kBitsPerByte) == kBitsPerWord,
+                  "Unexpected uword size");
+    return n == kBitsPerWord ? std::numeric_limits<uword>::max()
+                             : (static_cast<uword>(1) << n) - 1;
+  }
+
   // The lowest n bits are 1, the others are 0.
   static uword NBitMask(uint32_t n) {
     ASSERT(n <= kBitsPerWord);
-    if (n == kBitsPerWord) {
-      static_assert((sizeof(uword) * kBitsPerByte) == kBitsPerWord,
-                            "Unexpected uword size");
-      return std::numeric_limits<uword>::max();
-    }
-    return (static_cast<uword>(1) << n) - 1;
+    return NBitMaskUnsafe(n);
   }
 
   static word SignedNBitMask(uint32_t n) {
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index 9c5c023..6a75f04 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -237,6 +237,51 @@
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
+uint8_t CodeSourceMapOps::Read(ReadStream* stream,
+                               int32_t* arg1,
+                               int32_t* arg2) {
+  ASSERT(stream != nullptr && arg1 != nullptr);
+  const int32_t n = stream->Read<int32_t>();
+  const uint8_t op = OpField::decode(n);
+  *arg1 = ArgField::decode(n);
+  if (*arg1 > kMaxArgValue) {
+    *arg1 |= kSignBits;
+  }
+#if defined(DART_PRECOMPILER)
+  // The special handling for non-symbolic stack trace mode only needs to
+  // happen in the precompiler, because those CSMs are not serialized in
+  // precompiled snapshots.
+  if (op == kChangePosition && FLAG_dwarf_stack_traces_mode) {
+    const int32_t m = stream->Read<int32_t>();
+    if (arg2 != nullptr) {
+      *arg2 = m;
+    }
+  }
+#endif
+  return op;
+}
+
+void CodeSourceMapOps::Write(BaseWriteStream* stream,
+                             uint8_t op,
+                             int32_t arg1,
+                             int32_t arg2) {
+  ASSERT(stream != nullptr);
+  ASSERT(arg1 >= kMinArgValue && arg1 <= kMaxArgValue);
+  if (arg1 < 0) {
+    arg1 &= ~kSignBits;
+  }
+  const int32_t n = OpField::encode(op) | ArgField::encode(arg1);
+  stream->Write(n);
+#if defined(DART_PRECOMPILER)
+  if (op == kChangePosition && FLAG_dwarf_stack_traces_mode) {
+    // For non-symbolic stack traces, the CodeSourceMaps are not serialized,
+    // so we need not worry about increasing snapshot size by including more
+    // information here.
+    stream->Write(arg2);
+  }
+#endif
+}
+
 const TokenPosition& CodeSourceMapBuilder::kInitialPosition =
     TokenPosition::kDartCodePrologue;
 
@@ -491,10 +536,11 @@
 }
 
 void CodeSourceMapBuilder::WriteChangePosition(const TokenPosition pos) {
-  stream_.Write<uint8_t>(kChangePosition);
-  intptr_t position_or_line = pos.Serialize();
-#if defined(DART_PRECOMPILER)
+  const TokenPosition& last_written = written_token_pos_stack_.Last();
+  intptr_t position_or_line =
+      Utils::SubWithWrapAround(pos.Serialize(), last_written.Serialize());
   intptr_t column = TokenPosition::kNoSource.Serialize();
+#if defined(DART_PRECOMPILER)
   if (FLAG_precompiled_mode) {
     // Don't use the raw position value directly in precompiled mode. Instead,
     // use the value of kNoSource as a fallback when no line or column
@@ -504,17 +550,14 @@
     ASSERT(inline_id < inline_id_to_function_.length());
     script_ = inline_id_to_function_[inline_id]->script();
     script_.GetTokenLocation(pos, &position_or_line, &column);
+    intptr_t old_line = TokenPosition::kNoSource.Serialize();
+    script_.GetTokenLocation(last_written, &old_line);
+    position_or_line =
+        Utils::SubWithWrapAround<int32_t>(position_or_line, old_line);
   }
 #endif
-  stream_.Write<int32_t>(position_or_line);
-#if defined(DART_PRECOMPILER)
-  // For non-symbolic stack traces, the CodeSourceMaps are not serialized,
-  // so we need not worry about increasing snapshot size by including more
-  // information here.
-  if (FLAG_precompiled_mode && FLAG_dwarf_stack_traces_mode) {
-    stream_.Write<int32_t>(column);
-  }
-#endif
+  CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kChangePosition,
+                          position_or_line, column);
   written_token_pos_stack_.Last() = pos;
 }
 
@@ -533,29 +576,31 @@
   token_positions->Add(InitialPosition());
 
   while (stream.PendingBytes() > 0) {
-    uint8_t opcode = stream.Read<uint8_t>();
+    int32_t arg;
+    const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg);
     switch (opcode) {
-      case CodeSourceMapBuilder::kChangePosition: {
+      case CodeSourceMapOps::kChangePosition: {
+        const TokenPosition& old_token =
+            (*token_positions)[token_positions->length() - 1];
         (*token_positions)[token_positions->length() - 1] =
-            ReadPosition(&stream);
+            TokenPosition::Deserialize(
+                Utils::AddWithWrapAround(arg, old_token.Serialize()));
         break;
       }
-      case CodeSourceMapBuilder::kAdvancePC: {
-        int32_t delta = stream.Read<int32_t>();
-        current_pc_offset += delta;
+      case CodeSourceMapOps::kAdvancePC: {
+        current_pc_offset += arg;
         if (current_pc_offset > pc_offset) {
           return;
         }
         break;
       }
-      case CodeSourceMapBuilder::kPushFunction: {
-        int32_t func = stream.Read<int32_t>();
+      case CodeSourceMapOps::kPushFunction: {
         function_stack->Add(
-            &Function::Handle(Function::RawCast(functions_.At(func))));
+            &Function::Handle(Function::RawCast(functions_.At(arg))));
         token_positions->Add(InitialPosition());
         break;
       }
-      case CodeSourceMapBuilder::kPopFunction: {
+      case CodeSourceMapOps::kPopFunction: {
         // We never pop the root function.
         ASSERT(function_stack->length() > 1);
         ASSERT(token_positions->length() > 1);
@@ -563,8 +608,7 @@
         token_positions->RemoveLast();
         break;
       }
-      case CodeSourceMapBuilder::kNullCheck: {
-        stream.Read<int32_t>();
+      case CodeSourceMapOps::kNullCheck: {
         break;
       }
       default:
@@ -594,38 +638,35 @@
   function_stack.Add(0);
 
   while (stream.PendingBytes() > 0) {
-    uint8_t opcode = stream.Read<uint8_t>();
+    int32_t arg;
+    const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg);
     switch (opcode) {
-      case CodeSourceMapBuilder::kChangePosition: {
-        ReadPosition(&stream);
+      case CodeSourceMapOps::kChangePosition: {
         break;
       }
-      case CodeSourceMapBuilder::kAdvancePC: {
-        int32_t delta = stream.Read<int32_t>();
+      case CodeSourceMapOps::kAdvancePC: {
         // 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));
+            static_cast<intptr_t>(current_pc_offset + arg - 1));
         for (intptr_t i = 0; i < function_stack.length(); i++) {
           inline_interval.AddValue(function_stack[i]);
         }
-        current_pc_offset += delta;
+        current_pc_offset += arg;
         break;
       }
-      case CodeSourceMapBuilder::kPushFunction: {
-        int32_t func = stream.Read<int32_t>();
-        function_stack.Add(func);
+      case CodeSourceMapOps::kPushFunction: {
+        function_stack.Add(arg);
         break;
       }
-      case CodeSourceMapBuilder::kPopFunction: {
+      case CodeSourceMapOps::kPopFunction: {
         // We never pop the root function.
         ASSERT(function_stack.length() > 1);
         function_stack.RemoveLast();
         break;
       }
-      case CodeSourceMapBuilder::kNullCheck: {
-        stream.Read<int32_t>();
+      case CodeSourceMapOps::kNullCheck: {
         break;
       }
       default:
@@ -647,37 +688,34 @@
   THR_Print("Inline intervals for function '%s' {\n",
             root_.ToFullyQualifiedCString());
   while (stream.PendingBytes() > 0) {
-    uint8_t opcode = stream.Read<uint8_t>();
+    int32_t arg;
+    const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg);
     switch (opcode) {
-      case CodeSourceMapBuilder::kChangePosition: {
-        ReadPosition(&stream);
+      case CodeSourceMapOps::kChangePosition: {
         break;
       }
-      case CodeSourceMapBuilder::kAdvancePC: {
-        int32_t delta = stream.Read<int32_t>();
+      case CodeSourceMapOps::kAdvancePC: {
         THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
-                  start + current_pc_offset + delta - 1);
+                  start + current_pc_offset + arg - 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;
+        current_pc_offset += arg;
         break;
       }
-      case CodeSourceMapBuilder::kPushFunction: {
-        int32_t func = stream.Read<int32_t>();
+      case CodeSourceMapOps::kPushFunction: {
         function_stack.Add(
-            &Function::Handle(Function::RawCast(functions_.At(func))));
+            &Function::Handle(Function::RawCast(functions_.At(arg))));
         break;
       }
-      case CodeSourceMapBuilder::kPopFunction: {
+      case CodeSourceMapOps::kPopFunction: {
         // We never pop the root function.
         ASSERT(function_stack.length() > 1);
         function_stack.RemoveLast();
         break;
       }
-      case CodeSourceMapBuilder::kNullCheck: {
-        stream.Read<int32_t>();
+      case CodeSourceMapOps::kNullCheck: {
         break;
       }
       default:
@@ -701,32 +739,35 @@
   THR_Print("Source positions for function '%s' {\n",
             root_.ToFullyQualifiedCString());
   while (stream.PendingBytes() > 0) {
-    uint8_t opcode = stream.Read<uint8_t>();
+    int32_t arg;
+    const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg);
     switch (opcode) {
-      case CodeSourceMapBuilder::kChangePosition: {
-        token_positions[token_positions.length() - 1] = ReadPosition(&stream);
+      case CodeSourceMapOps::kChangePosition: {
+        const TokenPosition& old_token =
+            token_positions[token_positions.length() - 1];
+        token_positions[token_positions.length() - 1] =
+            TokenPosition::Deserialize(
+                Utils::AddWithWrapAround(arg, old_token.Serialize()));
         break;
       }
-      case CodeSourceMapBuilder::kAdvancePC: {
-        int32_t delta = stream.Read<int32_t>();
+      case CodeSourceMapOps::kAdvancePC: {
         THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
-                  start + current_pc_offset + delta - 1);
+                  start + current_pc_offset + arg - 1);
         for (intptr_t i = 0; i < function_stack.length(); i++) {
           THR_Print("%s@%s", function_stack[i]->ToCString(),
                     token_positions[i].ToCString());
         }
         THR_Print("\n");
-        current_pc_offset += delta;
+        current_pc_offset += arg;
         break;
       }
-      case CodeSourceMapBuilder::kPushFunction: {
-        int32_t func = stream.Read<int32_t>();
+      case CodeSourceMapOps::kPushFunction: {
         function_stack.Add(
-            &Function::Handle(Function::RawCast(functions_.At(func))));
+            &Function::Handle(Function::RawCast(functions_.At(arg))));
         token_positions.Add(InitialPosition());
         break;
       }
-      case CodeSourceMapBuilder::kPopFunction: {
+      case CodeSourceMapOps::kPopFunction: {
         // We never pop the root function.
         ASSERT(function_stack.length() > 1);
         ASSERT(token_positions.length() > 1);
@@ -734,11 +775,9 @@
         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);
+      case CodeSourceMapOps::kNullCheck: {
+        THR_Print("%" Px "-%" Px ": null check PP#%" Pd32 "\n",
+                  start + current_pc_offset, start + current_pc_offset, arg);
         break;
       }
       default:
@@ -755,29 +794,26 @@
   int32_t current_pc_offset = 0;
 
   while (stream.PendingBytes() > 0) {
-    uint8_t opcode = stream.Read<uint8_t>();
+    int32_t arg;
+    const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg);
     switch (opcode) {
-      case CodeSourceMapBuilder::kChangePosition: {
-        ReadPosition(&stream);
+      case CodeSourceMapOps::kChangePosition: {
         break;
       }
-      case CodeSourceMapBuilder::kAdvancePC: {
-        int32_t delta = stream.Read<int32_t>();
-        current_pc_offset += delta;
+      case CodeSourceMapOps::kAdvancePC: {
+        current_pc_offset += arg;
         RELEASE_ASSERT(current_pc_offset <= pc_offset);
         break;
       }
-      case CodeSourceMapBuilder::kPushFunction: {
-        stream.Read<int32_t>();
+      case CodeSourceMapOps::kPushFunction: {
         break;
       }
-      case CodeSourceMapBuilder::kPopFunction: {
+      case CodeSourceMapOps::kPopFunction: {
         break;
       }
-      case CodeSourceMapBuilder::kNullCheck: {
-        const int32_t name_index = stream.Read<int32_t>();
+      case CodeSourceMapOps::kNullCheck: {
         if (current_pc_offset == pc_offset) {
-          return name_index;
+          return arg;
         }
         break;
       }
@@ -790,18 +826,4 @@
   return -1;
 }
 
-TokenPosition CodeSourceMapReader::ReadPosition(ReadStream* stream) {
-  const TokenPosition line =
-      TokenPosition::Deserialize(stream->Read<int32_t>());
-#if defined(DART_PRECOMPILER)
-  // The special handling for non-symbolic stack trace mode only needs to
-  // happen in the precompiler, because those CSMs are not serialized in
-  // precompiled snapshots.
-  if (FLAG_dwarf_stack_traces_mode) {
-    stream->Read<int32_t>();  // Discard the column information.
-  }
-#endif
-  return line;
-}
-
 }  // namespace dart
diff --git a/runtime/vm/code_descriptors.h b/runtime/vm/code_descriptors.h
index a7e30d0..5356c50 100644
--- a/runtime/vm/code_descriptors.h
+++ b/runtime/vm/code_descriptors.h
@@ -186,6 +186,34 @@
   DISALLOW_ALLOCATION();
 };
 
+struct CodeSourceMapOps : AllStatic {
+  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;
+
+  static uint8_t Read(ReadStream* stream,
+                      int32_t* arg1,
+                      int32_t* arg2 = nullptr);
+
+  static void Write(BaseWriteStream* stream,
+                    uint8_t op,
+                    int32_t arg1 = 0,
+                    int32_t arg2 = 0);
+
+ private:
+  static constexpr intptr_t kOpBits = 3;
+
+  using OpField = BitField<int32_t, uint8_t, 0, kOpBits>;
+  using ArgField = BitField<int32_t, int32_t, OpField::kNextBit>;
+
+  static constexpr int32_t kMaxArgValue =
+      Utils::NBitMaskUnsafe(ArgField::bitsize() - 1);
+  static constexpr int32_t kMinArgValue = ~kMaxArgValue;
+  static constexpr int32_t kSignBits = static_cast<uint32_t>(kMinArgValue) << 1;
+};
+
 // 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.
@@ -210,12 +238,6 @@
   // 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 BeginCodeSourceRange(int32_t pc_offset, const InstructionSource& source);
   void EndCodeSourceRange(int32_t pc_offset, const InstructionSource& source);
   void NoteDescriptor(PcDescriptorsLayout::Kind kind,
@@ -244,8 +266,7 @@
   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);
+    CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kAdvancePC, distance);
     written_pc_offset_ += distance;
   }
   void BufferPush(intptr_t inline_id) {
@@ -253,8 +274,8 @@
     buffered_token_pos_stack_.Add(kInitialPosition);
   }
   void WritePush(intptr_t inline_id) {
-    stream_.Write<uint8_t>(kPushFunction);
-    stream_.Write<int32_t>(GetFunctionId(inline_id));
+    CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kPushFunction,
+                            GetFunctionId(inline_id));
     written_inline_id_stack_.Add(inline_id);
     written_token_pos_stack_.Add(kInitialPosition);
   }
@@ -263,13 +284,12 @@
     buffered_token_pos_stack_.RemoveLast();
   }
   void WritePop() {
-    stream_.Write<uint8_t>(kPopFunction);
+    CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::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);
+    CodeSourceMapOps::Write(&stream_, CodeSourceMapOps::kNullCheck, name_index);
   }
 
   void FlushBuffer();
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index cfafd48..ef08d8c 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -15,21 +15,19 @@
 
 class DwarfPosition {
  public:
-  // The DWARF standard uses 0 to denote missing line or column information.
-  DwarfPosition(intptr_t line, intptr_t column)
-      : line_(line > 0 ? line : 0), column_(column > 0 ? column : 0) {
+  DwarfPosition(int32_t line, int32_t column) : line_(line), column_(column) {
     // Should only have no line information if also no column information.
-    ASSERT(line_ > 0 || column_ == 0);
+    ASSERT(line_ > 0 || column_ <= 0);
   }
-  explicit DwarfPosition(intptr_t line) : DwarfPosition(line, 0) {}
-  constexpr DwarfPosition() : line_(0), column_(0) {}
+  explicit DwarfPosition(int32_t line) : DwarfPosition(line, -1) {}
+  constexpr DwarfPosition() : line_(-1), column_(-1) {}
 
-  intptr_t line() const { return line_; }
-  intptr_t column() const { return column_; }
+  int32_t line() const { return line_; }
+  int32_t column() const { return column_; }
 
  private:
-  intptr_t line_;
-  intptr_t column_;
+  int32_t line_;
+  int32_t column_;
 };
 
 static constexpr auto kNoDwarfPositionInfo = DwarfPosition();
@@ -392,15 +390,6 @@
   }
 }
 
-static DwarfPosition ReadPosition(ReadStream* stream) {
-  const intptr_t line = stream->Read<int32_t>();
-  if (!FLAG_dwarf_stack_traces_mode) {
-    return DwarfPosition(line);
-  }
-  const intptr_t column = stream->Read<int32_t>();
-  return DwarfPosition(line, column);
-}
-
 // 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
@@ -431,21 +420,24 @@
   node_stack.Add(root_node);
 
   while (stream.PendingBytes() > 0) {
-    uint8_t opcode = stream.Read<uint8_t>();
+    int32_t arg1;
+    int32_t arg2 = -1;
+    const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg1, &arg2);
     switch (opcode) {
-      case CodeSourceMapBuilder::kChangePosition: {
-        token_positions[token_positions.length() - 1] = ReadPosition(&stream);
+      case CodeSourceMapOps::kChangePosition: {
+        const DwarfPosition& old_pos =
+            token_positions[token_positions.length() - 1];
+        token_positions[token_positions.length() - 1] =
+            DwarfPosition(Utils::AddWithWrapAround(old_pos.line(), arg1), arg2);
         break;
       }
-      case CodeSourceMapBuilder::kAdvancePC: {
-        int32_t delta = stream.Read<int32_t>();
-        current_pc_offset += delta;
+      case CodeSourceMapOps::kAdvancePC: {
+        current_pc_offset += arg1;
         break;
       }
-      case CodeSourceMapBuilder::kPushFunction: {
-        int32_t func = stream.Read<int32_t>();
+      case CodeSourceMapOps::kPushFunction: {
         const Function& child_func =
-            Function::ZoneHandle(zone_, Function::RawCast(functions.At(func)));
+            Function::ZoneHandle(zone_, Function::RawCast(functions.At(arg1)));
         InliningNode* child_node = new (zone_)
             InliningNode(child_func, token_positions.Last(), current_pc_offset);
         node_stack.Last()->AppendChild(child_node);
@@ -453,7 +445,7 @@
         token_positions.Add(kNoDwarfPositionInfo);
         break;
       }
-      case CodeSourceMapBuilder::kPopFunction: {
+      case CodeSourceMapOps::kPopFunction: {
         // We never pop the root function.
         ASSERT(node_stack.length() > 1);
         ASSERT(token_positions.length() > 1);
@@ -462,8 +454,7 @@
         token_positions.RemoveLast();
         break;
       }
-      case CodeSourceMapBuilder::kNullCheck: {
-        stream.Read<int32_t>();
+      case CodeSourceMapOps::kNullCheck: {
         break;
       }
       default:
@@ -499,10 +490,13 @@
   stream->OffsetFromSymbol(root_asm_name, node->end_pc_offset);
   // DW_AT_call_file
   stream->uleb128(file);
+
+  // The DWARF standard uses 0 to denote missing line or column information.
+
   // DW_AT_call_line
-  stream->uleb128(node->position.line());
+  stream->uleb128(node->position.line() < 0 ? 0 : node->position.line());
   // DW_at_call_column
-  stream->uleb128(node->position.column());
+  stream->uleb128(node->position.column() < 0 ? 0 : node->position.column());
 
   for (InliningNode* child = node->children_head; child != NULL;
        child = child->children_next) {
@@ -609,16 +603,20 @@
     token_positions.Add(kNoDwarfPositionInfo);
 
     while (code_map_stream.PendingBytes() > 0) {
-      uint8_t opcode = code_map_stream.Read<uint8_t>();
+      int32_t arg1;
+      int32_t arg2 = -1;
+      const uint8_t opcode =
+          CodeSourceMapOps::Read(&code_map_stream, &arg1, &arg2);
       switch (opcode) {
-        case CodeSourceMapBuilder::kChangePosition: {
-          token_positions[token_positions.length() - 1] =
-              ReadPosition(&code_map_stream);
+        case CodeSourceMapOps::kChangePosition: {
+          const DwarfPosition& old_pos =
+              token_positions[token_positions.length() - 1];
+          token_positions[token_positions.length() - 1] = DwarfPosition(
+              Utils::AddWithWrapAround(old_pos.line(), arg1), arg2);
           break;
         }
-        case CodeSourceMapBuilder::kAdvancePC: {
-          int32_t delta = code_map_stream.Read<int32_t>();
-          current_pc_offset += delta;
+        case CodeSourceMapOps::kAdvancePC: {
+          current_pc_offset += arg1;
 
           const Function& function = *(function_stack.Last());
           script = function.script();
@@ -632,8 +630,14 @@
           }
 
           // 2. Update LNP line.
-          const intptr_t line = token_positions.Last().line();
-          const intptr_t column = token_positions.Last().column();
+          // The DWARF standard uses 0 to denote missing line or column
+          // information.
+          const intptr_t line = token_positions.Last().line() < 0
+                                    ? 0
+                                    : token_positions.Last().line();
+          const intptr_t column = token_positions.Last().column() < 0
+                                      ? 0
+                                      : token_positions.Last().column();
           if (line != previous_line) {
             stream->u1(DW_LNS_advance_line);
             stream->sleb128(line - previous_line);
@@ -668,15 +672,14 @@
           previous_pc_offset = current_pc_offset;
           break;
         }
-        case CodeSourceMapBuilder::kPushFunction: {
-          int32_t func_index = code_map_stream.Read<int32_t>();
-          const Function& child_func = Function::Handle(
-              zone_, Function::RawCast(functions.At(func_index)));
+        case CodeSourceMapOps::kPushFunction: {
+          const Function& child_func =
+              Function::Handle(zone_, Function::RawCast(functions.At(arg1)));
           function_stack.Add(&child_func);
           token_positions.Add(kNoDwarfPositionInfo);
           break;
         }
-        case CodeSourceMapBuilder::kPopFunction: {
+        case CodeSourceMapOps::kPopFunction: {
           // We never pop the root function.
           ASSERT(function_stack.length() > 1);
           ASSERT(token_positions.length() > 1);
@@ -684,8 +687,7 @@
           token_positions.RemoveLast();
           break;
         }
-        case CodeSourceMapBuilder::kNullCheck: {
-          code_map_stream.Read<int32_t>();
+        case CodeSourceMapOps::kNullCheck: {
           break;
         }
         default: