]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: more efficient encoding of DWARF line number information
authorJohn Dethridge <jcd@golang.org>
Fri, 7 Oct 2016 04:06:09 +0000 (15:06 +1100)
committerJohn Dethridge <jcd@golang.org>
Tue, 11 Oct 2016 01:42:35 +0000 (01:42 +0000)
The (pc, line) deltas in the line number information are currently encoded
either with a special opcode, or with a triplet of DW_LNS_advance_pc,
DW_LNS_advance_line, and DW_LNS_copy instructions.  Instead of DW_LNS_copy,
this change always uses a special opcode, which can make DW_LNS_advance_pc or
DW_LNS_advance_line unnecessary, or make their operands take fewer bytes.  It
chooses the special opcode so that the encoding of the remaining deltas is as
small as possible.

Use DW_LNS_const_add_pc or DW_LNS_fixed_advance_pc instead of DW_LNS_advance_pc
for deltas where they save a byte.

Update LINE_BASE and LINE_RANGE constants to optimal values for this strategy.

This reduces line number information by about 35% and total size by about 2%
for a typical binary.

Change-Id: Ia61d6bf19c95c1d34ba63c67ed32b376beda225f
Reviewed-on: https://go-review.googlesource.com/30577
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/link/internal/ld/dwarf.go

index 0f8cf11b055a84b19150f5c2715e9b026f86858e..61d3e4fb720ecd1299540652816fcc2e11a90807 100644 (file)
@@ -879,32 +879,104 @@ func finddebugruntimepath(s *Symbol) {
 }
 
 /*
- * Generate short opcodes when possible, long ones when necessary.
+ * Generate a sequence of opcodes that is as short as possible.
  * See section 6.2.5
  */
 const (
-       LINE_BASE   = -1
-       LINE_RANGE  = 4
+       LINE_BASE   = -4
+       LINE_RANGE  = 10
+       PC_RANGE    = (255 - OPCODE_BASE) / LINE_RANGE
        OPCODE_BASE = 10
 )
 
-func putpclcdelta(linkctxt *Link, ctxt dwarf.Context, s *Symbol, deltaPC int64, deltaLC int64) {
-       if LINE_BASE <= deltaLC && deltaLC < LINE_BASE+LINE_RANGE {
-               var opcode int64 = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * deltaPC)
-               if OPCODE_BASE <= opcode && opcode < 256 {
-                       Adduint8(linkctxt, s, uint8(opcode))
-                       return
+func putpclcdelta(linkctxt *Link, ctxt dwarf.Context, s *Symbol, deltaPC uint64, deltaLC int64) {
+       // Choose a special opcode that minimizes the number of bytes needed to
+       // encode the remaining PC delta and LC delta.
+       var opcode int64
+       if deltaLC < LINE_BASE {
+               if deltaPC >= PC_RANGE {
+                       opcode = OPCODE_BASE + (LINE_RANGE * PC_RANGE)
+               } else {
+                       opcode = OPCODE_BASE + (LINE_RANGE * int64(deltaPC))
+               }
+       } else if deltaLC < LINE_BASE+LINE_RANGE {
+               if deltaPC >= PC_RANGE {
+                       opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * PC_RANGE)
+                       if opcode > 255 {
+                               opcode -= LINE_RANGE
+                       }
+               } else {
+                       opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * int64(deltaPC))
                }
+       } else {
+               if deltaPC <= PC_RANGE {
+                       opcode = OPCODE_BASE + (LINE_RANGE - 1) + (LINE_RANGE * int64(deltaPC))
+                       if opcode > 255 {
+                               opcode = 255
+                       }
+               } else {
+                       // Use opcode 249 (pc+=23, lc+=5) or 255 (pc+=24, lc+=1).
+                       //
+                       // Let x=deltaPC-PC_RANGE.  If we use opcode 255, x will be the remaining
+                       // deltaPC that we need to encode separately before emitting 255.  If we
+                       // use opcode 249, we will need to encode x+1.  If x+1 takes one more
+                       // byte to encode than x, then we use opcode 255.
+                       //
+                       // In all other cases x and x+1 take the same number of bytes to encode,
+                       // so we use opcode 249, which may save us a byte in encoding deltaLC,
+                       // for similar reasons.
+                       switch deltaPC - PC_RANGE {
+                       // PC_RANGE is the largest deltaPC we can encode in one byte, using
+                       // DW_LNS_const_add_pc.
+                       //
+                       // (1<<16)-1 is the largest deltaPC we can encode in three bytes, using
+                       // DW_LNS_fixed_advance_pc.
+                       //
+                       // (1<<(7n))-1 is the largest deltaPC we can encode in n+1 bytes for
+                       // n=1,3,4,5,..., using DW_LNS_advance_pc.
+                       case PC_RANGE, (1 << 7) - 1, (1 << 16) - 1, (1 << 21) - 1, (1 << 28) - 1,
+                               (1 << 35) - 1, (1 << 42) - 1, (1 << 49) - 1, (1 << 56) - 1, (1 << 63) - 1:
+                               opcode = 255
+                       default:
+                               opcode = OPCODE_BASE + LINE_RANGE*PC_RANGE - 1 // 249
+                       }
+               }
+       }
+       if opcode < OPCODE_BASE || opcode > 255 {
+               panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
        }
 
+       // Subtract from deltaPC and deltaLC the amounts that the opcode will add.
+       deltaPC -= uint64((opcode - OPCODE_BASE) / LINE_RANGE)
+       deltaLC -= int64((opcode-OPCODE_BASE)%LINE_RANGE + LINE_BASE)
+
+       // Encode deltaPC.
        if deltaPC != 0 {
-               Adduint8(linkctxt, s, dwarf.DW_LNS_advance_pc)
-               dwarf.Sleb128put(ctxt, s, deltaPC)
+               if deltaPC <= PC_RANGE {
+                       // Adjust the opcode so that we can use the 1-byte DW_LNS_const_add_pc
+                       // instruction.
+                       opcode -= LINE_RANGE * int64(PC_RANGE-deltaPC)
+                       if opcode < OPCODE_BASE {
+                               panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
+                       }
+                       Adduint8(linkctxt, s, dwarf.DW_LNS_const_add_pc)
+               } else if (1<<14) <= deltaPC && deltaPC < (1<<16) {
+                       Adduint8(linkctxt, s, dwarf.DW_LNS_fixed_advance_pc)
+                       Adduint16(linkctxt, s, uint16(deltaPC))
+               } else {
+                       Adduint8(linkctxt, s, dwarf.DW_LNS_advance_pc)
+                       dwarf.Uleb128put(ctxt, s, int64(deltaPC))
+               }
+       }
+
+       // Encode deltaLC.
+       if deltaLC != 0 {
+               Adduint8(linkctxt, s, dwarf.DW_LNS_advance_line)
+               dwarf.Sleb128put(ctxt, s, deltaLC)
        }
 
-       Adduint8(linkctxt, s, dwarf.DW_LNS_advance_line)
-       dwarf.Sleb128put(ctxt, s, deltaLC)
-       Adduint8(linkctxt, s, dwarf.DW_LNS_copy)
+       // Output the special opcode.
+       Adduint8(linkctxt, s, uint8(opcode))
 }
 
 /*
@@ -1048,7 +1120,7 @@ func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) {
                                file = int(pcfile.value)
                        }
 
-                       putpclcdelta(ctxt, dwarfctxt, ls, s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line))
+                       putpclcdelta(ctxt, dwarfctxt, ls, uint64(s.Value+int64(pcline.pc)-pc), int64(pcline.value)-int64(line))
 
                        pc = s.Value + int64(pcline.pc)
                        line = int(pcline.value)