From 7bac2a95f65418ec18d1fb5f30ae47e91980f30c Mon Sep 17 00:00:00 2001 From: David Chase Date: Mon, 30 Apr 2018 23:52:14 -0400 Subject: [PATCH] cmd/compile: plumb prologueEnd into DWARF This marks the first instruction after the prologue for consumption by debuggers, specifically Delve, who asked for it. gdb appears to ignore it, lldb appears to use it. The bits for end-of-prologue and beginning-of-epilogue are added to Pos (reducing maximum line number by 4x, to 1048575). They're added in cmd/internal/obj/.go (currently x86 only), so the compiler-proper need not deal with them. The linker currently does nothing with beginning-of-epilogue, but the plumbing exists to make it easier in the future. This also upgrades the line number table to DWARF version 3. This CL includes a regression in the coverage for testdata/i22558.gdb-dbg.nexts, this appears to be a gdb artifact but the fix would be in the preceding CL in the stack. Change-Id: I3bda5f46a0ed232d137ad48f65a14835c742c506 Reviewed-on: https://go-review.googlesource.com/110416 Run-TryBot: David Chase TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- src/cmd/internal/obj/pcln.go | 26 +++++++++++++++----- src/cmd/internal/obj/x86/obj6.go | 10 ++++++++ src/cmd/internal/src/pos.go | 40 ++++++++++++++++++++++++++----- src/cmd/internal/src/pos_test.go | 40 +++++++++++++++++++++++++++++++ src/cmd/internal/src/xpos.go | 6 +++++ src/cmd/link/internal/ld/dwarf.go | 21 ++++++++++++---- 6 files changed, 127 insertions(+), 16 deletions(-) diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go index 09b56ac66f..d72d797ee5 100644 --- a/src/cmd/internal/obj/pcln.go +++ b/src/cmd/internal/obj/pcln.go @@ -9,6 +9,11 @@ import ( "log" ) +const ( + PrologueEnd = 2 + iota // overload "is_stmt" to include prologue_end + EpilogueBegin // overload "is_stmt" to include epilogue_end +) + func addvarint(d *Pcdata, v uint32) { for ; v >= 0x80; v >>= 7 { d.P = append(d.P, uint8(v|0x80)) @@ -235,6 +240,7 @@ func pctospadj(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg in // pctostmt returns either, // if phase==0, then whether the current instruction is a step-target (Dwarf is_stmt) +// bit-or'd with whether the current statement is a prologue end or epilogue begin // else (phase == 1), zero. // func pctostmt(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 { @@ -242,14 +248,22 @@ func pctostmt(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg int return 0 // Ignored; also different from initial value of -1, if that ever matters. } s := p.Pos.IsStmt() - if s == src.PosIsStmt { - return 1 + l := p.Pos.Xlogue() + + var is_stmt int32 + + // PrologueEnd, at least, is passed to the next instruction + switch l { + case src.PosPrologueEnd: + is_stmt = PrologueEnd + case src.PosEpilogueBegin: + is_stmt = EpilogueBegin } - if s == src.PosNotStmt { // includes NoSrcPos case - return 0 + + if s != src.PosNotStmt { + is_stmt |= 1 // either PosDefaultStmt from asm, or PosIsStmt from go } - // Line numbers in .s files will have no special setting, therefore default to is_stmt=1. - return 1 + return is_stmt } // pctopcdata computes the pcdata value in effect at p. diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index c2d508d96e..59a2e20d6b 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -33,6 +33,7 @@ package x86 import ( "cmd/internal/obj" "cmd/internal/objabi" + "cmd/internal/src" "cmd/internal/sys" "math" "strings" @@ -676,6 +677,10 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg)) // emit split check } + // Delve debugger would like the next instruction to be noted as the end of the function prologue. + // TODO: are there other cases (e.g., wrapper functions) that need marking? + markedPrologue := false + if autoffset != 0 { if autoffset%int32(ctxt.Arch.RegSize) != 0 { ctxt.Diag("unaligned stack size %d", autoffset) @@ -685,6 +690,8 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.From.Type = obj.TYPE_CONST p.From.Offset = int64(autoffset) p.Spadj = autoffset + p.Pos = p.Pos.WithXlogue(src.PosPrologueEnd) + markedPrologue = true } deltasp := autoffset @@ -700,6 +707,9 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { p.To.Reg = REG_SP p.To.Scale = 1 p.To.Offset = int64(autoffset) - int64(bpsize) + if !markedPrologue { + p.Pos = p.Pos.WithXlogue(src.PosPrologueEnd) + } // Move current frame to BP p = obj.Appendp(p, newprog) diff --git a/src/cmd/internal/src/pos.go b/src/cmd/internal/src/pos.go index 1cf68f28b5..110a57b98d 100644 --- a/src/cmd/internal/src/pos.go +++ b/src/cmd/internal/src/pos.go @@ -293,7 +293,7 @@ func (b *PosBase) InliningIndex() int { // A lico is a compact encoding of a LIne and COlumn number. type lico uint32 -// Layout constants: 22 bits for line, 8 bits for column, 2 for isStmt +// Layout constants: 20 bits for line, 8 bits for column, 2 for isStmt, 2 for pro/epilogue // (If this is too tight, we can either make lico 64b wide, // or we can introduce a tiered encoding where we remove column // information as line numbers grow bigger; similar to what gcc @@ -301,13 +301,18 @@ type lico uint32 // The bitfield order is chosen to make IsStmt be the least significant // part of a position; its use is to communicate statement edges through // instruction scrambling in code generation, not to impose an order. +// TODO: Prologue and epilogue are perhaps better handled as psuedoops for the assembler, +// because they have almost no interaction with other uses of the position. const ( - lineBits, lineMax = 22, 1< lineMax { // cannot represent line, use max. line so we have some information @@ -363,6 +376,9 @@ func (x lico) IsStmt() uint { } return uint(x) >> isStmtShift & isStmtMax } +func (x lico) Xlogue() PosXlogue { + return PosXlogue(uint(x) >> xlogueShift & xlogueMax) +} // withNotStmt returns a lico for the same location, but not a statement func (x lico) withNotStmt() lico { @@ -379,6 +395,18 @@ func (x lico) withIsStmt() lico { return x.withStmt(PosIsStmt) } +// withLogue attaches a prologue/epilogue attribute to a lico +func (x lico) withXlogue(xlogue PosXlogue) lico { + if x == 0 { + if xlogue == 0 { + return x + } + // Normalize 0 to "not a statement" + x = lico(PosNotStmt << isStmtShift) + } + return lico(uint(x) & ^uint(xlogueMax<