Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
go
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
go
Commits
6f6a9398
Commit
6f6a9398
authored
Aug 11, 2017
by
Austin Clements
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/dev.debug' into master
Change-Id: I85df2745af666b533f4f6f1d06f7c8e137590b5b
parents
3cb41be8
4c54a047
Changes
32
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
1551 additions
and
290 deletions
+1551
-290
src/cmd/compile/fmt_test.go
src/cmd/compile/fmt_test.go
+15
-0
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/gc/main.go
+7
-1
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/pgen.go
+323
-34
src/cmd/compile/internal/gc/scope.go
src/cmd/compile/internal/gc/scope.go
+1
-1
src/cmd/compile/internal/gc/sizeof_test.go
src/cmd/compile/internal/gc/sizeof_test.go
+1
-1
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/ssa.go
+66
-39
src/cmd/compile/internal/gc/syntax.go
src/cmd/compile/internal/gc/syntax.go
+2
-0
src/cmd/compile/internal/ssa/cache.go
src/cmd/compile/internal/ssa/cache.go
+22
-0
src/cmd/compile/internal/ssa/debug.go
src/cmd/compile/internal/ssa/debug.go
+559
-0
src/cmd/compile/internal/ssa/decompose.go
src/cmd/compile/internal/ssa/decompose.go
+1
-0
src/cmd/compile/internal/ssa/export_test.go
src/cmd/compile/internal/ssa/export_test.go
+11
-11
src/cmd/compile/internal/ssa/gen/genericOps.go
src/cmd/compile/internal/ssa/gen/genericOps.go
+1
-0
src/cmd/compile/internal/ssa/html.go
src/cmd/compile/internal/ssa/html.go
+13
-0
src/cmd/compile/internal/ssa/location.go
src/cmd/compile/internal/ssa/location.go
+29
-3
src/cmd/compile/internal/ssa/opGen.go
src/cmd/compile/internal/ssa/opGen.go
+6
-0
src/cmd/compile/internal/ssa/regalloc.go
src/cmd/compile/internal/ssa/regalloc.go
+75
-23
src/cmd/compile/internal/ssa/sizeof_test.go
src/cmd/compile/internal/ssa/sizeof_test.go
+1
-0
src/cmd/compile/internal/ssa/stackalloc.go
src/cmd/compile/internal/ssa/stackalloc.go
+1
-1
src/cmd/compile/internal/ssa/value.go
src/cmd/compile/internal/ssa/value.go
+14
-1
src/cmd/internal/dwarf/dwarf.go
src/cmd/internal/dwarf/dwarf.go
+122
-37
src/cmd/internal/obj/link.go
src/cmd/internal/obj/link.go
+28
-25
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/objfile.go
+10
-7
src/cmd/internal/obj/plist.go
src/cmd/internal/obj/plist.go
+11
-7
src/cmd/internal/obj/x86/a.out.go
src/cmd/internal/obj/x86/a.out.go
+117
-0
src/cmd/internal/obj/x86/obj6.go
src/cmd/internal/obj/x86/obj6.go
+21
-18
src/cmd/internal/objabi/symkind.go
src/cmd/internal/objabi/symkind.go
+1
-0
src/cmd/internal/objabi/symkind_string.go
src/cmd/internal/objabi/symkind_string.go
+2
-2
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/data.go
+24
-19
src/cmd/link/internal/ld/dwarf.go
src/cmd/link/internal/ld/dwarf.go
+61
-53
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/elf.go
+2
-5
src/cmd/link/internal/ld/symkind.go
src/cmd/link/internal/ld/symkind.go
+2
-0
src/cmd/link/internal/ld/symkind_string.go
src/cmd/link/internal/ld/symkind_string.go
+2
-2
No files found.
src/cmd/compile/fmt_test.go
View file @
6f6a9398
...
...
@@ -571,9 +571,16 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/ssa.Block %s"
:
""
,
"*cmd/compile/internal/ssa.Block %v"
:
""
,
"*cmd/compile/internal/ssa.Func %s"
:
""
,
"*cmd/compile/internal/ssa.Func %v"
:
""
,
"*cmd/compile/internal/ssa.FuncDebug %v"
:
""
,
"*cmd/compile/internal/ssa.LocalSlot %+v"
:
""
,
"*cmd/compile/internal/ssa.LocalSlot %v"
:
""
,
"*cmd/compile/internal/ssa.Register %v"
:
""
,
"*cmd/compile/internal/ssa.SparseTreeNode %v"
:
""
,
"*cmd/compile/internal/ssa.Value %s"
:
""
,
"*cmd/compile/internal/ssa.Value %v"
:
""
,
"*cmd/compile/internal/ssa.VarLoc %+v"
:
""
,
"*cmd/compile/internal/ssa.VarLoc %v"
:
""
,
"*cmd/compile/internal/ssa.sparseTreeMapEntry %v"
:
""
,
"*cmd/compile/internal/types.Field %p"
:
""
,
"*cmd/compile/internal/types.Field %v"
:
""
,
...
...
@@ -592,6 +599,7 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/types.Type %p"
:
""
,
"*cmd/compile/internal/types.Type %s"
:
""
,
"*cmd/compile/internal/types.Type %v"
:
""
,
"*cmd/internal/dwarf.Location %#v"
:
""
,
"*cmd/internal/obj.Addr %v"
:
""
,
"*cmd/internal/obj.LSym %v"
:
""
,
"*cmd/internal/obj.Prog %s"
:
""
,
...
...
@@ -600,17 +608,21 @@ var knownFormats = map[string]string{
"[16]byte %x"
:
""
,
"[]*cmd/compile/internal/gc.Node %v"
:
""
,
"[]*cmd/compile/internal/gc.Sig %#v"
:
""
,
"[]*cmd/compile/internal/ssa.Block %+v"
:
""
,
"[]*cmd/compile/internal/ssa.Value %v"
:
""
,
"[][]cmd/compile/internal/ssa.SlotID %v"
:
""
,
"[]byte %s"
:
""
,
"[]byte %x"
:
""
,
"[]cmd/compile/internal/ssa.Edge %v"
:
""
,
"[]cmd/compile/internal/ssa.ID %v"
:
""
,
"[]cmd/compile/internal/ssa.VarLocList %v"
:
""
,
"[]string %v"
:
""
,
"bool %v"
:
""
,
"byte %08b"
:
""
,
"byte %c"
:
""
,
"cmd/compile/internal/arm.shift %d"
:
""
,
"cmd/compile/internal/gc.Class %d"
:
""
,
"cmd/compile/internal/gc.Class %v"
:
""
,
"cmd/compile/internal/gc.Ctype %d"
:
""
,
"cmd/compile/internal/gc.Ctype %v"
:
""
,
"cmd/compile/internal/gc.Level %d"
:
""
,
...
...
@@ -630,11 +642,13 @@ var knownFormats = map[string]string{
"cmd/compile/internal/ssa.Edge %v"
:
""
,
"cmd/compile/internal/ssa.GCNode %v"
:
""
,
"cmd/compile/internal/ssa.ID %d"
:
""
,
"cmd/compile/internal/ssa.ID %v"
:
""
,
"cmd/compile/internal/ssa.LocalSlot %v"
:
""
,
"cmd/compile/internal/ssa.Location %v"
:
""
,
"cmd/compile/internal/ssa.Op %s"
:
""
,
"cmd/compile/internal/ssa.Op %v"
:
""
,
"cmd/compile/internal/ssa.ValAndOff %s"
:
""
,
"cmd/compile/internal/ssa.VarLocList %v"
:
""
,
"cmd/compile/internal/ssa.rbrank %d"
:
""
,
"cmd/compile/internal/ssa.regMask %d"
:
""
,
"cmd/compile/internal/ssa.register %d"
:
""
,
...
...
@@ -648,6 +662,7 @@ var knownFormats = map[string]string{
"cmd/compile/internal/types.EType %d"
:
""
,
"cmd/compile/internal/types.EType %s"
:
""
,
"cmd/compile/internal/types.EType %v"
:
""
,
"cmd/internal/dwarf.Location %#v"
:
""
,
"cmd/internal/src.Pos %s"
:
""
,
"cmd/internal/src.Pos %v"
:
""
,
"error %v"
:
""
,
...
...
src/cmd/compile/internal/gc/main.go
View file @
6f6a9398
...
...
@@ -44,6 +44,7 @@ var (
Debug_vlog
bool
Debug_wb
int
Debug_pctab
string
Debug_locationlist
int
)
// Debug arguments.
...
...
@@ -69,6 +70,7 @@ var debugtab = []struct {
{
"wb"
,
"print information about write barriers"
,
&
Debug_wb
},
{
"export"
,
"print export data"
,
&
Debug_export
},
{
"pctab"
,
"print named pc-value table"
,
&
Debug_pctab
},
{
"locationlists"
,
"print information about DWARF location list creation"
,
&
Debug_locationlist
},
}
const
debugHelpHeader
=
`usage: -d arg[,arg]* and arg is <key>[=<value>]
...
...
@@ -192,6 +194,7 @@ func Main(archInit func(*Arch)) {
flag
.
BoolVar
(
&
pure_go
,
"complete"
,
false
,
"compiling complete package (no C or assembly)"
)
flag
.
StringVar
(
&
debugstr
,
"d"
,
""
,
"print debug information about items in `list`; try -d help"
)
flag
.
BoolVar
(
&
flagDWARF
,
"dwarf"
,
true
,
"generate DWARF symbols"
)
flag
.
BoolVar
(
&
Ctxt
.
Flag_locationlists
,
"dwarflocationlists"
,
false
,
"add location lists to DWARF in optimized mode"
)
objabi
.
Flagcount
(
"e"
,
"no limit on number of errors reported"
,
&
Debug
[
'e'
])
objabi
.
Flagcount
(
"f"
,
"debug stack frames"
,
&
Debug
[
'f'
])
objabi
.
Flagcount
(
"h"
,
"halt on error"
,
&
Debug
[
'h'
])
...
...
@@ -298,6 +301,9 @@ func Main(archInit func(*Arch)) {
if
nBackendWorkers
>
1
&&
!
concurrentBackendAllowed
()
{
log
.
Fatalf
(
"cannot use concurrent backend compilation with provided flags; invoked as %v"
,
os
.
Args
)
}
if
Ctxt
.
Flag_locationlists
&&
len
(
Ctxt
.
Arch
.
DWARFRegisters
)
==
0
{
log
.
Fatalf
(
"location lists requested but register mapping not available on %v"
,
Ctxt
.
Arch
.
Name
)
}
// parse -d argument
if
debugstr
!=
""
{
...
...
@@ -383,7 +389,7 @@ func Main(archInit func(*Arch)) {
Debug
[
'l'
]
=
1
-
Debug
[
'l'
]
}
trackScopes
=
flagDWARF
&&
Debug
[
'l'
]
==
0
&&
Debug
[
'N'
]
!=
0
trackScopes
=
flagDWARF
&&
((
Debug
[
'l'
]
==
0
&&
Debug
[
'N'
]
!=
0
)
||
Ctxt
.
Flag_locationlists
)
Widthptr
=
thearch
.
LinkArch
.
PtrSize
Widthreg
=
thearch
.
LinkArch
.
RegSize
...
...
src/cmd/compile/internal/gc/pgen.go
View file @
6f6a9398
...
...
@@ -13,6 +13,7 @@ import (
"cmd/internal/src"
"cmd/internal/sys"
"fmt"
"math"
"math/rand"
"sort"
"sync"
...
...
@@ -303,29 +304,77 @@ func compileFunctions() {
func
debuginfo
(
fnsym
*
obj
.
LSym
,
curfn
interface
{})
[]
dwarf
.
Scope
{
fn
:=
curfn
.
(
*
Node
)
debugInfo
:=
fn
.
Func
.
DebugInfo
fn
.
Func
.
DebugInfo
=
nil
if
expect
:=
fn
.
Func
.
Nname
.
Sym
.
Linksym
();
fnsym
!=
expect
{
Fatalf
(
"unexpected fnsym: %v != %v"
,
fnsym
,
expect
)
}
var
dwarfVars
[]
*
dwarf
.
Var
var
varScopes
[]
ScopeID
var
automDecls
[]
*
Node
// Populate Automs for fn.
for
_
,
n
:=
range
fn
.
Func
.
Dcl
{
if
n
.
Op
!=
ONAME
{
// might be OTYPE or OLITERAL
continue
}
var
name
obj
.
AddrName
var
abbrev
int
offs
:=
n
.
Xoffset
switch
n
.
Class
()
{
case
PAUTO
:
if
!
n
.
Name
.
Used
()
{
Fatalf
(
"debuginfo unused node (AllocFrame should truncate fn.Func.Dcl)"
)
}
name
=
obj
.
NAME_AUTO
case
PPARAM
,
PPARAMOUT
:
name
=
obj
.
NAME_PARAM
default
:
continue
}
automDecls
=
append
(
automDecls
,
n
)
gotype
:=
ngotype
(
n
)
.
Linksym
()
fnsym
.
Func
.
Autom
=
append
(
fnsym
.
Func
.
Autom
,
&
obj
.
Auto
{
Asym
:
Ctxt
.
Lookup
(
n
.
Sym
.
Name
),
Aoffset
:
int32
(
n
.
Xoffset
),
Name
:
name
,
Gotype
:
gotype
,
})
}
var
dwarfVars
[]
*
dwarf
.
Var
var
decls
[]
*
Node
if
Ctxt
.
Flag_locationlists
&&
Ctxt
.
Flag_optimize
{
decls
,
dwarfVars
=
createComplexVars
(
fn
,
debugInfo
)
}
else
{
decls
,
dwarfVars
=
createSimpleVars
(
automDecls
)
}
var
varScopes
[]
ScopeID
for
_
,
decl
:=
range
decls
{
var
scope
ScopeID
if
!
decl
.
Name
.
Captured
()
&&
!
decl
.
Name
.
Byval
()
{
// n.Pos of captured variables is their first
// use in the closure but they should always
// be assigned to scope 0 instead.
// TODO(mdempsky): Verify this.
scope
=
findScope
(
fn
.
Func
.
Marks
,
decl
.
Pos
)
}
varScopes
=
append
(
varScopes
,
scope
)
}
return
assembleScopes
(
fnsym
,
fn
,
dwarfVars
,
varScopes
)
}
// createSimpleVars creates a DWARF entry for every variable declared in the
// function, claiming that they are permanently on the stack.
func
createSimpleVars
(
automDecls
[]
*
Node
)
([]
*
Node
,
[]
*
dwarf
.
Var
)
{
var
vars
[]
*
dwarf
.
Var
var
decls
[]
*
Node
for
_
,
n
:=
range
automDecls
{
if
n
.
IsAutoTmp
()
{
continue
}
var
abbrev
int
offs
:=
n
.
Xoffset
switch
n
.
Class
()
{
case
PAUTO
:
abbrev
=
dwarf
.
DW_ABRV_AUTO
if
Ctxt
.
FixedFrameSize
()
==
0
{
offs
-=
int64
(
Widthptr
)
...
...
@@ -335,48 +384,288 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
}
case
PPARAM
,
PPARAMOUT
:
name
=
obj
.
NAME_PARAM
abbrev
=
dwarf
.
DW_ABRV_PARAM
offs
+=
Ctxt
.
FixedFrameSize
()
default
:
continue
Fatalf
(
"createSimpleVars unexpected type %v for node %v"
,
n
.
Class
(),
n
)
}
gotype
:=
ngotype
(
n
)
.
Linksym
()
fnsym
.
Func
.
Autom
=
append
(
fnsym
.
Func
.
Autom
,
&
obj
.
Auto
{
Asym
:
Ctxt
.
Lookup
(
n
.
Sym
.
Name
),
Aoffset
:
int32
(
n
.
Xoffset
),
Name
:
name
,
Gotype
:
gotype
,
typename
:=
dwarf
.
InfoPrefix
+
typesymname
(
n
.
Type
)
decls
=
append
(
decls
,
n
)
vars
=
append
(
vars
,
&
dwarf
.
Var
{
Name
:
n
.
Sym
.
Name
,
Abbrev
:
abbrev
,
StackOffset
:
int32
(
offs
),
Type
:
Ctxt
.
Lookup
(
typename
),
})
}
return
decls
,
vars
}
if
n
.
IsAutoTmp
()
{
type
varPart
struct
{
varOffset
int64
slot
ssa
.
SlotID
locs
ssa
.
VarLocList
}
func
createComplexVars
(
fn
*
Node
,
debugInfo
*
ssa
.
FuncDebug
)
([]
*
Node
,
[]
*
dwarf
.
Var
)
{
for
_
,
locList
:=
range
debugInfo
.
Variables
{
for
_
,
loc
:=
range
locList
.
Locations
{
if
loc
.
StartProg
!=
nil
{
loc
.
StartPC
=
loc
.
StartProg
.
Pc
}
if
loc
.
EndProg
!=
nil
{
loc
.
EndPC
=
loc
.
EndProg
.
Pc
}
if
Debug_locationlist
==
0
{
loc
.
EndProg
=
nil
loc
.
StartProg
=
nil
}
}
}
// Group SSA variables by the user variable they were decomposed from.
varParts
:=
map
[
*
Node
][]
varPart
{}
for
slotID
,
locList
:=
range
debugInfo
.
Variables
{
if
len
(
locList
.
Locations
)
==
0
{
continue
}
slot
:=
debugInfo
.
Slots
[
slotID
]
for
slot
.
SplitOf
!=
nil
{
slot
=
slot
.
SplitOf
}
n
:=
slot
.
N
.
(
*
Node
)
varParts
[
n
]
=
append
(
varParts
[
n
],
varPart
{
varOffset
(
slot
),
ssa
.
SlotID
(
slotID
),
locList
})
}
typename
:=
dwarf
.
InfoPrefix
+
gotype
.
Name
[
len
(
"type."
)
:
]
dwarfVars
=
append
(
dwarfVars
,
&
dwarf
.
Var
{
Name
:
n
.
Sym
.
Name
,
Abbrev
:
abbrev
,
Offset
:
int32
(
offs
),
Type
:
Ctxt
.
Lookup
(
typename
),
})
// Produce a DWARF variable entry for each user variable.
// Don't iterate over the map -- that's nondeterministic, and
// createComplexVar has side effects. Instead, go by slot.
var
decls
[]
*
Node
var
vars
[]
*
dwarf
.
Var
for
_
,
slot
:=
range
debugInfo
.
Slots
{
for
slot
.
SplitOf
!=
nil
{
slot
=
slot
.
SplitOf
}
n
:=
slot
.
N
.
(
*
Node
)
parts
:=
varParts
[
n
]
if
parts
==
nil
{
continue
}
var
scope
ScopeID
if
!
n
.
Name
.
Captured
()
&&
!
n
.
Name
.
Byval
()
{
// n.Pos of captured variables is their first
// use in the closure but they should always
// be assigned to scope 0 instead.
// TODO(mdempsky): Verify this.
scope
=
findScope
(
fn
.
Func
.
Marks
,
n
.
Pos
)
// Get the order the parts need to be in to represent the memory
// of the decomposed user variable.
sort
.
Sort
(
partsByVarOffset
(
parts
))
if
dvar
:=
createComplexVar
(
debugInfo
,
n
,
parts
);
dvar
!=
nil
{
decls
=
append
(
decls
,
n
)
vars
=
append
(
vars
,
dvar
)
}
}
return
decls
,
vars
}
varScopes
=
append
(
varScopes
,
scope
)
// varOffset returns the offset of slot within the user variable it was
// decomposed from. This has nothing to do with its stack offset.
func
varOffset
(
slot
*
ssa
.
LocalSlot
)
int64
{
offset
:=
slot
.
Off
for
;
slot
.
SplitOf
!=
nil
;
slot
=
slot
.
SplitOf
{
offset
+=
slot
.
SplitOffset
}
return
offset
}
return
assembleScopes
(
fnsym
,
fn
,
dwarfVars
,
varScopes
)
type
partsByVarOffset
[]
varPart
func
(
a
partsByVarOffset
)
Len
()
int
{
return
len
(
a
)
}
func
(
a
partsByVarOffset
)
Less
(
i
,
j
int
)
bool
{
return
a
[
i
]
.
varOffset
<
a
[
j
]
.
varOffset
}
func
(
a
partsByVarOffset
)
Swap
(
i
,
j
int
)
{
a
[
i
],
a
[
j
]
=
a
[
j
],
a
[
i
]
}
// createComplexVar builds a DWARF variable entry and location list representing n.
func
createComplexVar
(
debugInfo
*
ssa
.
FuncDebug
,
n
*
Node
,
parts
[]
varPart
)
*
dwarf
.
Var
{
slots
:=
debugInfo
.
Slots
var
offs
int64
// base stack offset for this kind of variable
var
abbrev
int
switch
n
.
Class
()
{
case
PAUTO
:
abbrev
=
dwarf
.
DW_ABRV_AUTO_LOCLIST
if
Ctxt
.
FixedFrameSize
()
==
0
{
offs
-=
int64
(
Widthptr
)
}
if
objabi
.
Framepointer_enabled
(
objabi
.
GOOS
,
objabi
.
GOARCH
)
{
offs
-=
int64
(
Widthptr
)
}
case
PPARAM
,
PPARAMOUT
:
abbrev
=
dwarf
.
DW_ABRV_PARAM_LOCLIST
offs
+=
Ctxt
.
FixedFrameSize
()
default
:
return
nil
}
gotype
:=
ngotype
(
n
)
.
Linksym
()
typename
:=
dwarf
.
InfoPrefix
+
gotype
.
Name
[
len
(
"type."
)
:
]
// The stack offset is used as a sorting key, so for decomposed
// variables just give it the lowest one. It's not used otherwise.
stackOffset
:=
debugInfo
.
Slots
[
parts
[
0
]
.
slot
]
.
N
.
(
*
Node
)
.
Xoffset
+
offs
dvar
:=
&
dwarf
.
Var
{
Name
:
n
.
Sym
.
Name
,
Abbrev
:
abbrev
,
Type
:
Ctxt
.
Lookup
(
typename
),
StackOffset
:
int32
(
stackOffset
),
}
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"Building location list for %+v. Parts:
\n
"
,
n
)
for
_
,
part
:=
range
parts
{
Ctxt
.
Logf
(
"
\t
%v => %v
\n
"
,
debugInfo
.
Slots
[
part
.
slot
],
part
.
locs
)
}
}
// Given a variable that's been decomposed into multiple parts,
// its location list may need a new entry after the beginning or
// end of every location entry for each of its parts. For example:
//
// [variable] [pc range]
// string.ptr |----|-----| |----|
// string.len |------------| |--|
// ... needs a location list like:
// string |----|-----|-| |--|-|
//
// Note that location entries may or may not line up with each other,
// and some of the result will only have one or the other part.
//
// To build the resulting list:
// - keep a "current" pointer for each part
// - find the next transition point
// - advance the current pointer for each part up to that transition point
// - build the piece for the range between that transition point and the next
// - repeat
curLoc
:=
make
([]
int
,
len
(
slots
))
// findBoundaryAfter finds the next beginning or end of a piece after currentPC.
findBoundaryAfter
:=
func
(
currentPC
int64
)
int64
{
min
:=
int64
(
math
.
MaxInt64
)
for
slot
,
part
:=
range
parts
{
// For each part, find the first PC greater than current. Doesn't
// matter if it's a start or an end, since we're looking for any boundary.
// If it's the new winner, save it.
onePart
:
for
i
:=
curLoc
[
slot
];
i
<
len
(
part
.
locs
.
Locations
);
i
++
{
for
_
,
pc
:=
range
[
2
]
int64
{
part
.
locs
.
Locations
[
i
]
.
StartPC
,
part
.
locs
.
Locations
[
i
]
.
EndPC
}
{
if
pc
>
currentPC
{
if
pc
<
min
{
min
=
pc
}
break
onePart
}
}
}
}
return
min
}
var
start
int64
end
:=
findBoundaryAfter
(
0
)
for
{
// Advance to the next chunk.
start
=
end
end
=
findBoundaryAfter
(
start
)
if
end
==
math
.
MaxInt64
{
break
}
dloc
:=
dwarf
.
Location
{
StartPC
:
start
,
EndPC
:
end
}
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"Processing range %x -> %x
\n
"
,
start
,
end
)
}
// Advance curLoc to the last location that starts before/at start.
// After this loop, if there's a location that covers [start, end), it will be current.
// Otherwise the current piece will be too early.
for
_
,
part
:=
range
parts
{
choice
:=
-
1
for
i
:=
curLoc
[
part
.
slot
];
i
<
len
(
part
.
locs
.
Locations
);
i
++
{
if
part
.
locs
.
Locations
[
i
]
.
StartPC
>
start
{
break
//overshot
}
choice
=
i
// best yet
}
if
choice
!=
-
1
{
curLoc
[
part
.
slot
]
=
choice
}
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"
\t
%v => %v"
,
slots
[
part
.
slot
],
curLoc
[
part
.
slot
])
}
}
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"
\n
"
)
}
// Assemble the location list entry for this chunk.
present
:=
0
for
_
,
part
:=
range
parts
{
dpiece
:=
dwarf
.
Piece
{
Length
:
slots
[
part
.
slot
]
.
Type
.
Size
(),
}
locIdx
:=
curLoc
[
part
.
slot
]
if
locIdx
>=
len
(
part
.
locs
.
Locations
)
||
start
>=
part
.
locs
.
Locations
[
locIdx
]
.
EndPC
||
end
<=
part
.
locs
.
Locations
[
locIdx
]
.
StartPC
{
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"
\t
%v: missing"
,
slots
[
part
.
slot
])
}
dpiece
.
Missing
=
true
dloc
.
Pieces
=
append
(
dloc
.
Pieces
,
dpiece
)
continue
}
present
++
loc
:=
part
.
locs
.
Locations
[
locIdx
]
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"
\t
%v: %v"
,
slots
[
part
.
slot
],
loc
)
}
if
loc
.
OnStack
{
dpiece
.
OnStack
=
true
dpiece
.
StackOffset
=
int32
(
offs
+
slots
[
part
.
slot
]
.
Off
+
slots
[
part
.
slot
]
.
N
.
(
*
Node
)
.
Xoffset
)
}
else
{
for
reg
:=
0
;
reg
<
len
(
debugInfo
.
Registers
);
reg
++
{
if
loc
.
Registers
&
(
1
<<
uint8
(
reg
))
!=
0
{
dpiece
.
RegNum
=
Ctxt
.
Arch
.
DWARFRegisters
[
debugInfo
.
Registers
[
reg
]
.
ObjNum
()]
}
}
}
dloc
.
Pieces
=
append
(
dloc
.
Pieces
,
dpiece
)
}
if
present
==
0
{
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
" -> totally missing
\n
"
)
}
continue
}
// Extend the previous entry if possible.
if
len
(
dvar
.
LocationList
)
>
0
{
prev
:=
&
dvar
.
LocationList
[
len
(
dvar
.
LocationList
)
-
1
]
if
prev
.
EndPC
==
dloc
.
StartPC
&&
len
(
prev
.
Pieces
)
==
len
(
dloc
.
Pieces
)
{
equal
:=
true
for
i
:=
range
prev
.
Pieces
{
if
prev
.
Pieces
[
i
]
!=
dloc
.
Pieces
[
i
]
{
equal
=
false
}
}
if
equal
{
prev
.
EndPC
=
end
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"-> merged with previous, now %#v
\n
"
,
prev
)
}
continue
}
}
}
dvar
.
LocationList
=
append
(
dvar
.
LocationList
,
dloc
)
if
Debug_locationlist
!=
0
{
Ctxt
.
Logf
(
"-> added: %#v
\n
"
,
dloc
)
}
}
return
dvar
}
// fieldtrack adds R_USEFIELD relocations to fnsym to record any
...
...
src/cmd/compile/internal/gc/scope.go
View file @
6f6a9398
...
...
@@ -168,7 +168,7 @@ func (v varsByScopeAndOffset) Less(i, j int) bool {
if
v
.
scopes
[
i
]
!=
v
.
scopes
[
j
]
{
return
v
.
scopes
[
i
]
<
v
.
scopes
[
j
]
}
return
v
.
vars
[
i
]
.
Offset
<
v
.
vars
[
j
]
.
Offset
return
v
.
vars
[
i
]
.
StackOffset
<
v
.
vars
[
j
]
.
Stack
Offset
}
func
(
v
varsByScopeAndOffset
)
Swap
(
i
,
j
int
)
{
...
...
src/cmd/compile/internal/gc/sizeof_test.go
View file @
6f6a9398
...
...
@@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) {
_32bit
uintptr
// size on 32bit platforms
_64bit
uintptr
// size on 64bit platforms
}{
{
Func
{},
12
4
,
216
},
{
Func
{},
12
8
,
224
},
{
Name
{},
36
,
56
},
{
Param
{},
28
,
56
},
{
Node
{},
76
,
128
},
...
...
src/cmd/compile/internal/gc/ssa.go
View file @
6f6a9398
...
...
@@ -4386,14 +4386,15 @@ func genssa(f *ssa.Func, pp *Progs) {
// Remember where each block starts.
s
.
bstart
=
make
([]
*
obj
.
Prog
,
f
.
NumBlocks
())
s
.
pp
=
pp
var
valueProgs
map
[
*
obj
.
Prog
]
*
ssa
.
Value
var
blockProgs
map
[
*
obj
.
Prog
]
*
ssa
.
Block
var
progToValue
map
[
*
obj
.
Prog
]
*
ssa
.
Value
var
progToBlock
map
[
*
obj
.
Prog
]
*
ssa
.
Block
var
valueToProg
[]
*
obj
.
Prog
var
logProgs
=
e
.
log
if
logProgs
{
valueProgs
=
make
(
map
[
*
obj
.
Prog
]
*
ssa
.
Value
,
f
.
NumValues
())
blockProgs
=
make
(
map
[
*
obj
.
Prog
]
*
ssa
.
Block
,
f
.
NumBlocks
())
progToValue
=
make
(
map
[
*
obj
.
Prog
]
*
ssa
.
Value
,
f
.
NumValues
())
progToBlock
=
make
(
map
[
*
obj
.
Prog
]
*
ssa
.
Block
,
f
.
NumBlocks
())
f
.
Logf
(
"genssa %s
\n
"
,
f
.
Name
)
blockProgs
[
s
.
pp
.
next
]
=
f
.
Blocks
[
0
]
progToBlock
[
s
.
pp
.
next
]
=
f
.
Blocks
[
0
]
}
if
thearch
.
Use387
{
...
...
@@ -4402,6 +4403,11 @@ func genssa(f *ssa.Func, pp *Progs) {
s
.
ScratchFpMem
=
e
.
scratchFpMem
logLocationLists
:=
Debug_locationlist
!=
0
if
Ctxt
.
Flag_locationlists
{
e
.
curfn
.
Func
.
DebugInfo
=
ssa
.
BuildFuncDebug
(
f
,
logLocationLists
)
valueToProg
=
make
([]
*
obj
.
Prog
,
f
.
NumValues
())
}
// Emit basic blocks
for
i
,
b
:=
range
f
.
Blocks
{
s
.
bstart
[
b
.
ID
]
=
s
.
pp
.
next
...
...
@@ -4442,15 +4448,19 @@ func genssa(f *ssa.Func, pp *Progs) {
}
case
ssa
.
OpPhi
:
CheckLoweredPhi
(
v
)
case
ssa
.
OpRegKill
:
// nothing to do
default
:
// let the backend handle it
thearch
.
SSAGenValue
(
&
s
,
v
)
}
if
Ctxt
.
Flag_locationlists
{
valueToProg
[
v
.
ID
]
=
x
}
if
logProgs
{
for
;
x
!=
s
.
pp
.
next
;
x
=
x
.
Link
{
valueProgs
[
x
]
=
v
progToValue
[
x
]
=
v
}
}
}
...
...
@@ -4468,7 +4478,23 @@ func genssa(f *ssa.Func, pp *Progs) {
thearch
.
SSAGenBlock
(
&
s
,
b
,
next
)
if
logProgs
{
for
;
x
!=
s
.
pp
.
next
;
x
=
x
.
Link
{
blockProgs
[
x
]
=
b
progToBlock
[
x
]
=
b
}
}
}
if
Ctxt
.
Flag_locationlists
{
for
_
,
locList
:=
range
e
.
curfn
.
Func
.
DebugInfo
.
Variables
{
for
_
,
loc
:=
range
locList
.
Locations
{
loc
.
StartProg
=
valueToProg
[
loc
.
Start
.
ID
]
if
loc
.
End
==
nil
{
Fatalf
(
"empty loc %v compiling %v"
,
loc
,
f
.
Name
)
}
loc
.
EndProg
=
valueToProg
[
loc
.
End
.
ID
]
if
!
logLocationLists
{
loc
.
Start
=
nil
loc
.
End
=
nil
}
}
}
}
...
...
@@ -4481,9 +4507,9 @@ func genssa(f *ssa.Func, pp *Progs) {
if
logProgs
{
for
p
:=
pp
.
Text
;
p
!=
nil
;
p
=
p
.
Link
{
var
s
string
if
v
,
ok
:=
valueProgs
[
p
];
ok
{
if
v
,
ok
:=
progToValue
[
p
];
ok
{
s
=
v
.
String
()
}
else
if
b
,
ok
:=
blockProgs
[
p
];
ok
{
}
else
if
b
,
ok
:=
progToBlock
[
p
];
ok
{
s
=
b
.
String
()
}
else
{
s
=
" "
// most value and branch strings are 2-3 characters long
...
...
@@ -4501,9 +4527,9 @@ func genssa(f *ssa.Func, pp *Progs) {
buf
.
WriteString
(
"<dl class=
\"
ssa-gen
\"
>"
)
for
p
:=
pp
.
Text
;
p
!=
nil
;
p
=
p
.
Link
{
buf
.
WriteString
(
"<dt class=
\"
ssa-prog-src
\"
>"
)
if
v
,
ok
:=
valueProgs
[
p
];
ok
{
if
v
,
ok
:=
progToValue
[
p
];
ok
{
buf
.
WriteString
(
v
.
HTML
())
}
else
if
b
,
ok
:=
blockProgs
[
p
];
ok
{
}
else
if
b
,
ok
:=
progToBlock
[
p
];
ok
{
buf
.
WriteString
(
b
.
HTML
())
}
buf
.
WriteString
(
"</dt>"
)
...
...
@@ -4880,9 +4906,9 @@ func (e *ssafn) SplitString(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
lenType
:=
types
.
Types
[
TINT
]
if
n
.
Class
()
==
PAUTO
&&
!
n
.
Addrtaken
()
{
// Split this string up into two separate variables.
p
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".ptr"
,
ptrType
,
n
.
Pos
)
l
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".len"
,
lenType
,
n
.
Pos
)
return
ssa
.
LocalSlot
{
N
:
p
,
Type
:
ptrType
,
Off
:
0
},
ssa
.
LocalSlot
{
N
:
l
,
Type
:
lenType
,
Off
:
0
}
p
:=
e
.
splitSlot
(
&
name
,
".ptr"
,
0
,
ptrType
)
l
:=
e
.
splitSlot
(
&
name
,
".len"
,
ptrType
.
Size
(),
lenType
)
return
p
,
l
}
// Return the two parts of the larger variable.
return
ssa
.
LocalSlot
{
N
:
n
,
Type
:
ptrType
,
Off
:
name
.
Off
},
ssa
.
LocalSlot
{
N
:
n
,
Type
:
lenType
,
Off
:
name
.
Off
+
int64
(
Widthptr
)}
...
...
@@ -4897,9 +4923,9 @@ func (e *ssafn) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot
if
n
.
Type
.
IsEmptyInterface
()
{
f
=
".type"
}
c
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
f
,
t
,
n
.
Pos
)
d
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".data"
,
t
,
n
.
Pos
)
return
ssa
.
LocalSlot
{
N
:
c
,
Type
:
t
,
Off
:
0
},
ssa
.
LocalSlot
{
N
:
d
,
Type
:
t
,
Off
:
0
}
c
:=
e
.
splitSlot
(
&
name
,
f
,
0
,
t
)
d
:=
e
.
splitSlot
(
&
name
,
".data"
,
t
.
Size
(),
t
)
return
c
,
d
}
// Return the two parts of the larger variable.
return
ssa
.
LocalSlot
{
N
:
n
,
Type
:
t
,
Off
:
name
.
Off
},
ssa
.
LocalSlot
{
N
:
n
,
Type
:
t
,
Off
:
name
.
Off
+
int64
(
Widthptr
)}
...
...
@@ -4911,10 +4937,10 @@ func (e *ssafn) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ss
lenType
:=
types
.
Types
[
TINT
]
if
n
.
Class
()
==
PAUTO
&&
!
n
.
Addrtaken
()
{
// Split this slice up into three separate variables.
p
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".ptr"
,
ptrType
,
n
.
Pos
)
l
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".len"
,
lenType
,
n
.
Pos
)
c
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".cap"
,
lenType
,
n
.
Pos
)
return
ssa
.
LocalSlot
{
N
:
p
,
Type
:
ptrType
,
Off
:
0
},
ssa
.
LocalSlot
{
N
:
l
,
Type
:
lenType
,
Off
:
0
},
ssa
.
LocalSlot
{
N
:
c
,
Type
:
lenType
,
Off
:
0
}
p
:=
e
.
splitSlot
(
&
name
,
".ptr"
,
0
,
ptrType
)
l
:=
e
.
splitSlot
(
&
name
,
".len"
,
ptrType
.
Size
(),
lenType
)
c
:=
e
.
splitSlot
(
&
name
,
".cap"
,
ptrType
.
Size
()
+
lenType
.
Size
(),
lenType
)
return
p
,
l
,
c
}
// Return the three parts of the larger variable.
return
ssa
.
LocalSlot
{
N
:
n
,
Type
:
ptrType
,
Off
:
name
.
Off
},
...
...
@@ -4933,9 +4959,9 @@ func (e *ssafn) SplitComplex(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot)
}
if
n
.
Class
()
==
PAUTO
&&
!
n
.
Addrtaken
()
{
// Split this complex up into two separate variables.
c
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".real"
,
t
,
n
.
Pos
)
d
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".imag"
,
t
,
n
.
Pos
)
return
ssa
.
LocalSlot
{
N
:
c
,
Type
:
t
,
Off
:
0
},
ssa
.
LocalSlot
{
N
:
d
,
Type
:
t
,
Off
:
0
}
r
:=
e
.
splitSlot
(
&
name
,
".real"
,
0
,
t
)
i
:=
e
.
splitSlot
(
&
name
,
".imag"
,
t
.
Size
(),
t
)
return
r
,
i
}
// Return the two parts of the larger variable.
return
ssa
.
LocalSlot
{
N
:
n
,
Type
:
t
,
Off
:
name
.
Off
},
ssa
.
LocalSlot
{
N
:
n
,
Type
:
t
,
Off
:
name
.
Off
+
s
}
...
...
@@ -4951,9 +4977,10 @@ func (e *ssafn) SplitInt64(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot) {
}
if
n
.
Class
()
==
PAUTO
&&
!
n
.
Addrtaken
()
{
// Split this int64 up into two separate variables.
h
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".hi"
,
t
,
n
.
Pos
)
l
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
".lo"
,
types
.
Types
[
TUINT32
],
n
.
Pos
)
return
ssa
.
LocalSlot
{
N
:
h
,
Type
:
t
,
Off
:
0
},
ssa
.
LocalSlot
{
N
:
l
,
Type
:
types
.
Types
[
TUINT32
],
Off
:
0
}
if
thearch
.
LinkArch
.
ByteOrder
==
binary
.
BigEndian
{
return
e
.
splitSlot
(
&
name
,
".hi"
,
0
,
t
),
e
.
splitSlot
(
&
name
,
".lo"
,
t
.
Size
(),
types
.
Types
[
TUINT32
])
}
return
e
.
splitSlot
(
&
name
,
".hi"
,
t
.
Size
(),
t
),
e
.
splitSlot
(
&
name
,
".lo"
,
0
,
types
.
Types
[
TUINT32
])
}
// Return the two parts of the larger variable.
if
thearch
.
LinkArch
.
ByteOrder
==
binary
.
BigEndian
{
...
...
@@ -4966,12 +4993,15 @@ func (e *ssafn) SplitStruct(name ssa.LocalSlot, i int) ssa.LocalSlot {
n
:=
name
.
N
.
(
*
Node
)
st
:=
name
.
Type
ft
:=
st
.
FieldType
(
i
)
var
offset
int64
for
f
:=
0
;
f
<
i
;
f
++
{
offset
+=
st
.
FieldType
(
f
)
.
Size
()
}
if
n
.
Class
()
==
PAUTO
&&
!
n
.
Addrtaken
()
{
// Note: the _ field may appear several times. But
// have no fear, identically-named but distinct Autos are
// ok, albeit maybe confusing for a debugger.
x
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
"."
+
st
.
FieldName
(
i
),
ft
,
n
.
Pos
)
return
ssa
.
LocalSlot
{
N
:
x
,
Type
:
ft
,
Off
:
0
}
return
e
.
splitSlot
(
&
name
,
"."
+
st
.
FieldName
(
i
),
offset
,
ft
)
}
return
ssa
.
LocalSlot
{
N
:
n
,
Type
:
ft
,
Off
:
name
.
Off
+
st
.
FieldOff
(
i
)}
}
...
...
@@ -4984,8 +5014,7 @@ func (e *ssafn) SplitArray(name ssa.LocalSlot) ssa.LocalSlot {
}
et
:=
at
.
ElemType
()
if
n
.
Class
()
==
PAUTO
&&
!
n
.
Addrtaken
()
{
x
:=
e
.
namedAuto
(
n
.
Sym
.
Name
+
"[0]"
,
et
,
n
.
Pos
)
return
ssa
.
LocalSlot
{
N
:
x
,
Type
:
et
,
Off
:
0
}
return
e
.
splitSlot
(
&
name
,
"[0]"
,
0
,
et
)
}
return
ssa
.
LocalSlot
{
N
:
n
,
Type
:
et
,
Off
:
name
.
Off
}
}
...
...
@@ -4994,16 +5023,14 @@ func (e *ssafn) DerefItab(it *obj.LSym, offset int64) *obj.LSym {
return
itabsym
(
it
,
offset
)
}
// namedAuto returns a new AUTO variable with the given name and type.
// These are exposed to the debugger.
func
(
e
*
ssafn
)
namedAuto
(
name
string
,
typ
*
types
.
Type
,
pos
src
.
XPos
)
ssa
.
GCNode
{
t
:=
typ
s
:=
&
types
.
Sym
{
Name
:
name
,
Pkg
:
localpkg
}
// splitSlot returns a slot representing the data of parent starting at offset.
func
(
e
*
ssafn
)
splitSlot
(
parent
*
ssa
.
LocalSlot
,
suffix
string
,
offset
int64
,
t
*
types
.
Type
)
ssa
.
LocalSlot
{
s
:=
&
types
.
Sym
{
Name
:
parent
.
N
.
(
*
Node
)
.
Sym
.
Name
+
suffix
,
Pkg
:
localpkg
}
n
:=
new
(
Node
)
n
.
Name
=
new
(
Name
)
n
.
Op
=
ONAME
n
.
Pos
=
pos
n
.
Pos
=
p
arent
.
N
.
(
*
Node
)
.
P
os
n
.
Orig
=
n
s
.
Def
=
asTypesNode
(
n
)
...
...
@@ -5016,7 +5043,7 @@ func (e *ssafn) namedAuto(name string, typ *types.Type, pos src.XPos) ssa.GCNode
n
.
Name
.
Curfn
=
e
.
curfn
e
.
curfn
.
Func
.
Dcl
=
append
(
e
.
curfn
.
Func
.
Dcl
,
n
)
dowidth
(
t
)
return
n
return
ssa
.
LocalSlot
{
N
:
n
,
Type
:
t
,
Off
:
0
,
SplitOf
:
parent
,
SplitOffset
:
offset
}
}
func
(
e
*
ssafn
)
CanSSA
(
t
*
types
.
Type
)
bool
{
...
...
src/cmd/compile/internal/gc/syntax.go
View file @
6f6a9398
...
...
@@ -7,6 +7,7 @@
package
gc
import
(
"cmd/compile/internal/ssa"
"cmd/compile/internal/syntax"
"cmd/compile/internal/types"
"cmd/internal/obj"
...
...
@@ -369,6 +370,7 @@ type Func struct {
Closgen
int
Outerfunc
*
Node
// outer function (for closure)
FieldTrack
map
[
*
types
.
Sym
]
struct
{}
DebugInfo
*
ssa
.
FuncDebug
Ntype
*
Node
// signature
Top
int
// top context (Ecall, Eproc, etc)
Closure
*
Node
// OCLOSURE <-> ODCLFUNC
...
...
src/cmd/compile/internal/ssa/cache.go
View file @
6f6a9398
...
...
@@ -14,6 +14,11 @@ type Cache struct {
blocks
[
200
]
Block
locs
[
2000
]
Location
// Storage for DWARF variable locations. Lazily allocated
// since location lists are off by default.
varLocs
[]
VarLoc
curVarLoc
int
// Reusable stackAllocState.
// See stackalloc.go's {new,put}StackAllocState.
stackAllocState
*
stackAllocState
...
...
@@ -38,4 +43,21 @@ func (c *Cache) Reset() {
for
i
:=
range
xl
{
xl
[
i
]
=
nil
}
xvl
:=
c
.
varLocs
[
:
c
.
curVarLoc
]
for
i
:=
range
xvl
{
xvl
[
i
]
=
VarLoc
{}
}
c
.
curVarLoc
=
0
}
func
(
c
*
Cache
)
NewVarLoc
()
*
VarLoc
{
if
c
.
varLocs
==
nil
{
c
.
varLocs
=
make
([]
VarLoc
,
4000
)
}
if
c
.
curVarLoc
==
len
(
c
.
varLocs
)
{
return
&
VarLoc
{}
}
vl
:=
&
c
.
varLocs
[
c
.
curVarLoc
]
c
.
curVarLoc
++
return
vl
}
src/cmd/compile/internal/ssa/debug.go
0 → 100644
View file @
6f6a9398
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
ssa
import
(
"cmd/internal/obj"
"fmt"
"strings"
)
type
SlotID
int32
// A FuncDebug contains all the debug information for the variables in a
// function. Variables are identified by their LocalSlot, which may be the
// result of decomposing a larger variable.
type
FuncDebug
struct
{
Slots
[]
*
LocalSlot
Variables
[]
VarLocList
Registers
[]
Register
}
// append adds a location to the location list for slot.
func
(
f
*
FuncDebug
)
append
(
slot
SlotID
,
loc
*
VarLoc
)
{
f
.
Variables
[
slot
]
.
append
(
loc
)
}
// lastLoc returns the last VarLoc for slot, or nil if it has none.
func
(
f
*
FuncDebug
)
lastLoc
(
slot
SlotID
)
*
VarLoc
{
return
f
.
Variables
[
slot
]
.
last
()
}
func
(
f
*
FuncDebug
)
String
()
string
{
var
vars
[]
string
for
slot
,
list
:=
range
f
.
Variables
{
if
len
(
list
.
Locations
)
==
0
{
continue
}
vars
=
append
(
vars
,
fmt
.
Sprintf
(
"%v = %v"
,
f
.
Slots
[
slot
],
list
))
}
return
fmt
.
Sprintf
(
"{%v}"
,
strings
.
Join
(
vars
,
", "
))
}
// A VarLocList contains the locations for a variable, in program text order.
// It will often have gaps.
type
VarLocList
struct
{
Locations
[]
*
VarLoc
}
func
(
l
*
VarLocList
)
append
(
loc
*
VarLoc
)
{
l
.
Locations
=
append
(
l
.
Locations
,
loc
)
}
// last returns the last location in the list.
func
(
l
*
VarLocList
)
last
()
*
VarLoc
{
if
l
==
nil
||
len
(
l
.
Locations
)
==
0
{
return
nil
}
return
l
.
Locations
[
len
(
l
.
Locations
)
-
1
]
}
// A VarLoc describes a variable's location in a single contiguous range
// of program text. It is generated from the SSA representation, but it
// refers to the generated machine code, so the Values referenced are better
// understood as PCs than actual Values, and the ranges can cross blocks.
// The range is defined first by Values, which are then mapped to Progs
// during genssa and finally to function PCs after assembly.
// A variable can be on the stack and in any number of registers.
type
VarLoc
struct
{
// Inclusive -- the first SSA value that the range covers. The value
// doesn't necessarily have anything to do with the variable; it just
// identifies a point in the program text.
Start
*
Value
// Exclusive -- the first SSA value after start that the range doesn't
// cover. A location with start == end is empty.
End
*
Value
// The prog/PCs corresponding to Start and End above. These are for the
// convenience of later passes, since code generation isn't done when
// BuildFuncDebug runs.
StartProg
,
EndProg
*
obj
.
Prog
StartPC
,
EndPC
int64
// The registers this variable is available in. There can be more than
// one in various situations, e.g. it's being moved between registers.
Registers
RegisterSet
// Indicates whether the variable is on the stack. The stack position is
// stored in the associated gc.Node.
OnStack
bool
// Used only during generation. Indicates whether this location lasts
// past the block's end. Without this, there would be no way to distinguish
// between a range that ended on the last Value of a block and one that
// didn't end at all.
survivedBlock
bool
}
// RegisterSet is a bitmap of registers, indexed by Register.num.
type
RegisterSet
uint64
func
(
v
*
VarLoc
)
String
()
string
{
var
registers
[]
Register
if
v
.
Start
!=
nil
{
registers
=
v
.
Start
.
Block
.
Func
.
Config
.
registers
}
loc
:=
""
if
!
v
.
OnStack
&&
v
.
Registers
==
0
{
loc
=
"!!!no location!!!"
}
if
v
.
OnStack
{
loc
+=
"stack,"
}
var
regnames
[]
string
for
reg
:=
0
;
reg
<
64
;
reg
++
{
if
v
.
Registers
&
(
1
<<
uint8
(
reg
))
==
0
{
continue
}
if
registers
!=
nil
{
regnames
=
append
(
regnames
,
registers
[
reg
]
.
Name
())
}
else
{
regnames
=
append
(
regnames
,
fmt
.
Sprintf
(
"reg%v"
,
reg
))
}
}
loc
+=
strings
.
Join
(
regnames
,
","
)
pos
:=
func
(
v
*
Value
,
p
*
obj
.
Prog
,
pc
int64
)
string
{
if
v
==
nil
{
return
"?"
}
if
p
==
nil
{
return
fmt
.
Sprintf
(
"v%v"
,
v
.
ID
)
}
return
fmt
.
Sprintf
(
"v%v/%x"
,
v
.
ID
,
pc
)
}
surv
:=
""
if
v
.
survivedBlock
{
surv
=
"+"
}
return
fmt
.
Sprintf
(
"%v-%v%s@%s"
,
pos
(
v
.
Start
,
v
.
StartProg
,
v
.
StartPC
),
pos
(
v
.
End
,
v
.
EndProg
,
v
.
EndPC
),
surv
,
loc
)
}
// unexpected is used to indicate an inconsistency or bug in the debug info
// generation process. These are not fixable by users. At time of writing,
// changing this to a Fprintf(os.Stderr) and running make.bash generates
// thousands of warnings.
func
(
s
*
debugState
)
unexpected
(
v
*
Value
,
msg
string
,
args
...
interface
{})
{
s
.
f
.
Logf
(
"unexpected at "
+
fmt
.
Sprint
(
v
.
ID
)
+
":"
+
msg
,
args
...
)
}
func
(
s
*
debugState
)
logf
(
msg
string
,
args
...
interface
{})
{
s
.
f
.
Logf
(
msg
,
args
...
)
}
type
debugState
struct
{
loggingEnabled
bool
slots
[]
*
LocalSlot
f
*
Func
cache
*
Cache
numRegisters
int
// working storage for BuildFuncDebug, reused between blocks.
registerContents
[][]
SlotID
}
// BuildFuncDebug returns debug information for f.
// f must be fully processed, so that each Value is where it will be when
// machine code is emitted.
func
BuildFuncDebug
(
f
*
Func
,
loggingEnabled
bool
)
*
FuncDebug
{
if
f
.
RegAlloc
==
nil
{
f
.
Fatalf
(
"BuildFuncDebug on func %v that has not been fully processed"
,
f
)
}
state
:=
&
debugState
{
loggingEnabled
:
loggingEnabled
,
slots
:
make
([]
*
LocalSlot
,
len
(
f
.
Names
)),
cache
:
f
.
Cache
,
f
:
f
,
numRegisters
:
len
(
f
.
Config
.
registers
),
registerContents
:
make
([][]
SlotID
,
len
(
f
.
Config
.
registers
)),
}
// TODO: consider storing this in Cache and reusing across functions.
valueNames
:=
make
([][]
SlotID
,
f
.
NumValues
())
for
i
,
slot
:=
range
f
.
Names
{
slot
:=
slot
state
.
slots
[
i
]
=
&
slot
if
isSynthetic
(
&
slot
)
{
continue
}
for
_
,
value
:=
range
f
.
NamedValues
[
slot
]
{
valueNames
[
value
.
ID
]
=
append
(
valueNames
[
value
.
ID
],
SlotID
(
i
))
}
}
if
state
.
loggingEnabled
{
var
names
[]
string
for
i
,
name
:=
range
f
.
Names
{
names
=
append
(
names
,
fmt
.
Sprintf
(
"%v = %v"
,
i
,
name
))
}
state
.
logf
(
"Name table: %v
\n
"
,
strings
.
Join
(
names
,
", "
))
}
// Build up block states, starting with the first block, then
// processing blocks once their predecessors have been processed.
// TODO: use a reverse post-order traversal instead of the work queue.
// Location list entries for each block.
blockLocs
:=
make
([]
*
FuncDebug
,
f
.
NumBlocks
())
// Work queue of blocks to visit. Some of them may already be processed.
work
:=
[]
*
Block
{
f
.
Entry
}
for
len
(
work
)
>
0
{
b
:=
work
[
0
]
work
=
work
[
1
:
]
if
blockLocs
[
b
.
ID
]
!=
nil
{
continue
// already processed
}
if
!
state
.
predecessorsDone
(
b
,
blockLocs
)
{
continue
// not ready yet
}
for
_
,
edge
:=
range
b
.
Succs
{
if
blockLocs
[
edge
.
Block
()
.
ID
]
!=
nil
{
continue
}
work
=
append
(
work
,
edge
.
Block
())
}
// Build the starting state for the block from the final
// state of its predecessors.
locs
:=
state
.
mergePredecessors
(
b
,
blockLocs
)
if
state
.
loggingEnabled
{
state
.
logf
(
"Processing %v, initial locs %v, regs %v
\n
"
,
b
,
locs
,
state
.
registerContents
)
}
// Update locs/registers with the effects of each Value.
for
_
,
v
:=
range
b
.
Values
{
slots
:=
valueNames
[
v
.
ID
]
// Loads and stores inherit the names of their sources.
var
source
*
Value
switch
v
.
Op
{
case
OpStoreReg
:
source
=
v
.
Args
[
0
]
case
OpLoadReg
:
switch
a
:=
v
.
Args
[
0
];
a
.
Op
{
case
OpArg
:
source
=
a
case
OpStoreReg
:
source
=
a
.
Args
[
0
]
default
:
state
.
unexpected
(
v
,
"load with unexpected source op %v"
,
a
)
}
}
if
source
!=
nil
{
slots
=
append
(
slots
,
valueNames
[
source
.
ID
]
...
)
// As of writing, the compiler never uses a load/store as a
// source of another load/store, so there's no reason this should
// ever be consulted. Update just in case, and so that when
// valueNames is cached, we can reuse the memory.
valueNames
[
v
.
ID
]
=
slots
}
if
len
(
slots
)
==
0
{
continue
}
reg
,
_
:=
f
.
getHome
(
v
.
ID
)
.
(
*
Register
)
state
.
processValue
(
locs
,
v
,
slots
,
reg
)
}
// The block is done; end the locations for all its slots.
for
_
,
locList
:=
range
locs
.
Variables
{
last
:=
locList
.
last
()
if
last
==
nil
||
last
.
End
!=
nil
{
continue
}
if
len
(
b
.
Values
)
!=
0
{
last
.
End
=
b
.
Values
[
len
(
b
.
Values
)
-
1
]
}
else
{
// This happens when a value survives into an empty block from its predecessor.
// Just carry it forward for liveness's sake.
last
.
End
=
last
.
Start
}
last
.
survivedBlock
=
true
}
if
state
.
loggingEnabled
{
f
.
Logf
(
"Block done: locs %v, regs %v. work = %+v
\n
"
,
locs
,
state
.
registerContents
,
work
)
}
blockLocs
[
b
.
ID
]
=
locs
}
// Build the complete debug info by concatenating each of the blocks'
// locations together.
info
:=
&
FuncDebug
{
Variables
:
make
([]
VarLocList
,
len
(
state
.
slots
)),
Slots
:
state
.
slots
,
Registers
:
f
.
Config
.
registers
,
}
for
_
,
b
:=
range
f
.
Blocks
{
// Ignore empty blocks; there will be some records for liveness
// but they're all useless.
if
len
(
b
.
Values
)
==
0
{
continue
}
if
blockLocs
[
b
.
ID
]
==
nil
{
state
.
unexpected
(
b
.
Values
[
0
],
"Never processed block %v
\n
"
,
b
)
continue
}
for
slot
,
blockLocList
:=
range
blockLocs
[
b
.
ID
]
.
Variables
{
for
_
,
loc
:=
range
blockLocList
.
Locations
{
if
!
loc
.
OnStack
&&
loc
.
Registers
==
0
{
state
.
unexpected
(
loc
.
Start
,
"Location for %v with no storage: %+v
\n
"
,
state
.
slots
[
slot
],
loc
)
continue
// don't confuse downstream with our bugs
}
if
loc
.
Start
==
nil
||
loc
.
End
==
nil
{
state
.
unexpected
(
b
.
Values
[
0
],
"Location for %v missing start or end: %v
\n
"
,
state
.
slots
[
slot
],
loc
)
continue
}
info
.
append
(
SlotID
(
slot
),
loc
)
}
}
}
if
state
.
loggingEnabled
{
f
.
Logf
(
"Final result:
\n
"
)
for
slot
,
locList
:=
range
info
.
Variables
{
f
.
Logf
(
"
\t
%v => %v
\n
"
,
state
.
slots
[
slot
],
locList
)
}
}
return
info
}
// isSynthetic reports whether if slot represents a compiler-inserted variable,
// e.g. an autotmp or an anonymous return value that needed a stack slot.
func
isSynthetic
(
slot
*
LocalSlot
)
bool
{
c
:=
slot
.
Name
()[
0
]
return
c
==
'.'
||
c
==
'~'
}
// predecessorsDone reports whether block is ready to be processed.
func
(
state
*
debugState
)
predecessorsDone
(
b
*
Block
,
blockLocs
[]
*
FuncDebug
)
bool
{
f
:=
b
.
Func
for
_
,
edge
:=
range
b
.
Preds
{
// Ignore back branches, e.g. the continuation of a for loop.
// This may not work for functions with mutual gotos, which are not
// reducible, in which case debug information will be missing for any
// code after that point in the control flow.
if
f
.
sdom
()
.
isAncestorEq
(
b
,
edge
.
b
)
{
if
state
.
loggingEnabled
{
f
.
Logf
(
"ignoring back branch from %v to %v
\n
"
,
edge
.
b
,
b
)
}
continue
// back branch
}
if
blockLocs
[
edge
.
b
.
ID
]
==
nil
{
if
state
.
loggingEnabled
{
f
.
Logf
(
"%v is not ready because %v isn't done
\n
"
,
b
,
edge
.
b
)
}
return
false
}
}
return
true
}
// mergePredecessors takes the end state of each of b's predecessors and
// intersects them to form the starting state for b.
// The registers slice (the second return value) will be reused for each call to mergePredecessors.
func
(
state
*
debugState
)
mergePredecessors
(
b
*
Block
,
blockLocs
[]
*
FuncDebug
)
*
FuncDebug
{
live
:=
make
([]
VarLocList
,
len
(
state
.
slots
))
// Filter out back branches.
var
preds
[]
*
Block
for
_
,
pred
:=
range
b
.
Preds
{
if
blockLocs
[
pred
.
b
.
ID
]
!=
nil
{
preds
=
append
(
preds
,
pred
.
b
)
}
}
if
len
(
preds
)
>
0
{
p
:=
preds
[
0
]
for
slot
,
locList
:=
range
blockLocs
[
p
.
ID
]
.
Variables
{
last
:=
locList
.
last
()
if
last
==
nil
||
!
last
.
survivedBlock
{
continue
}
// If this block is empty, carry forward the end value for liveness.
// It'll be ignored later.
start
:=
last
.
End
if
len
(
b
.
Values
)
!=
0
{
start
=
b
.
Values
[
0
]
}
loc
:=
state
.
cache
.
NewVarLoc
()
loc
.
Start
=
start
loc
.
OnStack
=
last
.
OnStack
loc
.
Registers
=
last
.
Registers
live
[
slot
]
.
append
(
loc
)
}
}
if
state
.
loggingEnabled
&&
len
(
b
.
Preds
)
>
1
{
state
.
logf
(
"Starting merge with state from %v: %v
\n
"
,
b
.
Preds
[
0
]
.
b
,
blockLocs
[
b
.
Preds
[
0
]
.
b
.
ID
])
}
for
i
:=
1
;
i
<
len
(
preds
);
i
++
{
p
:=
preds
[
i
]
if
state
.
loggingEnabled
{
state
.
logf
(
"Merging in state from %v: %v &= %v
\n
"
,
p
,
live
,
blockLocs
[
p
.
ID
])
}
for
slot
,
liveVar
:=
range
live
{
liveLoc
:=
liveVar
.
last
()
if
liveLoc
==
nil
{
continue
}
predLoc
:=
blockLocs
[
p
.
ID
]
.
lastLoc
(
SlotID
(
slot
))
// Clear out slots missing/dead in p.
if
predLoc
==
nil
||
!
predLoc
.
survivedBlock
{
live
[
slot
]
.
Locations
=
nil
continue
}
// Unify storage locations.
liveLoc
.
OnStack
=
liveLoc
.
OnStack
&&
predLoc
.
OnStack
liveLoc
.
Registers
&=
predLoc
.
Registers
}
}
// Create final result.
locs
:=
&
FuncDebug
{
Variables
:
live
,
Slots
:
state
.
slots
}
for
reg
:=
range
state
.
registerContents
{
state
.
registerContents
[
reg
]
=
state
.
registerContents
[
reg
][
:
0
]
}
for
slot
,
locList
:=
range
live
{
loc
:=
locList
.
last
()
if
loc
==
nil
{
continue
}
for
reg
:=
0
;
reg
<
state
.
numRegisters
;
reg
++
{
if
loc
.
Registers
&
(
1
<<
uint8
(
reg
))
!=
0
{
state
.
registerContents
[
reg
]
=
append
(
state
.
registerContents
[
reg
],
SlotID
(
slot
))
}
}
}
return
locs
}
// processValue updates locs and state.registerContents to reflect v, a value with
// the names in vSlots and homed in vReg.
func
(
state
*
debugState
)
processValue
(
locs
*
FuncDebug
,
v
*
Value
,
vSlots
[]
SlotID
,
vReg
*
Register
)
{
switch
{
case
v
.
Op
==
OpRegKill
:
if
state
.
loggingEnabled
{
existingSlots
:=
make
([]
bool
,
len
(
state
.
slots
))
for
_
,
slot
:=
range
state
.
registerContents
[
vReg
.
num
]
{
existingSlots
[
slot
]
=
true
}
for
_
,
slot
:=
range
vSlots
{
if
existingSlots
[
slot
]
{
existingSlots
[
slot
]
=
false
}
else
{
state
.
unexpected
(
v
,
"regkill of unassociated name %v
\n
"
,
state
.
slots
[
slot
])
}
}
for
slot
,
live
:=
range
existingSlots
{
if
live
{
state
.
unexpected
(
v
,
"leftover register name: %v
\n
"
,
state
.
slots
[
slot
])
}
}
}
state
.
registerContents
[
vReg
.
num
]
=
nil
for
_
,
slot
:=
range
vSlots
{
last
:=
locs
.
lastLoc
(
slot
)
if
last
==
nil
{
state
.
unexpected
(
v
,
"regkill of already dead %v, %+v
\n
"
,
vReg
,
state
.
slots
[
slot
])
continue
}
if
state
.
loggingEnabled
{
state
.
logf
(
"at %v: %v regkilled out of %v
\n
"
,
v
.
ID
,
state
.
slots
[
slot
],
vReg
.
Name
())
}
if
last
.
End
!=
nil
{
state
.
unexpected
(
v
,
"regkill of dead slot, died at %v
\n
"
,
last
.
End
)
}
last
.
End
=
v
regs
:=
last
.
Registers
&^
(
1
<<
uint8
(
vReg
.
num
))
if
!
last
.
OnStack
&&
regs
==
0
{
continue
}
loc
:=
state
.
cache
.
NewVarLoc
()
loc
.
Start
=
v
loc
.
OnStack
=
last
.
OnStack
loc
.
Registers
=
regs
locs
.
append
(
slot
,
loc
)
}
case
v
.
Op
==
OpArg
:
for
_
,
slot
:=
range
vSlots
{
if
state
.
loggingEnabled
{
state
.
logf
(
"at %v: %v now on stack from arg
\n
"
,
v
.
ID
,
state
.
slots
[
slot
])
}
loc
:=
state
.
cache
.
NewVarLoc
()
loc
.
Start
=
v
loc
.
OnStack
=
true
locs
.
append
(
slot
,
loc
)
}
case
v
.
Op
==
OpStoreReg
:
for
_
,
slot
:=
range
vSlots
{
if
state
.
loggingEnabled
{
state
.
logf
(
"at %v: %v spilled to stack
\n
"
,
v
.
ID
,
state
.
slots
[
slot
])
}
last
:=
locs
.
lastLoc
(
slot
)
if
last
==
nil
{
state
.
unexpected
(
v
,
"spill of unnamed register %v
\n
"
,
vReg
)
break
}
last
.
End
=
v
loc
:=
state
.
cache
.
NewVarLoc
()
loc
.
Start
=
v
loc
.
OnStack
=
true
loc
.
Registers
=
last
.
Registers
locs
.
append
(
slot
,
loc
)
}
case
vReg
!=
nil
:
if
state
.
loggingEnabled
{
newSlots
:=
make
([]
bool
,
len
(
state
.
slots
))
for
_
,
slot
:=
range
vSlots
{
newSlots
[
slot
]
=
true
}
for
_
,
slot
:=
range
state
.
registerContents
[
vReg
.
num
]
{
if
!
newSlots
[
slot
]
{
state
.
unexpected
(
v
,
"%v clobbered
\n
"
,
state
.
slots
[
slot
])
}
}
}
for
_
,
slot
:=
range
vSlots
{
if
state
.
loggingEnabled
{
state
.
logf
(
"at %v: %v now in %v
\n
"
,
v
.
ID
,
state
.
slots
[
slot
],
vReg
.
Name
())
}
last
:=
locs
.
lastLoc
(
slot
)
if
last
!=
nil
&&
last
.
End
==
nil
{
last
.
End
=
v
}
state
.
registerContents
[
vReg
.
num
]
=
append
(
state
.
registerContents
[
vReg
.
num
],
slot
)
loc
:=
state
.
cache
.
NewVarLoc
()
loc
.
Start
=
v
if
last
!=
nil
{
loc
.
OnStack
=
last
.
OnStack
loc
.
Registers
=
last
.
Registers
}
loc
.
Registers
|=
1
<<
uint8
(
vReg
.
num
)
locs
.
append
(
slot
,
loc
)
}
default
:
state
.
unexpected
(
v
,
"named value with no reg
\n
"
)
}
}
src/cmd/compile/internal/ssa/decompose.go
View file @
6f6a9398
...
...
@@ -98,6 +98,7 @@ func decomposeBuiltIn(f *Func) {
delete
(
f
.
NamedValues
,
name
)
case
t
.
IsFloat
()
:
// floats are never decomposed, even ones bigger than RegSize
newNames
=
append
(
newNames
,
name
)
case
t
.
Size
()
>
f
.
Config
.
RegSize
:
f
.
Fatalf
(
"undecomposed named type %v %v"
,
name
,
t
)
default
:
...
...
src/cmd/compile/internal/ssa/export_test.go
View file @
6f6a9398
...
...
@@ -82,33 +82,33 @@ func (DummyFrontend) Auto(pos src.XPos, t *types.Type) GCNode {
return
&
DummyAuto
{
t
:
t
,
s
:
"aDummyAuto"
}
}
func
(
d
DummyFrontend
)
SplitString
(
s
LocalSlot
)
(
LocalSlot
,
LocalSlot
)
{
return
LocalSlot
{
s
.
N
,
dummyTypes
.
BytePtr
,
s
.
Off
},
LocalSlot
{
s
.
N
,
dummyTypes
.
Int
,
s
.
Off
+
8
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
BytePtr
,
Off
:
s
.
Off
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Int
,
Off
:
s
.
Off
+
8
}
}
func
(
d
DummyFrontend
)
SplitInterface
(
s
LocalSlot
)
(
LocalSlot
,
LocalSlot
)
{
return
LocalSlot
{
s
.
N
,
dummyTypes
.
BytePtr
,
s
.
Off
},
LocalSlot
{
s
.
N
,
dummyTypes
.
BytePtr
,
s
.
Off
+
8
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
BytePtr
,
Off
:
s
.
Off
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
BytePtr
,
Off
:
s
.
Off
+
8
}
}
func
(
d
DummyFrontend
)
SplitSlice
(
s
LocalSlot
)
(
LocalSlot
,
LocalSlot
,
LocalSlot
)
{
return
LocalSlot
{
s
.
N
,
s
.
Type
.
ElemType
()
.
PtrTo
(),
s
.
Off
},
LocalSlot
{
s
.
N
,
dummyTypes
.
Int
,
s
.
Off
+
8
},
LocalSlot
{
s
.
N
,
dummyTypes
.
Int
,
s
.
Off
+
16
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
s
.
Type
.
ElemType
()
.
PtrTo
(),
Off
:
s
.
Off
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Int
,
Off
:
s
.
Off
+
8
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Int
,
Off
:
s
.
Off
+
16
}
}
func
(
d
DummyFrontend
)
SplitComplex
(
s
LocalSlot
)
(
LocalSlot
,
LocalSlot
)
{
if
s
.
Type
.
Size
()
==
16
{
return
LocalSlot
{
s
.
N
,
dummyTypes
.
Float64
,
s
.
Off
},
LocalSlot
{
s
.
N
,
dummyTypes
.
Float64
,
s
.
Off
+
8
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Float64
,
Off
:
s
.
Off
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Float64
,
Off
:
s
.
Off
+
8
}
}
return
LocalSlot
{
s
.
N
,
dummyTypes
.
Float32
,
s
.
Off
},
LocalSlot
{
s
.
N
,
dummyTypes
.
Float32
,
s
.
Off
+
4
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Float32
,
Off
:
s
.
Off
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Float32
,
Off
:
s
.
Off
+
4
}
}
func
(
d
DummyFrontend
)
SplitInt64
(
s
LocalSlot
)
(
LocalSlot
,
LocalSlot
)
{
if
s
.
Type
.
IsSigned
()
{
return
LocalSlot
{
s
.
N
,
dummyTypes
.
Int32
,
s
.
Off
+
4
},
LocalSlot
{
s
.
N
,
dummyTypes
.
UInt32
,
s
.
Off
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
Int32
,
Off
:
s
.
Off
+
4
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
UInt32
,
Off
:
s
.
Off
}
}
return
LocalSlot
{
s
.
N
,
dummyTypes
.
UInt32
,
s
.
Off
+
4
},
LocalSlot
{
s
.
N
,
dummyTypes
.
UInt32
,
s
.
Off
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
UInt32
,
Off
:
s
.
Off
+
4
},
LocalSlot
{
N
:
s
.
N
,
Type
:
dummyTypes
.
UInt32
,
Off
:
s
.
Off
}
}
func
(
d
DummyFrontend
)
SplitStruct
(
s
LocalSlot
,
i
int
)
LocalSlot
{
return
LocalSlot
{
s
.
N
,
s
.
Type
.
FieldType
(
i
),
s
.
Off
+
s
.
Type
.
FieldOff
(
i
)}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
s
.
Type
.
FieldType
(
i
),
Off
:
s
.
Off
+
s
.
Type
.
FieldOff
(
i
)}
}
func
(
d
DummyFrontend
)
SplitArray
(
s
LocalSlot
)
LocalSlot
{
return
LocalSlot
{
s
.
N
,
s
.
Type
.
ElemType
(),
s
.
Off
}
return
LocalSlot
{
N
:
s
.
N
,
Type
:
s
.
Type
.
ElemType
(),
Off
:
s
.
Off
}
}
func
(
DummyFrontend
)
Line
(
_
src
.
XPos
)
string
{
return
"unknown.go:0"
...
...
src/cmd/compile/internal/ssa/gen/genericOps.go
View file @
6f6a9398
...
...
@@ -417,6 +417,7 @@ var genericOps = []opData{
{
name
:
"VarKill"
,
argLength
:
1
,
aux
:
"Sym"
,
symEffect
:
"None"
},
// aux is a *gc.Node of a variable that is known to be dead. arg0=mem, returns mem
{
name
:
"VarLive"
,
argLength
:
1
,
aux
:
"Sym"
,
symEffect
:
"None"
},
// aux is a *gc.Node of a variable that must be kept live. arg0=mem, returns mem
{
name
:
"KeepAlive"
,
argLength
:
2
,
typ
:
"Mem"
},
// arg[0] is a value that must be kept alive until this mark. arg[1]=mem, returns mem
{
name
:
"RegKill"
},
// regalloc has determined that the value in this register is dead
// Ops for breaking 64-bit operations on 32-bit architectures
{
name
:
"Int64Make"
,
argLength
:
2
,
typ
:
"UInt64"
},
// arg0=hi, arg1=lo
...
...
src/cmd/compile/internal/ssa/html.go
View file @
6f6a9398
...
...
@@ -11,6 +11,7 @@ import (
"html"
"io"
"os"
"strings"
)
type
HTMLWriter
struct
{
...
...
@@ -362,6 +363,18 @@ func (v *Value) LongHTML() string {
if
int
(
v
.
ID
)
<
len
(
r
)
&&
r
[
v
.
ID
]
!=
nil
{
s
+=
" : "
+
html
.
EscapeString
(
r
[
v
.
ID
]
.
Name
())
}
var
names
[]
string
for
name
,
values
:=
range
v
.
Block
.
Func
.
NamedValues
{
for
_
,
value
:=
range
values
{
if
value
==
v
{
names
=
append
(
names
,
name
.
Name
())
break
// drop duplicates.
}
}
}
if
len
(
names
)
!=
0
{
s
+=
" ("
+
strings
.
Join
(
names
,
", "
)
+
")"
}
s
+=
"</span>"
return
s
}
...
...
src/cmd/compile/internal/ssa/location.go
View file @
6f6a9398
...
...
@@ -26,12 +26,38 @@ func (r *Register) Name() string {
return
r
.
name
}
// A LocalSlot is a location in the stack frame.
// It is (possibly a subpiece of) a PPARAM, PPARAMOUT, or PAUTO ONAME node.
// ObjNum returns the register number from cmd/internal/obj/$ARCH that
// corresponds to this register.
func
(
r
*
Register
)
ObjNum
()
int16
{
return
r
.
objNum
}
// A LocalSlot is a location in the stack frame, which identifies and stores
// part or all of a PPARAM, PPARAMOUT, or PAUTO ONAME node.
// It can represent a whole variable, part of a larger stack slot, or part of a
// variable that has been decomposed into multiple stack slots.
// As an example, a string could have the following configurations:
//
// stack layout LocalSlots
//
// Optimizations are disabled. s is on the stack and represented in its entirety.
// [ ------- s string ---- ] { N: s, Type: string, Off: 0 }
//
// s was not decomposed, but the SSA operates on its parts individually, so
// there is a LocalSlot for each of its fields that points into the single stack slot.
// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8}
//
// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot.
// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0},
// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8}
// parent = &{N: s, Type: string}
type
LocalSlot
struct
{
N
GCNode
// an ONAME *gc.Node representing a
variable on the stack
N
GCNode
// an ONAME *gc.Node representing a
stack location.
Type
*
types
.
Type
// type of slot
Off
int64
// offset of slot in N
SplitOf
*
LocalSlot
// slot is a decomposition of SplitOf
SplitOffset
int64
// .. at this offset.
}
func
(
s
LocalSlot
)
Name
()
string
{
...
...
src/cmd/compile/internal/ssa/opGen.go
View file @
6f6a9398
...
...
@@ -1897,6 +1897,7 @@ const (
OpVarKill
OpVarLive
OpKeepAlive
OpRegKill
OpInt64Make
OpInt64Hi
OpInt64Lo
...
...
@@ -22497,6 +22498,11 @@ var opcodeTable = [...]opInfo{
argLen
:
2
,
generic
:
true
,
},
{
name
:
"RegKill"
,
argLen
:
0
,
generic
:
true
,
},
{
name
:
"Int64Make"
,
argLen
:
2
,
...
...
src/cmd/compile/internal/ssa/regalloc.go
View file @
6f6a9398
...
...
@@ -242,6 +242,9 @@ type regAllocState struct {
// current state of each (preregalloc) Value
values
[]
valState
// names associated with each Value
valueNames
[][]
LocalSlot
// ID of SP, SB values
sp
,
sb
ID
...
...
@@ -300,6 +303,13 @@ type startReg struct {
// freeReg frees up register r. Any current user of r is kicked out.
func
(
s
*
regAllocState
)
freeReg
(
r
register
)
{
s
.
freeOrResetReg
(
r
,
false
)
}
// freeOrResetReg frees up register r. Any current user of r is kicked out.
// resetting indicates that the operation is only for bookkeeping,
// e.g. when clearing out state upon entry to a new block.
func
(
s
*
regAllocState
)
freeOrResetReg
(
r
register
,
resetting
bool
)
{
v
:=
s
.
regs
[
r
]
.
v
if
v
==
nil
{
s
.
f
.
Fatalf
(
"tried to free an already free register %d
\n
"
,
r
)
...
...
@@ -309,6 +319,16 @@ func (s *regAllocState) freeReg(r register) {
if
s
.
f
.
pass
.
debug
>
regDebug
{
fmt
.
Printf
(
"freeReg %s (dump %s/%s)
\n
"
,
s
.
registers
[
r
]
.
Name
(),
v
,
s
.
regs
[
r
]
.
c
)
}
if
!
resetting
&&
s
.
f
.
Config
.
ctxt
.
Flag_locationlists
&&
len
(
s
.
valueNames
[
v
.
ID
])
!=
0
{
kill
:=
s
.
curBlock
.
NewValue0
(
src
.
NoXPos
,
OpRegKill
,
types
.
TypeVoid
)
for
int
(
kill
.
ID
)
>=
len
(
s
.
orig
)
{
s
.
orig
=
append
(
s
.
orig
,
nil
)
}
for
_
,
name
:=
range
s
.
valueNames
[
v
.
ID
]
{
s
.
f
.
NamedValues
[
name
]
=
append
(
s
.
f
.
NamedValues
[
name
],
kill
)
}
s
.
f
.
setHome
(
kill
,
&
s
.
registers
[
r
])
}
s
.
regs
[
r
]
=
regState
{}
s
.
values
[
v
.
ID
]
.
regs
&^=
regMask
(
1
)
<<
r
s
.
used
&^=
regMask
(
1
)
<<
r
...
...
@@ -599,6 +619,17 @@ func (s *regAllocState) init(f *Func) {
s
.
values
=
make
([]
valState
,
f
.
NumValues
())
s
.
orig
=
make
([]
*
Value
,
f
.
NumValues
())
s
.
copies
=
make
(
map
[
*
Value
]
bool
)
if
s
.
f
.
Config
.
ctxt
.
Flag_locationlists
{
s
.
valueNames
=
make
([][]
LocalSlot
,
f
.
NumValues
())
for
slot
,
values
:=
range
f
.
NamedValues
{
if
isSynthetic
(
&
slot
)
{
continue
}
for
_
,
value
:=
range
values
{
s
.
valueNames
[
value
.
ID
]
=
append
(
s
.
valueNames
[
value
.
ID
],
slot
)
}
}
}
for
_
,
b
:=
range
f
.
Blocks
{
for
_
,
v
:=
range
b
.
Values
{
if
!
v
.
Type
.
IsMemory
()
&&
!
v
.
Type
.
IsVoid
()
&&
!
v
.
Type
.
IsFlags
()
&&
!
v
.
Type
.
IsTuple
()
{
...
...
@@ -692,7 +723,9 @@ func (s *regAllocState) liveAfterCurrentInstruction(v *Value) bool {
// Sets the state of the registers to that encoded in regs.
func
(
s
*
regAllocState
)
setState
(
regs
[]
endReg
)
{
s
.
freeRegs
(
s
.
used
)
for
s
.
used
!=
0
{
s
.
freeOrResetReg
(
pickReg
(
s
.
used
),
true
)
}
for
_
,
x
:=
range
regs
{
s
.
assignReg
(
x
.
r
,
x
.
v
,
x
.
c
)
}
...
...
@@ -735,6 +768,9 @@ func (s *regAllocState) regalloc(f *Func) {
}
for
_
,
b
:=
range
f
.
Blocks
{
if
s
.
f
.
pass
.
debug
>
regDebug
{
fmt
.
Printf
(
"Begin processing block %v
\n
"
,
b
)
}
s
.
curBlock
=
b
// Initialize regValLiveSet and uses fields for this block.
...
...
@@ -830,9 +866,6 @@ func (s *regAllocState) regalloc(f *Func) {
// This is the complicated case. We have more than one predecessor,
// which means we may have Phi ops.
// Copy phi ops into new schedule.
b
.
Values
=
append
(
b
.
Values
,
phis
...
)
// Start with the final register state of the primary predecessor
idx
:=
s
.
primary
[
b
.
ID
]
if
idx
<
0
{
...
...
@@ -910,6 +943,9 @@ func (s *regAllocState) regalloc(f *Func) {
}
}
// Copy phi ops into new schedule.
b
.
Values
=
append
(
b
.
Values
,
phis
...
)
// Third pass - pick registers for phis whose inputs
// were not in a register.
for
i
,
v
:=
range
phis
{
...
...
@@ -1005,7 +1041,7 @@ func (s *regAllocState) regalloc(f *Func) {
pidx
:=
e
.
i
for
_
,
v
:=
range
succ
.
Values
{
if
v
.
Op
!=
OpPhi
{
break
continue
}
if
!
s
.
values
[
v
.
ID
]
.
needReg
{
continue
...
...
@@ -1565,6 +1601,9 @@ func (s *regAllocState) placeSpills() {
for
_
,
b
:=
range
f
.
Blocks
{
var
m
regMask
for
_
,
v
:=
range
b
.
Values
{
if
v
.
Op
==
OpRegKill
{
continue
}
if
v
.
Op
!=
OpPhi
{
break
}
...
...
@@ -1675,7 +1714,7 @@ func (s *regAllocState) placeSpills() {
for
_
,
b
:=
range
f
.
Blocks
{
nphi
:=
0
for
_
,
v
:=
range
b
.
Values
{
if
v
.
Op
!=
OpPhi
{
if
v
.
Op
!=
Op
RegKill
&&
v
.
Op
!=
Op
Phi
{
break
}
nphi
++
...
...
@@ -1800,6 +1839,9 @@ func (e *edgeState) setup(idx int, srcReg []endReg, dstReg []startReg, stacklive
}
// Phis need their args to end up in a specific location.
for
_
,
v
:=
range
e
.
b
.
Values
{
if
v
.
Op
==
OpRegKill
{
continue
}
if
v
.
Op
!=
OpPhi
{
break
}
...
...
@@ -1878,6 +1920,7 @@ func (e *edgeState) process() {
if
e
.
s
.
f
.
pass
.
debug
>
regDebug
{
fmt
.
Printf
(
"breaking cycle with v%d in %s:%s
\n
"
,
vid
,
loc
.
Name
(),
c
)
}
e
.
erase
(
r
)
if
_
,
isReg
:=
loc
.
(
*
Register
);
isReg
{
c
=
e
.
p
.
NewValue1
(
d
.
pos
,
OpCopy
,
c
.
Type
,
c
)
}
else
{
...
...
@@ -1943,6 +1986,18 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
}
}
_
,
dstReg
:=
loc
.
(
*
Register
)
// Pre-clobber destination. This avoids the
// following situation:
// - v is currently held in R0 and stacktmp0.
// - We want to copy stacktmp1 to stacktmp0.
// - We choose R0 as the temporary register.
// During the copy, both R0 and stacktmp0 are
// clobbered, losing both copies of v. Oops!
// Erasing the destination early means R0 will not
// be chosen as the temp register, as it will then
// be the last copy of v.
e
.
erase
(
loc
)
var
x
*
Value
if
c
==
nil
{
if
!
e
.
s
.
values
[
vid
]
.
rematerializeable
{
...
...
@@ -1953,8 +2008,8 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
}
else
{
// Rematerialize into stack slot. Need a free
// register to accomplish this.
e
.
erase
(
loc
)
// see pre-clobber comment below
r
:=
e
.
findRegFor
(
v
.
Type
)
e
.
erase
(
r
)
x
=
v
.
copyIntoNoXPos
(
e
.
p
)
e
.
set
(
r
,
vid
,
x
,
false
,
pos
)
// Make sure we spill with the size of the slot, not the
...
...
@@ -1976,20 +2031,8 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
x
=
e
.
p
.
NewValue1
(
pos
,
OpLoadReg
,
c
.
Type
,
c
)
}
else
{
// mem->mem. Use temp register.
// Pre-clobber destination. This avoids the
// following situation:
// - v is currently held in R0 and stacktmp0.
// - We want to copy stacktmp1 to stacktmp0.
// - We choose R0 as the temporary register.
// During the copy, both R0 and stacktmp0 are
// clobbered, losing both copies of v. Oops!
// Erasing the destination early means R0 will not
// be chosen as the temp register, as it will then
// be the last copy of v.
e
.
erase
(
loc
)
r
:=
e
.
findRegFor
(
c
.
Type
)
e
.
erase
(
r
)
t
:=
e
.
p
.
NewValue1
(
pos
,
OpLoadReg
,
c
.
Type
,
c
)
e
.
set
(
r
,
vid
,
t
,
false
,
pos
)
x
=
e
.
p
.
NewValue1
(
pos
,
OpStoreReg
,
loc
.
(
LocalSlot
)
.
Type
,
t
)
...
...
@@ -2008,7 +2051,6 @@ func (e *edgeState) processDest(loc Location, vid ID, splice **Value, pos src.XP
// set changes the contents of location loc to hold the given value and its cached representative.
func
(
e
*
edgeState
)
set
(
loc
Location
,
vid
ID
,
c
*
Value
,
final
bool
,
pos
src
.
XPos
)
{
e
.
s
.
f
.
setHome
(
c
,
loc
)
e
.
erase
(
loc
)
e
.
contents
[
loc
]
=
contentRecord
{
vid
,
c
,
final
,
pos
}
a
:=
e
.
cache
[
vid
]
if
len
(
a
)
==
0
{
...
...
@@ -2059,6 +2101,16 @@ func (e *edgeState) erase(loc Location) {
fmt
.
Printf
(
"v%d no longer available in %s:%s
\n
"
,
vid
,
loc
.
Name
(),
c
)
}
a
[
i
],
a
=
a
[
len
(
a
)
-
1
],
a
[
:
len
(
a
)
-
1
]
if
e
.
s
.
f
.
Config
.
ctxt
.
Flag_locationlists
{
if
_
,
isReg
:=
loc
.
(
*
Register
);
isReg
&&
int
(
c
.
ID
)
<
len
(
e
.
s
.
valueNames
)
&&
len
(
e
.
s
.
valueNames
[
c
.
ID
])
!=
0
{
kill
:=
e
.
p
.
NewValue0
(
src
.
NoXPos
,
OpRegKill
,
types
.
TypeVoid
)
e
.
s
.
f
.
setHome
(
kill
,
loc
)
for
_
,
name
:=
range
e
.
s
.
valueNames
[
c
.
ID
]
{
e
.
s
.
f
.
NamedValues
[
name
]
=
append
(
e
.
s
.
f
.
NamedValues
[
name
],
kill
)
}
}
}
break
}
}
...
...
@@ -2118,8 +2170,8 @@ func (e *edgeState) findRegFor(typ *types.Type) Location {
// Allocate a temp location to spill a register to.
// The type of the slot is immaterial - it will not be live across
// any safepoint. Just use a type big enough to hold any register.
t
:=
LocalSlot
{
e
.
s
.
f
.
fe
.
Auto
(
c
.
Pos
,
types
.
Int64
),
types
.
Int64
,
0
}
// TODO: reuse these slots.
t
:=
LocalSlot
{
N
:
e
.
s
.
f
.
fe
.
Auto
(
c
.
Pos
,
types
.
Int64
),
Type
:
types
.
Int64
}
// TODO: reuse these slots.
They'll need to be erased first.
e
.
set
(
t
,
vid
,
x
,
false
,
c
.
Pos
)
if
e
.
s
.
f
.
pass
.
debug
>
regDebug
{
fmt
.
Printf
(
" SPILL %s->%s %s
\n
"
,
r
.
Name
(),
t
.
Name
(),
x
.
LongString
())
...
...
src/cmd/compile/internal/ssa/sizeof_test.go
View file @
6f6a9398
...
...
@@ -24,6 +24,7 @@ func TestSizeof(t *testing.T) {
}{
{
Value
{},
68
,
112
},
{
Block
{},
152
,
288
},
{
LocalSlot
{},
32
,
48
},
{
valState
{},
28
,
40
},
}
...
...
src/cmd/compile/internal/ssa/stackalloc.go
View file @
6f6a9398
...
...
@@ -151,7 +151,7 @@ func (s *stackAllocState) stackalloc() {
if
v
.
Op
!=
OpArg
{
continue
}
loc
:=
LocalSlot
{
v
.
Aux
.
(
GCNode
),
v
.
Type
,
v
.
AuxInt
}
loc
:=
LocalSlot
{
N
:
v
.
Aux
.
(
GCNode
),
Type
:
v
.
Type
,
Off
:
v
.
AuxInt
}
if
f
.
pass
.
debug
>
stackDebug
{
fmt
.
Printf
(
"stackalloc %s to %s
\n
"
,
v
,
loc
.
Name
())
}
...
...
src/cmd/compile/internal/ssa/value.go
View file @
6f6a9398
...
...
@@ -10,6 +10,7 @@ import (
"cmd/internal/src"
"fmt"
"math"
"strings"
)
// A Value represents a value in the SSA representation of the program.
...
...
@@ -98,7 +99,7 @@ func (v *Value) AuxValAndOff() ValAndOff {
return
ValAndOff
(
v
.
AuxInt
)
}
// long form print. v# = opcode <type> [aux] args [: reg]
// long form print. v# = opcode <type> [aux] args [: reg]
(names)
func
(
v
*
Value
)
LongString
()
string
{
s
:=
fmt
.
Sprintf
(
"v%d = %s"
,
v
.
ID
,
v
.
Op
)
s
+=
" <"
+
v
.
Type
.
String
()
+
">"
...
...
@@ -110,6 +111,18 @@ func (v *Value) LongString() string {
if
int
(
v
.
ID
)
<
len
(
r
)
&&
r
[
v
.
ID
]
!=
nil
{
s
+=
" : "
+
r
[
v
.
ID
]
.
Name
()
}
var
names
[]
string
for
name
,
values
:=
range
v
.
Block
.
Func
.
NamedValues
{
for
_
,
value
:=
range
values
{
if
value
==
v
{
names
=
append
(
names
,
name
.
Name
())
break
// drop duplicates.
}
}
}
if
len
(
names
)
!=
0
{
s
+=
" ("
+
strings
.
Join
(
names
,
", "
)
+
")"
}
return
s
}
...
...
src/cmd/internal/dwarf/dwarf.go
View file @
6f6a9398
...
...
@@ -15,6 +15,9 @@ import (
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
const
InfoPrefix
=
"go.info."
// RangePrefix is the prefix for all the symbols containing DWARF location lists.
const
LocPrefix
=
"go.loc."
// RangePrefix is the prefix for all the symbols containing DWARF range lists.
const
RangePrefix
=
"go.range."
...
...
@@ -23,13 +26,31 @@ type Sym interface {
Len
()
int64
}
// A Location represents a variable's location at a particular PC range.
// It becomes a location list entry in the DWARF.
type
Location
struct
{
StartPC
,
EndPC
int64
Pieces
[]
Piece
}
// A Piece represents the location of a particular part of a variable.
// It becomes part of a location list entry (a DW_OP_piece) in the DWARF.
type
Piece
struct
{
Length
int64
StackOffset
int32
RegNum
int16
Missing
bool
OnStack
bool
// if true, RegNum is unset.
}
// A Var represents a local variable or a function parameter.
type
Var
struct
{
Name
string
Abbrev
int
// Either DW_ABRV_AUTO or DW_ABRV_PARAM
Offset
int32
Scope
int32
Type
Sym
Name
string
Abbrev
int
// Either DW_ABRV_AUTO or DW_ABRV_PARAM
StackOffset
int32
LocationList
[]
Location
Scope
int32
Type
Sym
}
// A Scope represents a lexical scope. All variables declared within a
...
...
@@ -205,7 +226,7 @@ const (
)
// Index into the abbrevs table below.
// Keep in sync with ispubname() and ispubtype()
below
.
// Keep in sync with ispubname() and ispubtype()
in ld/dwarf.go
.
// ispubtype considers >= NULLTYPE public
const
(
DW_ABRV_NULL
=
iota
...
...
@@ -213,7 +234,9 @@ const (
DW_ABRV_FUNCTION
DW_ABRV_VARIABLE
DW_ABRV_AUTO
DW_ABRV_AUTO_LOCLIST
DW_ABRV_PARAM
DW_ABRV_PARAM_LOCLIST
DW_ABRV_LEXICAL_BLOCK_RANGES
DW_ABRV_LEXICAL_BLOCK_SIMPLE
DW_ABRV_STRUCTFIELD
...
...
@@ -297,6 +320,17 @@ var abbrevs = [DW_NABRV]dwAbbrev{
},
},
/* AUTO_LOCLIST */
{
DW_TAG_variable
,
DW_CHILDREN_no
,
[]
dwAttrForm
{
{
DW_AT_name
,
DW_FORM_string
},
{
DW_AT_location
,
DW_FORM_sec_offset
},
{
DW_AT_type
,
DW_FORM_ref_addr
},
},
},
/* PARAM */
{
DW_TAG_formal_parameter
,
...
...
@@ -307,6 +341,18 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{
DW_AT_type
,
DW_FORM_ref_addr
},
},
},
/* PARAM_LOCLIST */
{
DW_TAG_formal_parameter
,
DW_CHILDREN_no
,
[]
dwAttrForm
{
{
DW_AT_name
,
DW_FORM_string
},
{
DW_AT_location
,
DW_FORM_sec_offset
},
{
DW_AT_type
,
DW_FORM_ref_addr
},
},
},
/* LEXICAL_BLOCK_RANGES */
{
DW_TAG_lexical_block
,
...
...
@@ -684,31 +730,30 @@ func HasChildren(die *DWDie) bool {
// PutFunc writes a DIE for a function to s.
// It also writes child DIEs for each variable in vars.
func
PutFunc
(
ctxt
Context
,
s
,
ranges
Sym
,
name
string
,
external
bool
,
startPC
Sym
,
size
int64
,
scopes
[]
Scope
)
error
{
Uleb128put
(
ctxt
,
s
,
DW_ABRV_FUNCTION
)
putattr
(
ctxt
,
s
,
DW_ABRV_FUNCTION
,
DW_FORM_string
,
DW_CLS_STRING
,
int64
(
len
(
name
)),
name
)
putattr
(
ctxt
,
s
,
DW_ABRV_FUNCTION
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
0
,
startPC
)
putattr
(
ctxt
,
s
,
DW_ABRV_FUNCTION
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
size
,
startPC
)
putattr
(
ctxt
,
s
,
DW_ABRV_FUNCTION
,
DW_FORM_block1
,
DW_CLS_BLOCK
,
1
,
[]
byte
{
DW_OP_call_frame_cfa
})
func
PutFunc
(
ctxt
Context
,
info
,
loc
,
ranges
Sym
,
name
string
,
external
bool
,
startPC
Sym
,
size
int64
,
scopes
[]
Scope
)
error
{
Uleb128put
(
ctxt
,
info
,
DW_ABRV_FUNCTION
)
putattr
(
ctxt
,
info
,
DW_ABRV_FUNCTION
,
DW_FORM_string
,
DW_CLS_STRING
,
int64
(
len
(
name
)),
name
)
putattr
(
ctxt
,
info
,
DW_ABRV_FUNCTION
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
0
,
startPC
)
putattr
(
ctxt
,
info
,
DW_ABRV_FUNCTION
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
size
,
startPC
)
putattr
(
ctxt
,
info
,
DW_ABRV_FUNCTION
,
DW_FORM_block1
,
DW_CLS_BLOCK
,
1
,
[]
byte
{
DW_OP_call_frame_cfa
})
var
ev
int64
if
external
{
ev
=
1
}
putattr
(
ctxt
,
s
,
DW_ABRV_FUNCTION
,
DW_FORM_flag
,
DW_CLS_FLAG
,
ev
,
0
)
putattr
(
ctxt
,
info
,
DW_ABRV_FUNCTION
,
DW_FORM_flag
,
DW_CLS_FLAG
,
ev
,
0
)
if
len
(
scopes
)
>
0
{
var
encbuf
[
20
]
byte
if
putscope
(
ctxt
,
s
,
ranges
,
startPC
,
0
,
scopes
,
encbuf
[
:
0
])
<
int32
(
len
(
scopes
))
{
if
putscope
(
ctxt
,
info
,
loc
,
ranges
,
startPC
,
0
,
scopes
,
encbuf
[
:
0
])
<
int32
(
len
(
scopes
))
{
return
errors
.
New
(
"multiple toplevel scopes"
)
}
}
Uleb128put
(
ctxt
,
s
,
0
)
Uleb128put
(
ctxt
,
info
,
0
)
return
nil
}
func
putscope
(
ctxt
Context
,
s
,
ranges
Sym
,
startPC
Sym
,
curscope
int32
,
scopes
[]
Scope
,
encbuf
[]
byte
)
int32
{
func
putscope
(
ctxt
Context
,
info
,
loc
,
ranges
,
startPC
Sym
,
curscope
int32
,
scopes
[]
Scope
,
encbuf
[]
byte
)
int32
{
for
_
,
v
:=
range
scopes
[
curscope
]
.
Vars
{
putvar
(
ctxt
,
s
,
v
,
encbuf
)
putvar
(
ctxt
,
info
,
loc
,
v
,
startPC
,
encbuf
)
}
this
:=
curscope
curscope
++
...
...
@@ -719,12 +764,12 @@ func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes [
}
if
len
(
scope
.
Ranges
)
==
1
{
Uleb128put
(
ctxt
,
s
,
DW_ABRV_LEXICAL_BLOCK_SIMPLE
)
putattr
(
ctxt
,
s
,
DW_ABRV_LEXICAL_BLOCK_SIMPLE
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
scope
.
Ranges
[
0
]
.
Start
,
startPC
)
putattr
(
ctxt
,
s
,
DW_ABRV_LEXICAL_BLOCK_SIMPLE
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
scope
.
Ranges
[
0
]
.
End
,
startPC
)
Uleb128put
(
ctxt
,
info
,
DW_ABRV_LEXICAL_BLOCK_SIMPLE
)
putattr
(
ctxt
,
info
,
DW_ABRV_LEXICAL_BLOCK_SIMPLE
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
scope
.
Ranges
[
0
]
.
Start
,
startPC
)
putattr
(
ctxt
,
info
,
DW_ABRV_LEXICAL_BLOCK_SIMPLE
,
DW_FORM_addr
,
DW_CLS_ADDRESS
,
scope
.
Ranges
[
0
]
.
End
,
startPC
)
}
else
{
Uleb128put
(
ctxt
,
s
,
DW_ABRV_LEXICAL_BLOCK_RANGES
)
putattr
(
ctxt
,
s
,
DW_ABRV_LEXICAL_BLOCK_RANGES
,
DW_FORM_sec_offset
,
DW_CLS_PTR
,
ranges
.
Len
(),
ranges
)
Uleb128put
(
ctxt
,
info
,
DW_ABRV_LEXICAL_BLOCK_RANGES
)
putattr
(
ctxt
,
info
,
DW_ABRV_LEXICAL_BLOCK_RANGES
,
DW_FORM_sec_offset
,
DW_CLS_PTR
,
ranges
.
Len
(),
ranges
)
ctxt
.
AddAddress
(
ranges
,
nil
,
-
1
)
ctxt
.
AddAddress
(
ranges
,
startPC
,
0
)
...
...
@@ -736,32 +781,72 @@ func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes [
ctxt
.
AddAddress
(
ranges
,
nil
,
0
)
}
curscope
=
putscope
(
ctxt
,
s
,
ranges
,
startPC
,
curscope
,
scopes
,
encbuf
)
curscope
=
putscope
(
ctxt
,
info
,
loc
,
ranges
,
startPC
,
curscope
,
scopes
,
encbuf
)
Uleb128put
(
ctxt
,
s
,
0
)
Uleb128put
(
ctxt
,
info
,
0
)
}
return
curscope
}
func
putvar
(
ctxt
Context
,
s
Sym
,
v
*
Var
,
encbuf
[]
byte
)
{
func
putvar
(
ctxt
Context
,
info
,
loc
Sym
,
v
*
Var
,
startPC
Sym
,
encbuf
[]
byte
)
{
n
:=
v
.
Name
Uleb128put
(
ctxt
,
s
,
int64
(
v
.
Abbrev
))
putattr
(
ctxt
,
s
,
v
.
Abbrev
,
DW_FORM_string
,
DW_CLS_STRING
,
int64
(
len
(
n
)),
n
)
loc
:=
append
(
encbuf
[
:
0
],
DW_OP_call_frame_cfa
)
if
v
.
Offset
!=
0
{
loc
=
append
(
loc
,
DW_OP_consts
)
loc
=
AppendSleb128
(
loc
,
int64
(
v
.
Offset
))
loc
=
append
(
loc
,
DW_OP_plus
)
Uleb128put
(
ctxt
,
info
,
int64
(
v
.
Abbrev
))
putattr
(
ctxt
,
info
,
v
.
Abbrev
,
DW_FORM_string
,
DW_CLS_STRING
,
int64
(
len
(
n
)),
n
)
if
v
.
Abbrev
==
DW_ABRV_AUTO_LOCLIST
||
v
.
Abbrev
==
DW_ABRV_PARAM_LOCLIST
{
putattr
(
ctxt
,
info
,
v
.
Abbrev
,
DW_FORM_sec_offset
,
DW_CLS_PTR
,
int64
(
loc
.
Len
()),
loc
)
addLocList
(
ctxt
,
loc
,
startPC
,
v
,
encbuf
)
}
else
{
loc
:=
append
(
encbuf
[
:
0
],
DW_OP_call_frame_cfa
)
if
v
.
StackOffset
!=
0
{
loc
=
append
(
loc
,
DW_OP_consts
)
loc
=
AppendSleb128
(
loc
,
int64
(
v
.
StackOffset
))
loc
=
append
(
loc
,
DW_OP_plus
)
}
putattr
(
ctxt
,
info
,
v
.
Abbrev
,
DW_FORM_block1
,
DW_CLS_BLOCK
,
int64
(
len
(
loc
)),
loc
)
}
putattr
(
ctxt
,
info
,
v
.
Abbrev
,
DW_FORM_ref_addr
,
DW_CLS_REFERENCE
,
0
,
v
.
Type
)
}
func
addLocList
(
ctxt
Context
,
listSym
,
startPC
Sym
,
v
*
Var
,
encbuf
[]
byte
)
{
// Base address entry: max ptr followed by the base address.
ctxt
.
AddInt
(
listSym
,
ctxt
.
PtrSize
(),
^
0
)
ctxt
.
AddAddress
(
listSym
,
startPC
,
0
)
for
_
,
entry
:=
range
v
.
LocationList
{
ctxt
.
AddInt
(
listSym
,
ctxt
.
PtrSize
(),
entry
.
StartPC
)
ctxt
.
AddInt
(
listSym
,
ctxt
.
PtrSize
(),
entry
.
EndPC
)
locBuf
:=
encbuf
[
:
0
]
for
_
,
piece
:=
range
entry
.
Pieces
{
if
!
piece
.
Missing
{
if
piece
.
OnStack
{
locBuf
=
append
(
locBuf
,
DW_OP_fbreg
)
locBuf
=
AppendSleb128
(
locBuf
,
int64
(
piece
.
StackOffset
))
}
else
{
if
piece
.
RegNum
<
32
{
locBuf
=
append
(
locBuf
,
DW_OP_reg0
+
byte
(
piece
.
RegNum
))
}
else
{
locBuf
=
append
(
locBuf
,
DW_OP_regx
)
locBuf
=
AppendUleb128
(
locBuf
,
uint64
(
piece
.
RegNum
))
}
}
}
if
len
(
entry
.
Pieces
)
>
1
{
locBuf
=
append
(
locBuf
,
DW_OP_piece
)
locBuf
=
AppendUleb128
(
locBuf
,
uint64
(
piece
.
Length
))
}
}
ctxt
.
AddInt
(
listSym
,
2
,
int64
(
len
(
locBuf
)))
ctxt
.
AddBytes
(
listSym
,
locBuf
)
}
putattr
(
ctxt
,
s
,
v
.
Abbrev
,
DW_FORM_block1
,
DW_CLS_BLOCK
,
int64
(
len
(
loc
)),
loc
)
putattr
(
ctxt
,
s
,
v
.
Abbrev
,
DW_FORM_ref_addr
,
DW_CLS_REFERENCE
,
0
,
v
.
Type
)
// End list
ctxt
.
AddInt
(
listSym
,
ctxt
.
PtrSize
(),
0
)
ctxt
.
AddInt
(
listSym
,
ctxt
.
PtrSize
(),
0
)
}
// VarsByOffset attaches the methods of sort.Interface to []*Var,
// sorting in increasing Offset.
// sorting in increasing
Stack
Offset.
type
VarsByOffset
[]
*
Var
func
(
s
VarsByOffset
)
Len
()
int
{
return
len
(
s
)
}
func
(
s
VarsByOffset
)
Less
(
i
,
j
int
)
bool
{
return
s
[
i
]
.
Offset
<
s
[
j
]
.
Offset
}
func
(
s
VarsByOffset
)
Less
(
i
,
j
int
)
bool
{
return
s
[
i
]
.
StackOffset
<
s
[
j
]
.
Stack
Offset
}
func
(
s
VarsByOffset
)
Swap
(
i
,
j
int
)
{
s
[
i
],
s
[
j
]
=
s
[
j
],
s
[
i
]
}
src/cmd/internal/obj/link.go
View file @
6f6a9398
...
...
@@ -330,7 +330,8 @@ type FuncInfo struct {
Autom
[]
*
Auto
Pcln
Pcln
dwarfSym
*
LSym
dwarfInfoSym
*
LSym
dwarfLocSym
*
LSym
dwarfRangesSym
*
LSym
GCArgs
LSym
...
...
@@ -476,25 +477,26 @@ type Pcdata struct {
// Link holds the context for writing object code from a compiler
// to be linker input or for reading that input into the linker.
type
Link
struct
{
Headtype
objabi
.
HeadType
Arch
*
LinkArch
Debugasm
bool
Debugvlog
bool
Debugpcln
string
Flag_shared
bool
Flag_dynlink
bool
Flag_optimize
bool
Bso
*
bufio
.
Writer
Pathname
string
hashmu
sync
.
Mutex
// protects hash
hash
map
[
string
]
*
LSym
// name -> sym mapping
statichash
map
[
string
]
*
LSym
// name -> sym mapping for static syms
PosTable
src
.
PosTable
InlTree
InlTree
// global inlining tree used by gc/inl.go
Imports
[]
string
DiagFunc
func
(
string
,
...
interface
{})
DebugInfo
func
(
fn
*
LSym
,
curfn
interface
{})
[]
dwarf
.
Scope
// if non-nil, curfn is a *gc.Node
Errors
int
Headtype
objabi
.
HeadType
Arch
*
LinkArch
Debugasm
bool
Debugvlog
bool
Debugpcln
string
Flag_shared
bool
Flag_dynlink
bool
Flag_optimize
bool
Flag_locationlists
bool
Bso
*
bufio
.
Writer
Pathname
string
hashmu
sync
.
Mutex
// protects hash
hash
map
[
string
]
*
LSym
// name -> sym mapping
statichash
map
[
string
]
*
LSym
// name -> sym mapping for static syms
PosTable
src
.
PosTable
InlTree
InlTree
// global inlining tree used by gc/inl.go
Imports
[]
string
DiagFunc
func
(
string
,
...
interface
{})
DebugInfo
func
(
fn
*
LSym
,
curfn
interface
{})
[]
dwarf
.
Scope
// if non-nil, curfn is a *gc.Node
Errors
int
Framepointer_enabled
bool
...
...
@@ -533,9 +535,10 @@ func (ctxt *Link) FixedFrameSize() int64 {
// LinkArch is the definition of a single architecture.
type
LinkArch
struct
{
*
sys
.
Arch
Init
func
(
*
Link
)
Preprocess
func
(
*
Link
,
*
LSym
,
ProgAlloc
)
Assemble
func
(
*
Link
,
*
LSym
,
ProgAlloc
)
Progedit
func
(
*
Link
,
*
Prog
,
ProgAlloc
)
UnaryDst
map
[
As
]
bool
// Instruction takes one operand, a destination.
Init
func
(
*
Link
)
Preprocess
func
(
*
Link
,
*
LSym
,
ProgAlloc
)
Assemble
func
(
*
Link
,
*
LSym
,
ProgAlloc
)
Progedit
func
(
*
Link
,
*
Prog
,
ProgAlloc
)
UnaryDst
map
[
As
]
bool
// Instruction takes one operand, a destination.
DWARFRegisters
map
[
int16
]
int16
}
src/cmd/internal/obj/objfile.go
View file @
6f6a9398
...
...
@@ -465,15 +465,18 @@ func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
}
// dwarfSym returns the DWARF symbols for TEXT symbol.
func
(
ctxt
*
Link
)
dwarfSym
(
s
*
LSym
)
(
dwarfInfoSym
,
dwarfRangesSym
*
LSym
)
{
func
(
ctxt
*
Link
)
dwarfSym
(
s
*
LSym
)
(
dwarfInfoSym
,
dwarf
LocSym
,
dwarf
RangesSym
*
LSym
)
{
if
s
.
Type
!=
objabi
.
STEXT
{
ctxt
.
Diag
(
"dwarfSym of non-TEXT %v"
,
s
)
}
if
s
.
Func
.
dwarfSym
==
nil
{
s
.
Func
.
dwarfSym
=
ctxt
.
LookupDerived
(
s
,
dwarf
.
InfoPrefix
+
s
.
Name
)
if
s
.
Func
.
dwarfInfoSym
==
nil
{
s
.
Func
.
dwarfInfoSym
=
ctxt
.
LookupDerived
(
s
,
dwarf
.
InfoPrefix
+
s
.
Name
)
if
ctxt
.
Flag_locationlists
{
s
.
Func
.
dwarfLocSym
=
ctxt
.
LookupDerived
(
s
,
dwarf
.
LocPrefix
+
s
.
Name
)
}
s
.
Func
.
dwarfRangesSym
=
ctxt
.
LookupDerived
(
s
,
dwarf
.
RangePrefix
+
s
.
Name
)
}
return
s
.
Func
.
dwarfSym
,
s
.
Func
.
dwarfRangesSym
return
s
.
Func
.
dwarf
InfoSym
,
s
.
Func
.
dwarfLoc
Sym
,
s
.
Func
.
dwarfRangesSym
}
func
(
s
*
LSym
)
Len
()
int64
{
...
...
@@ -483,15 +486,15 @@ func (s *LSym) Len() int64 {
// populateDWARF fills in the DWARF Debugging Information Entries for TEXT symbol s.
// The DWARFs symbol must already have been initialized in InitTextSym.
func
(
ctxt
*
Link
)
populateDWARF
(
curfn
interface
{},
s
*
LSym
)
{
dsym
,
drsym
:=
ctxt
.
dwarfSym
(
s
)
if
dsym
.
Size
!=
0
{
info
,
loc
,
ranges
:=
ctxt
.
dwarfSym
(
s
)
if
info
.
Size
!=
0
{
ctxt
.
Diag
(
"makeFuncDebugEntry double process %v"
,
s
)
}
var
scopes
[]
dwarf
.
Scope
if
ctxt
.
DebugInfo
!=
nil
{
scopes
=
ctxt
.
DebugInfo
(
s
,
curfn
)
}
err
:=
dwarf
.
PutFunc
(
dwCtxt
{
ctxt
},
dsym
,
drsym
,
s
.
Name
,
!
s
.
Static
(),
s
,
s
.
Size
,
scopes
)
err
:=
dwarf
.
PutFunc
(
dwCtxt
{
ctxt
},
info
,
loc
,
ranges
,
s
.
Name
,
!
s
.
Static
(),
s
,
s
.
Size
,
scopes
)
if
err
!=
nil
{
ctxt
.
Diag
(
"emitting DWARF for %s failed: %v"
,
s
.
Name
,
err
)
}
...
...
src/cmd/internal/obj/plist.go
View file @
6f6a9398
...
...
@@ -136,13 +136,17 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
ctxt
.
Text
=
append
(
ctxt
.
Text
,
s
)
// Set up DWARF entries for s.
dsym
,
drsym
:=
ctxt
.
dwarfSym
(
s
)
dsym
.
Type
=
objabi
.
SDWARFINFO
dsym
.
Set
(
AttrDuplicateOK
,
s
.
DuplicateOK
())
drsym
.
Type
=
objabi
.
SDWARFRANGE
drsym
.
Set
(
AttrDuplicateOK
,
s
.
DuplicateOK
())
ctxt
.
Data
=
append
(
ctxt
.
Data
,
dsym
)
ctxt
.
Data
=
append
(
ctxt
.
Data
,
drsym
)
info
,
loc
,
ranges
:=
ctxt
.
dwarfSym
(
s
)
info
.
Type
=
objabi
.
SDWARFINFO
info
.
Set
(
AttrDuplicateOK
,
s
.
DuplicateOK
())
if
loc
!=
nil
{
loc
.
Type
=
objabi
.
SDWARFLOC
loc
.
Set
(
AttrDuplicateOK
,
s
.
DuplicateOK
())
ctxt
.
Data
=
append
(
ctxt
.
Data
,
loc
)
}
ranges
.
Type
=
objabi
.
SDWARFRANGE
ranges
.
Set
(
AttrDuplicateOK
,
s
.
DuplicateOK
())
ctxt
.
Data
=
append
(
ctxt
.
Data
,
info
,
ranges
)
// Set up the function's gcargs and gclocals.
// They will be filled in later if needed.
...
...
src/cmd/internal/obj/x86/a.out.go
View file @
6f6a9398
...
...
@@ -1006,3 +1006,120 @@ const (
T_64
=
1
<<
6
T_GOTYPE
=
1
<<
7
)
// https://www.uclibc.org/docs/psABI-x86_64.pdf, figure 3.36
var
AMD64DWARFRegisters
=
map
[
int16
]
int16
{
REG_AX
:
0
,
REG_DX
:
1
,
REG_CX
:
2
,
REG_BX
:
3
,
REG_SI
:
4
,
REG_DI
:
5
,
REG_BP
:
6
,
REG_SP
:
7
,
REG_R8
:
8
,
REG_R9
:
9
,
REG_R10
:
10
,
REG_R11
:
11
,
REG_R12
:
12
,
REG_R13
:
13
,
REG_R14
:
14
,
REG_R15
:
15
,
// 16 is "Return Address RA", whatever that is.
// XMM registers. %xmmN => XN.
REG_X0
:
17
,
REG_X1
:
18
,
REG_X2
:
19
,
REG_X3
:
20
,
REG_X4
:
21
,
REG_X5
:
22
,
REG_X6
:
23
,
REG_X7
:
24
,
REG_X8
:
25
,
REG_X9
:
26
,
REG_X10
:
27
,
REG_X11
:
28
,
REG_X12
:
29
,
REG_X13
:
30
,
REG_X14
:
31
,
REG_X15
:
32
,
// ST registers. %stN => FN.
REG_F0
:
33
,
REG_F1
:
34
,
REG_F2
:
35
,
REG_F3
:
36
,
REG_F4
:
37
,
REG_F5
:
38
,
REG_F6
:
39
,
REG_F7
:
40
,
// MMX registers. %mmN => MN.
REG_M0
:
41
,
REG_M1
:
42
,
REG_M2
:
43
,
REG_M3
:
44
,
REG_M4
:
45
,
REG_M5
:
46
,
REG_M6
:
47
,
REG_M7
:
48
,
// 48 is flags, which doesn't have a name.
REG_ES
:
50
,
REG_CS
:
51
,
REG_SS
:
52
,
REG_DS
:
53
,
REG_FS
:
54
,
REG_GS
:
55
,
// 58 and 59 are {fs,gs}base, which don't have names.
REG_TR
:
62
,
REG_LDTR
:
63
,
// 64-66 are mxcsr, fcw, fsw, which don't have names.
}
// https://www.uclibc.org/docs/psABI-i386.pdf, table 2.14
var
X86DWARFRegisters
=
map
[
int16
]
int16
{
REG_AX
:
0
,
REG_CX
:
1
,
REG_DX
:
2
,
REG_BX
:
3
,
REG_SP
:
4
,
REG_BP
:
5
,
REG_SI
:
6
,
REG_DI
:
7
,
// 8 is "Return Address RA", whatever that is.
// 9 is flags, which doesn't have a name.
// ST registers. %stN => FN.
REG_F0
:
11
,
REG_F1
:
12
,
REG_F2
:
13
,
REG_F3
:
14
,
REG_F4
:
15
,
REG_F5
:
16
,
REG_F6
:
17
,
REG_F7
:
18
,
// XMM registers. %xmmN => XN.
REG_X0
:
21
,
REG_X1
:
22
,
REG_X2
:
23
,
REG_X3
:
24
,
REG_X4
:
25
,
REG_X5
:
26
,
REG_X6
:
27
,
REG_X7
:
28
,
// MMX registers. %mmN => MN.
REG_M0
:
29
,
REG_M1
:
30
,
REG_M2
:
31
,
REG_M3
:
32
,
REG_M4
:
33
,
REG_M5
:
34
,
REG_M6
:
35
,
REG_M7
:
36
,
// 39 is mxcsr, which doesn't have a name.
REG_ES
:
40
,
REG_CS
:
41
,
REG_SS
:
42
,
REG_DS
:
43
,
REG_FS
:
44
,
REG_GS
:
45
,
REG_TR
:
48
,
REG_LDTR
:
49
,
}
src/cmd/internal/obj/x86/obj6.go
View file @
6f6a9398
...
...
@@ -1231,28 +1231,31 @@ var unaryDst = map[obj.As]bool{
}
var
Linkamd64
=
obj
.
LinkArch
{
Arch
:
sys
.
ArchAMD64
,
Init
:
instinit
,
Preprocess
:
preprocess
,
Assemble
:
span6
,
Progedit
:
progedit
,
UnaryDst
:
unaryDst
,
Arch
:
sys
.
ArchAMD64
,
Init
:
instinit
,
Preprocess
:
preprocess
,
Assemble
:
span6
,
Progedit
:
progedit
,
UnaryDst
:
unaryDst
,
DWARFRegisters
:
AMD64DWARFRegisters
,
}
var
Linkamd64p32
=
obj
.
LinkArch
{
Arch
:
sys
.
ArchAMD64P32
,
Init
:
instinit
,
Preprocess
:
preprocess
,
Assemble
:
span6
,
Progedit
:
progedit
,
UnaryDst
:
unaryDst
,
Arch
:
sys
.
ArchAMD64P32
,
Init
:
instinit
,
Preprocess
:
preprocess
,
Assemble
:
span6
,
Progedit
:
progedit
,
UnaryDst
:
unaryDst
,
DWARFRegisters
:
AMD64DWARFRegisters
,
}
var
Link386
=
obj
.
LinkArch
{
Arch
:
sys
.
Arch386
,
Init
:
instinit
,
Preprocess
:
preprocess
,
Assemble
:
span6
,
Progedit
:
progedit
,
UnaryDst
:
unaryDst
,
Arch
:
sys
.
Arch386
,
Init
:
instinit
,
Preprocess
:
preprocess
,
Assemble
:
span6
,
Progedit
:
progedit
,
UnaryDst
:
unaryDst
,
DWARFRegisters
:
AMD64DWARFRegisters
,
}
src/cmd/internal/objabi/symkind.go
View file @
6f6a9398
...
...
@@ -57,4 +57,5 @@ const (
// Debugging data
SDWARFINFO
SDWARFRANGE
SDWARFLOC
)
src/cmd/internal/objabi/symkind_string.go
View file @
6f6a9398
...
...
@@ -4,9 +4,9 @@ package objabi
import
"fmt"
const
_SymKind_name
=
"SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGE"
const
_SymKind_name
=
"SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGE
SDWARFLOC
"
var
_SymKind_index
=
[
...
]
uint8
{
0
,
4
,
9
,
16
,
26
,
31
,
35
,
44
,
51
,
61
,
72
}
var
_SymKind_index
=
[
...
]
uint8
{
0
,
4
,
9
,
16
,
26
,
31
,
35
,
44
,
51
,
61
,
72
,
81
}
func
(
i
SymKind
)
String
()
string
{
if
i
>=
SymKind
(
len
(
_SymKind_index
)
-
1
)
{
...
...
src/cmd/link/internal/ld/data.go
View file @
6f6a9398
...
...
@@ -592,15 +592,7 @@ func relocsym(ctxt *Link, s *Symbol) {
}
case
objabi
.
R_DWARFREF
:
var
sectName
string
var
vaddr
int64
switch
{
case
r
.
Sym
.
Sect
!=
nil
:
sectName
=
r
.
Sym
.
Sect
.
Name
vaddr
=
int64
(
r
.
Sym
.
Sect
.
Vaddr
)
case
r
.
Sym
.
Type
==
SDWARFRANGE
:
sectName
=
".debug_ranges"
default
:
if
r
.
Sym
.
Sect
==
nil
{
Errorf
(
s
,
"missing DWARF section for relocation target %s"
,
r
.
Sym
.
Name
)
}
...
...
@@ -615,8 +607,8 @@ func relocsym(ctxt *Link, s *Symbol) {
r
.
Type
=
objabi
.
R_ADDR
}
r
.
Xsym
=
ctxt
.
Syms
.
ROLookup
(
sect
Name
,
0
)
r
.
Xadd
=
r
.
Add
+
Symaddr
(
r
.
Sym
)
-
vaddr
r
.
Xsym
=
ctxt
.
Syms
.
ROLookup
(
r
.
Sym
.
Sect
.
Name
,
0
)
r
.
Xadd
=
r
.
Add
+
Symaddr
(
r
.
Sym
)
-
int64
(
r
.
Sym
.
Sect
.
Vaddr
)
o
=
r
.
Xadd
rs
=
r
.
Xsym
...
...
@@ -625,7 +617,7 @@ func relocsym(ctxt *Link, s *Symbol) {
}
break
}
o
=
Symaddr
(
r
.
Sym
)
+
r
.
Add
-
vaddr
o
=
Symaddr
(
r
.
Sym
)
+
r
.
Add
-
int64
(
r
.
Sym
.
Sect
.
Vaddr
)
case
objabi
.
R_WEAKADDROFF
:
if
!
r
.
Sym
.
Attr
.
Reachable
()
{
...
...
@@ -1843,9 +1835,9 @@ func (ctxt *Link) dodata() {
dwarfgeneratedebugsyms
(
ctxt
)
var
s
*
Symbol
var
i
int
for
i
,
s
=
range
dwarfp
{
for
;
i
<
len
(
dwarfp
);
i
++
{
s
:=
dwarfp
[
i
]
if
s
.
Type
!=
SDWARFSECT
{
break
}
...
...
@@ -1862,13 +1854,26 @@ func (ctxt *Link) dodata() {
}
checkdatsize
(
ctxt
,
datsize
,
SDWARFSECT
)
if
i
<
len
(
dwarfp
)
{
sect
=
addsection
(
&
Segdwarf
,
".debug_info"
,
04
)
for
i
<
len
(
dwarfp
)
{
curType
:=
dwarfp
[
i
]
.
Type
var
sect
*
Section
switch
curType
{
case
SDWARFINFO
:
sect
=
addsection
(
&
Segdwarf
,
".debug_info"
,
04
)
case
SDWARFRANGE
:
sect
=
addsection
(
&
Segdwarf
,
".debug_ranges"
,
04
)
case
SDWARFLOC
:
sect
=
addsection
(
&
Segdwarf
,
".debug_loc"
,
04
)
default
:
Errorf
(
dwarfp
[
i
],
"unknown DWARF section %v"
,
curType
)
}
sect
.
Align
=
1
datsize
=
Rnd
(
datsize
,
int64
(
sect
.
Align
))
sect
.
Vaddr
=
uint64
(
datsize
)
for
_
,
s
:=
range
dwarfp
[
i
:
]
{
if
s
.
Type
!=
SDWARFINFO
{
for
;
i
<
len
(
dwarfp
);
i
++
{
s
:=
dwarfp
[
i
]
if
s
.
Type
!=
curType
{
break
}
s
.
Sect
=
sect
...
...
@@ -1878,7 +1883,7 @@ func (ctxt *Link) dodata() {
datsize
+=
s
.
Size
}
sect
.
Length
=
uint64
(
datsize
)
-
sect
.
Vaddr
checkdatsize
(
ctxt
,
datsize
,
SDWARFINFO
)
checkdatsize
(
ctxt
,
datsize
,
curType
)
}
/* number the sections */
...
...
src/cmd/link/internal/ld/dwarf.go
View file @
6f6a9398
...
...
@@ -67,26 +67,15 @@ func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
r
.
Add
=
ofs
}
/*
* Offsets and sizes of the debug_* sections in the cout file.
*/
var
abbrevsym
*
Symbol
var
arangessec
*
Symbol
var
framesec
*
Symbol
var
infosec
*
Symbol
var
linesec
*
Symbol
var
rangesec
*
Symbol
var
gdbscript
string
var
dwarfp
[]
*
Symbol
func
writeabbrev
(
ctxt
*
Link
,
syms
[]
*
Symbol
)
[]
*
Symbol
{
func
writeabbrev
(
ctxt
*
Link
)
*
Symbol
{
s
:=
ctxt
.
Syms
.
Lookup
(
".debug_abbrev"
,
0
)
s
.
Type
=
SDWARFSECT
abbrevsym
=
s
Addbytes
(
s
,
dwarf
.
GetAbbrev
())
return
append
(
syms
,
s
)
return
s
}
/*
...
...
@@ -993,13 +982,10 @@ func getCompilationDir() string {
func
writelines
(
ctxt
*
Link
,
syms
[]
*
Symbol
)
([]
*
Symbol
,
[]
*
Symbol
)
{
var
dwarfctxt
dwarf
.
Context
=
dwctxt
{
ctxt
}
if
linesec
==
nil
{
linesec
=
ctxt
.
Syms
.
Lookup
(
".debug_line"
,
0
)
}
linesec
.
Type
=
SDWARFSECT
linesec
.
R
=
linesec
.
R
[
:
0
]
ls
:=
ctxt
.
Syms
.
Lookup
(
".debug_line"
,
0
)
ls
.
Type
=
SDWARFSECT
ls
.
R
=
ls
.
R
[
:
0
]
ls
:=
linesec
syms
=
append
(
syms
,
ls
)
var
funcs
[]
*
Symbol
...
...
@@ -1019,7 +1005,7 @@ func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) {
dwinfo
=
newdie
(
ctxt
,
&
dwroot
,
dwarf
.
DW_ABRV_COMPUNIT
,
"go"
,
0
)
newattr
(
dwinfo
,
dwarf
.
DW_AT_language
,
dwarf
.
DW_CLS_CONSTANT
,
int64
(
lang
),
0
)
newattr
(
dwinfo
,
dwarf
.
DW_AT_stmt_list
,
dwarf
.
DW_CLS_PTR
,
0
,
l
inesec
)
newattr
(
dwinfo
,
dwarf
.
DW_AT_stmt_list
,
dwarf
.
DW_CLS_PTR
,
0
,
l
s
)
newattr
(
dwinfo
,
dwarf
.
DW_AT_low_pc
,
dwarf
.
DW_CLS_ADDRESS
,
s
.
Value
,
s
)
// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
compDir
:=
getCompilationDir
()
...
...
@@ -1178,12 +1164,9 @@ func appendPCDeltaCFA(b []byte, deltapc, cfa int64) []byte {
func
writeframes
(
ctxt
*
Link
,
syms
[]
*
Symbol
)
[]
*
Symbol
{
var
dwarfctxt
dwarf
.
Context
=
dwctxt
{
ctxt
}
if
framesec
==
nil
{
framesec
=
ctxt
.
Syms
.
Lookup
(
".debug_frame"
,
0
)
}
framesec
.
Type
=
SDWARFSECT
framesec
.
R
=
framesec
.
R
[
:
0
]
fs
:=
framesec
fs
:=
ctxt
.
Syms
.
Lookup
(
".debug_frame"
,
0
)
fs
.
Type
=
SDWARFSECT
fs
.
R
=
fs
.
R
[
:
0
]
syms
=
append
(
syms
,
fs
)
// Emit the CIE, Section 6.4.1
...
...
@@ -1280,7 +1263,7 @@ func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
// ptrsize: address range
Adduint32
(
ctxt
,
fs
,
uint32
(
4
+
2
*
SysArch
.
PtrSize
+
len
(
deltaBuf
)))
// length (excludes itself)
if
Linkmode
==
LinkExternal
{
adddwarfref
(
ctxt
,
fs
,
f
ramesec
,
4
)
adddwarfref
(
ctxt
,
fs
,
f
s
,
4
)
}
else
{
Adduint32
(
ctxt
,
fs
,
0
)
// CIE offset
}
...
...
@@ -1292,27 +1275,24 @@ func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
}
func
writeranges
(
ctxt
*
Link
,
syms
[]
*
Symbol
)
[]
*
Symbol
{
if
rangesec
==
nil
{
rangesec
=
ctxt
.
Syms
.
Lookup
(
".debug_ranges"
,
0
)
}
rangesec
.
Type
=
SDWARFSECT
rangesec
.
Attr
|=
AttrReachable
rangesec
.
R
=
rangesec
.
R
[
:
0
]
empty
:=
true
for
_
,
s
:=
range
ctxt
.
Textp
{
rangeSym
:=
ctxt
.
Syms
.
Lookup
(
dwarf
.
RangePrefix
+
s
.
Name
,
int
(
s
.
Version
))
rangeSym
.
Attr
|=
AttrReachable
rangeSym
.
Type
=
SDWARFRANGE
rangeSym
.
Value
=
rangesec
.
Size
rangesec
.
P
=
append
(
rangesec
.
P
,
rangeSym
.
P
...
)
for
_
,
r
:=
range
rangeSym
.
R
{
r
.
Off
+=
int32
(
rangesec
.
Size
)
rangesec
.
R
=
append
(
rangesec
.
R
,
r
)
if
rangeSym
.
Size
==
0
{
continue
}
rangesec
.
Size
+=
rangeSym
.
Size
rangeSym
.
Attr
|=
AttrReachable
|
AttrNotInSymbolTable
rangeSym
.
Type
=
SDWARFRANGE
syms
=
append
(
syms
,
rangeSym
)
empty
=
false
}
if
rangesec
.
Size
>
0
{
if
!
empty
{
// PE does not like empty sections
rangesec
:=
ctxt
.
Syms
.
Lookup
(
".debug_ranges"
,
0
)
rangesec
.
Type
=
SDWARFRANGE
rangesec
.
Attr
|=
AttrReachable
rangesec
.
R
=
rangesec
.
R
[
:
0
]
syms
=
append
(
syms
,
rangesec
)
}
return
syms
...
...
@@ -1325,18 +1305,14 @@ const (
COMPUNITHEADERSIZE
=
4
+
2
+
4
+
1
)
func
writeinfo
(
ctxt
*
Link
,
syms
[]
*
Symbol
,
funcs
[]
*
Symbol
)
[]
*
Symbol
{
if
infosec
==
nil
{
infosec
=
ctxt
.
Syms
.
Lookup
(
".debug_info"
,
0
)
}
func
writeinfo
(
ctxt
*
Link
,
syms
[]
*
Symbol
,
funcs
[]
*
Symbol
,
abbrevsym
*
Symbol
)
[]
*
Symbol
{
infosec
:=
ctxt
.
Syms
.
Lookup
(
".debug_info"
,
0
)
infosec
.
R
=
infosec
.
R
[
:
0
]
infosec
.
Type
=
SDWARFINFO
infosec
.
Attr
|=
AttrReachable
syms
=
append
(
syms
,
infosec
)
if
arangessec
==
nil
{
arangessec
=
ctxt
.
Syms
.
Lookup
(
".dwarfaranges"
,
0
)
}
arangessec
:=
ctxt
.
Syms
.
Lookup
(
".dwarfaranges"
,
0
)
arangessec
.
R
=
arangessec
.
R
[
:
0
]
var
dwarfctxt
dwarf
.
Context
=
dwctxt
{
ctxt
}
...
...
@@ -1577,10 +1553,10 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
genasmsym
(
ctxt
,
defdwsymb
)
syms
:=
writeabbrev
(
ctxt
,
nil
)
abbrev
:=
writeabbrev
(
ctxt
)
syms
:=
[]
*
Symbol
{
abbrev
}
syms
,
funcs
:=
writelines
(
ctxt
,
syms
)
syms
=
writeframes
(
ctxt
,
syms
)
syms
=
writeranges
(
ctxt
,
syms
)
synthesizestringtypes
(
ctxt
,
dwtypes
.
Child
)
synthesizeslicetypes
(
ctxt
,
dwtypes
.
Child
)
...
...
@@ -1596,16 +1572,42 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
// Need to reorder symbols so SDWARFINFO is after all SDWARFSECT
// (but we need to generate dies before writepub)
infosyms
:=
writeinfo
(
ctxt
,
nil
,
funcs
)
infosyms
:=
writeinfo
(
ctxt
,
nil
,
funcs
,
abbrev
)
syms
=
writepub
(
ctxt
,
".debug_pubnames"
,
ispubname
,
syms
)
syms
=
writepub
(
ctxt
,
".debug_pubtypes"
,
ispubtype
,
syms
)
syms
=
writearanges
(
ctxt
,
syms
)
syms
=
writegdbscript
(
ctxt
,
syms
)
syms
=
append
(
syms
,
infosyms
...
)
syms
=
collectlocs
(
ctxt
,
syms
,
funcs
)
syms
=
writeranges
(
ctxt
,
syms
)
dwarfp
=
syms
}
func
collectlocs
(
ctxt
*
Link
,
syms
[]
*
Symbol
,
funcs
[]
*
Symbol
)
[]
*
Symbol
{
empty
:=
true
for
_
,
fn
:=
range
funcs
{
for
_
,
reloc
:=
range
fn
.
R
{
if
reloc
.
Type
==
objabi
.
R_DWARFREF
&&
strings
.
HasPrefix
(
reloc
.
Sym
.
Name
,
dwarf
.
LocPrefix
)
{
reloc
.
Sym
.
Attr
|=
AttrReachable
|
AttrNotInSymbolTable
syms
=
append
(
syms
,
reloc
.
Sym
)
empty
=
false
// One location list entry per function, but many relocations to it. Don't duplicate.
break
}
}
}
// Don't emit .debug_loc if it's empty -- it makes the ARM linker mad.
if
!
empty
{
locsym
:=
ctxt
.
Syms
.
Lookup
(
".debug_loc"
,
0
)
locsym
.
R
=
locsym
.
R
[
:
0
]
locsym
.
Type
=
SDWARFLOC
locsym
.
Attr
|=
AttrReachable
syms
=
append
(
syms
,
locsym
)
}
return
syms
}
/*
* Elf.
*/
...
...
@@ -1618,6 +1620,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
Addstring
(
shstrtab
,
".debug_aranges"
)
Addstring
(
shstrtab
,
".debug_frame"
)
Addstring
(
shstrtab
,
".debug_info"
)
Addstring
(
shstrtab
,
".debug_loc"
)
Addstring
(
shstrtab
,
".debug_line"
)
Addstring
(
shstrtab
,
".debug_pubnames"
)
Addstring
(
shstrtab
,
".debug_pubtypes"
)
...
...
@@ -1625,6 +1628,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
Addstring
(
shstrtab
,
".debug_ranges"
)
if
Linkmode
==
LinkExternal
{
Addstring
(
shstrtab
,
elfRelType
+
".debug_info"
)
Addstring
(
shstrtab
,
elfRelType
+
".debug_loc"
)
Addstring
(
shstrtab
,
elfRelType
+
".debug_aranges"
)
Addstring
(
shstrtab
,
elfRelType
+
".debug_line"
)
Addstring
(
shstrtab
,
elfRelType
+
".debug_frame"
)
...
...
@@ -1651,6 +1655,10 @@ func dwarfaddelfsectionsyms(ctxt *Link) {
putelfsectionsym
(
sym
,
sym
.
Sect
.
Elfsect
.
shnum
)
sym
=
ctxt
.
Syms
.
Lookup
(
".debug_frame"
,
0
)
putelfsectionsym
(
sym
,
sym
.
Sect
.
Elfsect
.
shnum
)
sym
=
ctxt
.
Syms
.
Lookup
(
".debug_loc"
,
0
)
if
sym
.
Sect
!=
nil
{
putelfsectionsym
(
sym
,
sym
.
Sect
.
Elfsect
.
shnum
)
}
sym
=
ctxt
.
Syms
.
Lookup
(
".debug_ranges"
,
0
)
if
sym
.
Sect
!=
nil
{
putelfsectionsym
(
sym
,
sym
.
Sect
.
Elfsect
.
shnum
)
...
...
src/cmd/link/internal/ld/elf.go
View file @
6f6a9398
...
...
@@ -1808,7 +1808,7 @@ func elfrelocsect(ctxt *Link, sect *Section, syms []*Symbol) {
continue
}
if
r
.
Xsym
==
nil
{
Errorf
(
sym
,
"missing xsym in relocation
"
)
Errorf
(
sym
,
"missing xsym in relocation
%#v %#v"
,
r
.
Sym
.
Name
,
sym
)
continue
}
if
r
.
Xsym
.
ElfsymForReloc
()
==
0
{
...
...
@@ -2596,12 +2596,9 @@ elfobj:
elfshreloc
(
sect
)
}
for
_
,
s
:=
range
dwarfp
{
if
len
(
s
.
R
)
>
0
||
s
.
Type
==
SDWARFINFO
{
if
len
(
s
.
R
)
>
0
||
s
.
Type
==
SDWARFINFO
||
s
.
Type
==
SDWARFLOC
{
elfshreloc
(
s
.
Sect
)
}
if
s
.
Type
==
SDWARFINFO
{
break
}
}
// add a .note.GNU-stack section to mark the stack as non-executable
sh
:=
elfshname
(
".note.GNU-stack"
)
...
...
src/cmd/link/internal/ld/symkind.go
View file @
6f6a9398
...
...
@@ -105,6 +105,7 @@ const (
SDWARFSECT
SDWARFINFO
SDWARFRANGE
SDWARFLOC
SSUB
=
SymKind
(
1
<<
8
)
SMASK
=
SymKind
(
SSUB
-
1
)
SHIDDEN
=
SymKind
(
1
<<
9
)
...
...
@@ -124,6 +125,7 @@ var abiSymKindToSymKind = [...]SymKind{
STLSBSS
,
SDWARFINFO
,
SDWARFRANGE
,
SDWARFLOC
,
}
// readOnly are the symbol kinds that form read-only sections. In some
...
...
src/cmd/link/internal/ld/symkind_string.go
View file @
6f6a9398
...
...
@@ -4,9 +4,9 @@ package ld
import
"fmt"
const
_SymKind_name
=
"SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGE"
const
_SymKind_name
=
"SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGE
SDWARFLOC
"
var
_SymKind_index
=
[
...
]
uint16
{
0
,
4
,
9
,
19
,
24
,
31
,
40
,
47
,
54
,
61
,
69
,
79
,
88
,
98
,
110
,
124
,
136
,
148
,
160
,
173
,
182
,
191
,
198
,
206
,
214
,
220
,
229
,
237
,
244
,
254
,
262
,
267
,
271
,
280
,
287
,
292
,
304
,
316
,
333
,
350
,
355
,
364
,
370
,
380
,
388
,
398
,
408
,
419
}
var
_SymKind_index
=
[
...
]
uint16
{
0
,
4
,
9
,
19
,
24
,
31
,
40
,
47
,
54
,
61
,
69
,
79
,
88
,
98
,
110
,
124
,
136
,
148
,
160
,
173
,
182
,
191
,
198
,
206
,
214
,
220
,
229
,
237
,
244
,
254
,
262
,
267
,
271
,
280
,
287
,
292
,
304
,
316
,
333
,
350
,
355
,
364
,
370
,
380
,
388
,
398
,
408
,
419
,
428
}
func
(
i
SymKind
)
String
()
string
{
if
i
<
0
||
i
>=
SymKind
(
len
(
_SymKind_index
)
-
1
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment