2016-03-01 22:57:46 +00:00
// Copyright 2013 The Go Authors. All rights reserved.
2015-02-27 22:57:28 -05:00
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ld
import (
2019-07-30 17:28:29 -04:00
"cmd/internal/obj"
2017-04-18 12:53:25 -07:00
"cmd/internal/objabi"
2017-03-04 07:09:33 -08:00
"cmd/internal/src"
2018-10-04 16:32:21 -07:00
"cmd/internal/sys"
2020-04-07 17:08:00 -04:00
"cmd/link/internal/loader"
2017-10-04 17:54:04 -04:00
"cmd/link/internal/sym"
2019-04-12 09:29:43 -07:00
"encoding/binary"
cmd/compile, cmd/link, runtime: make defers low-cost through inline code and extra funcdata
Generate inline code at defer time to save the args of defer calls to unique
(autotmp) stack slots, and generate inline code at exit time to check which defer
calls were made and make the associated function/method/interface calls. We
remember that a particular defer statement was reached by storing in the deferBits
variable (always stored on the stack). At exit time, we check the bits of the
deferBits variable to determine which defer function calls to make (in reverse
order). These low-cost defers are only used for functions where no defers
appear in loops. In addition, we don't do these low-cost defers if there are too
many defer statements or too many exits in a function (to limit code increase).
When a function uses open-coded defers, we produce extra
FUNCDATA_OpenCodedDeferInfo information that specifies the number of defers, and
for each defer, the stack slots where the closure and associated args have been
stored. The funcdata also includes the location of the deferBits variable.
Therefore, for panics, we can use this funcdata to determine exactly which defers
are active, and call the appropriate functions/methods/closures with the correct
arguments for each active defer.
In order to unwind the stack correctly after a recover(), we need to add an extra
code segment to functions with open-coded defers that simply calls deferreturn()
and returns. This segment is not reachable by the normal function, but is returned
to by the runtime during recovery. We set the liveness information of this
deferreturn() to be the same as the liveness at the first function call during the
last defer exit code (so all return values and all stack slots needed by the defer
calls will be live).
I needed to increase the stackguard constant from 880 to 896, because of a small
amount of new code in deferreturn().
The -N flag disables open-coded defers. '-d defer' prints out the kind of defer
being used at each defer statement (heap-allocated, stack-allocated, or
open-coded).
Cost of defer statement [ go test -run NONE -bench BenchmarkDefer$ runtime ]
With normal (stack-allocated) defers only: 35.4 ns/op
With open-coded defers: 5.6 ns/op
Cost of function call alone (remove defer keyword): 4.4 ns/op
Text size increase (including funcdata) for go binary without/with open-coded defers: 0.09%
The average size increase (including funcdata) for only the functions that use
open-coded defers is 1.1%.
The cost of a panic followed by a recover got noticeably slower, since panic
processing now requires a scan of the stack for open-coded defer frames. This scan
is required, even if no frames are using open-coded defers:
Cost of panic and recover [ go test -run NONE -bench BenchmarkPanicRecover runtime ]
Without open-coded defers: 62.0 ns/op
With open-coded defers: 255 ns/op
A CGO Go-to-C-to-Go benchmark got noticeably faster because of open-coded defers:
CGO Go-to-C-to-Go benchmark [cd misc/cgo/test; go test -run NONE -bench BenchmarkCGoCallback ]
Without open-coded defers: 443 ns/op
With open-coded defers: 347 ns/op
Updates #14939 (defer performance)
Updates #34481 (design doc)
Change-Id: I63b1a60d1ebf28126f55ee9fd7ecffe9cb23d1ff
Reviewed-on: https://go-review.googlesource.com/c/go/+/202340
Reviewed-by: Austin Clements <austin@google.com>
2019-06-24 12:59:22 -07:00
"fmt"
2015-02-27 22:57:28 -05:00
"log"
2015-12-29 10:16:40 -05:00
"os"
"path/filepath"
2018-06-11 16:46:23 -07:00
"strings"
2015-02-27 22:57:28 -05:00
)
2020-04-07 17:08:00 -04:00
// pclnState holds state information used during pclntab generation.
// Here 'ldr' is just a pointer to the context's loader, 'container'
// is a bitmap holding whether a given symbol index is an outer or
// container symbol, 'deferReturnSym' is the index for the symbol
// "runtime.deferreturn", 'nameToOffset' is a helper function for
// capturing function names, 'numberedFiles' records the file number
// assigned to a given file symbol, 'filepaths' is a slice of
// expanded paths (indexed by file number).
type pclnState struct {
ldr * loader . Loader
container loader . Bitmap
deferReturnSym loader . Sym
nameToOffset func ( name string ) int32
numberedFiles map [ loader . Sym ] int64
filepaths [ ] string
}
func makepclnState ( ctxt * Link ) pclnState {
ldr := ctxt . loader
drs := ldr . Lookup ( "runtime.deferreturn" , sym . SymVerABIInternal )
return pclnState {
container : loader . MakeBitmap ( ldr . NSym ( ) ) ,
ldr : ldr ,
deferReturnSym : drs ,
numberedFiles : make ( map [ loader . Sym ] int64 ) ,
// NB: initial entry in filepaths below is to reserve the zero value,
// so that when we do a map lookup in numberedFiles fails, it will not
// return a value slot in filepaths.
filepaths : [ ] string { "" } ,
}
}
func ( state * pclnState ) ftabaddstring ( ftab * loader . SymbolBuilder , s string ) int32 {
start := len ( ftab . Data ( ) )
2019-04-11 18:05:19 -07:00
ftab . Grow ( int64 ( start + len ( s ) + 1 ) ) // make room for s plus trailing NUL
2020-04-07 17:08:00 -04:00
ftd := ftab . Data ( )
copy ( ftd [ start : ] , s )
2019-04-11 18:05:19 -07:00
return int32 ( start )
2015-02-27 22:57:28 -05:00
}
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
// numberfile assigns a file number to the file if it hasn't been assigned already.
2020-04-07 17:08:00 -04:00
func ( state * pclnState ) numberfile ( file loader . Sym ) int64 {
if val , ok := state . numberedFiles [ file ] ; ok {
return val
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
}
2020-04-07 17:08:00 -04:00
sn := state . ldr . SymName ( file )
path := sn [ len ( src . FileSymPrefix ) : ]
val := int64 ( len ( state . filepaths ) )
state . numberedFiles [ file ] = val
state . filepaths = append ( state . filepaths , expandGoroot ( path ) )
return val
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
}
2020-04-07 17:08:00 -04:00
func ( state * pclnState ) fileVal ( file loader . Sym ) int64 {
if val , ok := state . numberedFiles [ file ] ; ok {
return val
}
panic ( "should have been numbered first" )
}
func ( state * pclnState ) renumberfiles ( ctxt * Link , fi loader . FuncInfo , d * sym . Pcdata ) {
2015-02-27 22:57:28 -05:00
// Give files numbers.
2020-04-07 17:08:00 -04:00
nf := fi . NumFile ( )
for i := uint32 ( 0 ) ; i < nf ; i ++ {
state . numberfile ( fi . File ( int ( i ) ) )
2015-02-27 22:57:28 -05:00
}
2019-04-12 09:29:43 -07:00
buf := make ( [ ] byte , binary . MaxVarintLen32 )
2015-03-02 12:35:15 -05:00
newval := int32 ( - 1 )
2017-10-04 17:54:04 -04:00
var out sym . Pcdata
2019-07-30 17:28:29 -04:00
it := obj . NewPCIter ( uint32 ( ctxt . Arch . MinLC ) )
for it . Init ( d . P ) ; ! it . Done ; it . Next ( ) {
2015-02-27 22:57:28 -05:00
// value delta
2019-07-30 17:28:29 -04:00
oldval := it . Value
2015-02-27 22:57:28 -05:00
2016-04-20 14:22:20 -04:00
var val int32
2015-02-27 22:57:28 -05:00
if oldval == - 1 {
val = - 1
} else {
2020-04-07 17:08:00 -04:00
if oldval < 0 || oldval >= int32 ( nf ) {
2015-02-27 22:57:28 -05:00
log . Fatalf ( "bad pcdata %d" , oldval )
}
2020-04-07 17:08:00 -04:00
val = int32 ( state . fileVal ( fi . File ( int ( oldval ) ) ) )
2015-02-27 22:57:28 -05:00
}
2016-04-20 14:22:20 -04:00
dv := val - newval
2015-02-27 22:57:28 -05:00
newval = val
2019-04-12 09:29:43 -07:00
// value
n := binary . PutVarint ( buf , int64 ( dv ) )
out . P = append ( out . P , buf [ : n ] ... )
2015-02-27 22:57:28 -05:00
// pc delta
2019-07-30 17:28:29 -04:00
pc := ( it . NextPC - it . PC ) / it . PCScale
2019-04-12 09:29:43 -07:00
n = binary . PutUvarint ( buf , uint64 ( pc ) )
out . P = append ( out . P , buf [ : n ] ... )
2015-02-27 22:57:28 -05:00
}
// terminating value delta
2019-04-12 09:29:43 -07:00
// we want to write varint-encoded 0, which is just 0
out . P = append ( out . P , 0 )
2015-02-27 22:57:28 -05:00
* d = out
}
2020-04-07 17:08:00 -04:00
// onlycsymbol looks at a symbol's name to report whether this is a
// symbol that is referenced by C code
func onlycsymbol ( sname string ) bool {
switch sname {
2019-02-20 16:52:35 +01:00
case "_cgo_topofstack" , "__cgo_topofstack" , "_cgo_panic" , "crosscall2" :
2016-12-10 13:30:13 -05:00
return true
}
2020-04-07 17:08:00 -04:00
if strings . HasPrefix ( sname , "_cgoexp_" ) {
2018-06-11 16:46:23 -07:00
return true
}
2016-12-10 13:30:13 -05:00
return false
}
2020-04-10 10:30:27 -04:00
func emitPcln ( ctxt * Link , s loader . Sym , container loader . Bitmap ) bool {
if ctxt . BuildMode == BuildModePlugin && ctxt . HeadType == objabi . Hdarwin && onlycsymbol ( ctxt . loader . SymName ( s ) ) {
2020-04-07 17:08:00 -04:00
return false
}
// We want to generate func table entries only for the "lowest
// level" symbols, not containers of subsymbols.
2020-04-10 10:30:27 -04:00
return ! container . Has ( s )
2015-02-27 22:57:28 -05:00
}
2020-04-07 17:08:00 -04:00
func ( state * pclnState ) computeDeferReturn ( target * Target , s loader . Sym ) uint32 {
deferreturn := uint32 ( 0 )
lastWasmAddr := uint32 ( 0 )
relocs := state . ldr . Relocs ( s )
for ri := 0 ; ri < relocs . Count ( ) ; ri ++ {
r := relocs . At2 ( ri )
if target . IsWasm ( ) && r . Type ( ) == objabi . R_ADDR {
// Wasm does not have a live variable set at the deferreturn
// call itself. Instead it has one identified by the
// resumption point immediately preceding the deferreturn.
// The wasm code has a R_ADDR relocation which is used to
// set the resumption point to PC_B.
lastWasmAddr = uint32 ( r . Add ( ) )
}
2020-05-14 19:22:59 -04:00
if r . Type ( ) . IsDirectCall ( ) && ( r . Sym ( ) == state . deferReturnSym || state . ldr . IsDeferReturnTramp ( r . Sym ( ) ) ) {
2020-04-07 17:08:00 -04:00
if target . IsWasm ( ) {
deferreturn = lastWasmAddr - 1
} else {
// Note: the relocation target is in the call instruction, but
// is not necessarily the whole instruction (for instance, on
// x86 the relocation applies to bytes [1:5] of the 5 byte call
// instruction).
deferreturn = uint32 ( r . Off ( ) )
switch target . Arch . Family {
case sys . AMD64 , sys . I386 :
deferreturn --
case sys . PPC64 , sys . ARM , sys . ARM64 , sys . MIPS , sys . MIPS64 :
// no change
case sys . RISCV64 :
// TODO(jsing): The JALR instruction is marked with
// R_CALLRISCV, whereas the actual reloc is currently
// one instruction earlier starting with the AUIPC.
deferreturn -= 4
case sys . S390X :
deferreturn -= 2
default :
panic ( fmt . Sprint ( "Unhandled architecture:" , target . Arch . Family ) )
}
}
break // only need one
}
}
return deferreturn
}
2020-04-10 13:07:08 -04:00
// genInlTreeSym generates the InlTree sym for a function with the
// specified FuncInfo.
func ( state * pclnState ) genInlTreeSym ( fi loader . FuncInfo , arch * sys . Arch ) loader . Sym {
2020-04-07 17:08:00 -04:00
ldr := state . ldr
2020-04-10 13:07:08 -04:00
its := ldr . CreateExtSym ( "" , 0 )
2020-04-07 17:08:00 -04:00
inlTreeSym := ldr . MakeSymbolUpdater ( its )
2020-04-10 13:07:08 -04:00
// Note: the generated symbol is given a type of sym.SGOFUNC, as a
// signal to the symtab() phase that it needs to be grouped in with
// other similar symbols (gcdata, etc); the dodata() phase will
// eventually switch the type back to SRODATA.
inlTreeSym . SetType ( sym . SGOFUNC )
2020-04-07 17:08:00 -04:00
ldr . SetAttrReachable ( its , true )
ninl := fi . NumInlTree ( )
for i := 0 ; i < int ( ninl ) ; i ++ {
call := fi . InlTree ( i )
// Usually, call.File is already numbered since the file
// shows up in the Pcfile table. However, two inlined calls
// might overlap exactly so that only the innermost file
// appears in the Pcfile table. In that case, this assigns
// the outer file a number.
val := state . numberfile ( call . File )
fn := ldr . SymName ( call . Func )
nameoff := state . nameToOffset ( fn )
inlTreeSym . SetUint16 ( arch , int64 ( i * 20 + 0 ) , uint16 ( call . Parent ) )
inlTreeSym . SetUint8 ( arch , int64 ( i * 20 + 2 ) , uint8 ( objabi . GetFuncID ( fn , "" ) ) )
// byte 3 is unused
inlTreeSym . SetUint32 ( arch , int64 ( i * 20 + 4 ) , uint32 ( val ) )
inlTreeSym . SetUint32 ( arch , int64 ( i * 20 + 8 ) , uint32 ( call . Line ) )
inlTreeSym . SetUint32 ( arch , int64 ( i * 20 + 12 ) , uint32 ( nameoff ) )
inlTreeSym . SetUint32 ( arch , int64 ( i * 20 + 16 ) , uint32 ( call . ParentPC ) )
}
return its
}
2015-02-27 22:57:28 -05:00
// pclntab initializes the pclntab symbol with
// runtime function and file name information.
2015-04-07 12:55:02 +12:00
// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
2015-03-16 11:53:08 +13:00
var pclntabNfunc int32
var pclntabFiletabOffset int32
var pclntabPclntabOffset int32
2020-04-20 18:42:35 -04:00
var pclntabFirstFunc loader . Sym
var pclntabLastFunc loader . Sym
2015-03-16 11:53:08 +13:00
2020-04-10 10:30:27 -04:00
// pclntab generates the pcln table for the link output. Return value
// is a bitmap indexed by global symbol that marks 'container' text
// symbols, e.g. the set of all symbols X such that Outer(S) = X for
// some other text symbol S.
func ( ctxt * Link ) pclntab ( ) loader . Bitmap {
2016-08-22 10:33:13 -04:00
funcdataBytes := int64 ( 0 )
2020-04-07 17:08:00 -04:00
ldr := ctxt . loader
ftabsym := ldr . LookupOrCreateSym ( "runtime.pclntab" , 0 )
ftab := ldr . MakeSymbolUpdater ( ftabsym )
ftab . SetType ( sym . SPCLNTAB )
ldr . SetAttrReachable ( ftabsym , true )
state := makepclnState ( ctxt )
2015-02-27 22:57:28 -05:00
// See golang.org/s/go12symtab for the format. Briefly:
// 8-byte header
// nfunc [thearch.ptrsize bytes]
// function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
// end PC [thearch.ptrsize bytes]
// offset to file table [4 bytes]
2017-10-16 14:20:01 +13:00
// Find container symbols and mark them as such.
2020-05-15 18:35:05 -04:00
for _ , s := range ctxt . Textp {
2020-04-07 17:08:00 -04:00
outer := ldr . OuterSym ( s )
if outer != 0 {
state . container . Set ( outer )
2015-06-27 12:57:06 -07:00
}
}
2019-04-15 09:25:23 -07:00
// Gather some basic stats and info.
var nfunc int32
2020-05-15 18:35:05 -04:00
prevSect := ldr . SymSect ( ctxt . Textp [ 0 ] )
for _ , s := range ctxt . Textp {
2020-04-10 10:30:27 -04:00
if ! emitPcln ( ctxt , s , state . container ) {
2019-04-15 09:25:23 -07:00
continue
}
nfunc ++
2020-04-20 18:42:35 -04:00
if pclntabFirstFunc == 0 {
pclntabFirstFunc = s
2015-02-27 22:57:28 -05:00
}
2020-04-07 17:08:00 -04:00
ss := ldr . SymSect ( s )
if ss != prevSect {
// With multiple text sections, the external linker may
// insert functions between the sections, which are not
// known by Go. This leaves holes in the PC range covered
// by the func table. We need to generate an entry to mark
// the hole.
2020-02-16 16:18:04 -05:00
nfunc ++
2020-04-07 17:08:00 -04:00
prevSect = ss
2020-02-16 16:18:04 -05:00
}
2015-02-27 22:57:28 -05:00
}
2015-03-16 11:53:08 +13:00
pclntabNfunc = nfunc
2017-09-30 15:06:44 +00:00
ftab . Grow ( 8 + int64 ( ctxt . Arch . PtrSize ) + int64 ( nfunc ) * 2 * int64 ( ctxt . Arch . PtrSize ) + int64 ( ctxt . Arch . PtrSize ) + 4 )
ftab . SetUint32 ( ctxt . Arch , 0 , 0xfffffffb )
ftab . SetUint8 ( ctxt . Arch , 6 , uint8 ( ctxt . Arch . MinLC ) )
ftab . SetUint8 ( ctxt . Arch , 7 , uint8 ( ctxt . Arch . PtrSize ) )
ftab . SetUint ( ctxt . Arch , 8 , uint64 ( nfunc ) )
2017-09-30 21:10:49 +00:00
pclntabPclntabOffset = int32 ( 8 + ctxt . Arch . PtrSize )
2015-02-27 22:57:28 -05:00
2020-05-15 18:35:05 -04:00
szHint := len ( ctxt . Textp ) * 2
2020-04-07 17:08:00 -04:00
funcnameoff := make ( map [ string ] int32 , szHint )
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
nameToOffset := func ( name string ) int32 {
nameoff , ok := funcnameoff [ name ]
if ! ok {
2020-04-07 17:08:00 -04:00
nameoff = state . ftabaddstring ( ftab , name )
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
funcnameoff [ name ] = nameoff
}
return nameoff
}
2020-04-07 17:08:00 -04:00
state . nameToOffset = nameToOffset
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
2020-04-07 17:08:00 -04:00
pctaboff := make ( map [ string ] uint32 , szHint )
2019-04-15 09:39:29 -07:00
writepctab := func ( off int32 , p [ ] byte ) int32 {
start , ok := pctaboff [ string ( p ) ]
if ! ok {
if len ( p ) > 0 {
2020-04-07 17:08:00 -04:00
start = uint32 ( len ( ftab . Data ( ) ) )
2019-04-15 09:39:29 -07:00
ftab . AddBytes ( p )
}
pctaboff [ string ( p ) ] = start
}
newoff := int32 ( ftab . SetUint32 ( ctxt . Arch , int64 ( off ) , start ) )
return newoff
}
2020-04-15 22:23:41 -04:00
setAddr := ( * loader . SymbolBuilder ) . SetAddrPlus
2020-06-25 22:38:14 -04:00
if ctxt . IsExe ( ) && ctxt . IsInternal ( ) {
2020-04-15 22:23:41 -04:00
// Internal linking static executable. At this point the function
// addresses are known, so we can just use them instead of emitting
// relocations.
// For other cases we are generating a relocatable binary so we
// still need to emit relocations.
setAddr = func ( s * loader . SymbolBuilder , arch * sys . Arch , off int64 , tgt loader . Sym , add int64 ) int64 {
if v := ldr . SymValue ( tgt ) ; v != 0 {
return s . SetUint ( arch , off , uint64 ( v + add ) )
}
return s . SetAddrPlus ( arch , off , tgt , add )
}
}
2020-04-07 17:08:00 -04:00
pcsp := sym . Pcdata { }
pcfile := sym . Pcdata { }
pcline := sym . Pcdata { }
pcdata := [ ] sym . Pcdata { }
funcdata := [ ] loader . Sym { }
funcdataoff := [ ] int64 { }
2019-04-15 09:25:23 -07:00
nfunc = 0 // repurpose nfunc as a running index
2020-05-15 18:35:05 -04:00
prevFunc := ctxt . Textp [ 0 ]
for _ , s := range ctxt . Textp {
2020-04-10 10:30:27 -04:00
if ! emitPcln ( ctxt , s , state . container ) {
2015-02-27 22:57:28 -05:00
continue
}
2020-02-16 16:18:04 -05:00
2020-04-07 17:08:00 -04:00
thisSect := ldr . SymSect ( s )
prevSect := ldr . SymSect ( prevFunc )
if thisSect != prevSect {
// With multiple text sections, there may be a hole here
// in the address space (see the comment above). We use an
// invalid funcoff value to mark the hole. See also
// runtime/symtab.go:findfunc
prevFuncSize := int64 ( ldr . SymSize ( prevFunc ) )
2020-04-15 22:23:41 -04:00
setAddr ( ftab , ctxt . Arch , 8 + int64 ( ctxt . Arch . PtrSize ) + int64 ( nfunc ) * 2 * int64 ( ctxt . Arch . PtrSize ) , prevFunc , prevFuncSize )
2020-02-16 16:18:04 -05:00
ftab . SetUint ( ctxt . Arch , 8 + int64 ( ctxt . Arch . PtrSize ) + int64 ( nfunc ) * 2 * int64 ( ctxt . Arch . PtrSize ) + int64 ( ctxt . Arch . PtrSize ) , ^ uint64 ( 0 ) )
nfunc ++
}
prevFunc = s
2020-04-07 17:08:00 -04:00
pcsp . P = pcsp . P [ : 0 ]
pcline . P = pcline . P [ : 0 ]
pcfile . P = pcfile . P [ : 0 ]
pcdata = pcdata [ : 0 ]
funcdataoff = funcdataoff [ : 0 ]
funcdata = funcdata [ : 0 ]
fi := ldr . FuncInfo ( s )
if fi . Valid ( ) {
fi . Preload ( )
npc := fi . NumPcdata ( )
for i := uint32 ( 0 ) ; i < npc ; i ++ {
pcdata = append ( pcdata , sym . Pcdata { P : fi . Pcdata ( int ( i ) ) } )
}
nfd := fi . NumFuncdataoff ( )
for i := uint32 ( 0 ) ; i < nfd ; i ++ {
funcdataoff = append ( funcdataoff , fi . Funcdataoff ( int ( i ) ) )
}
2020-04-10 14:12:44 -04:00
funcdata = fi . Funcdata ( funcdata )
2015-02-27 22:57:28 -05:00
}
2020-04-07 17:08:00 -04:00
if fi . Valid ( ) && fi . NumInlTree ( ) > 0 {
if len ( pcdata ) <= objabi . PCDATA_InlTreeIndex {
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
// Create inlining pcdata table.
2020-04-07 17:08:00 -04:00
newpcdata := make ( [ ] sym . Pcdata , objabi . PCDATA_InlTreeIndex + 1 )
copy ( newpcdata , pcdata )
pcdata = newpcdata
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
}
2020-04-07 17:08:00 -04:00
if len ( funcdataoff ) <= objabi . FUNCDATA_InlTree {
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
// Create inline tree funcdata.
2020-04-07 17:08:00 -04:00
newfuncdata := make ( [ ] loader . Sym , objabi . FUNCDATA_InlTree + 1 )
newfuncdataoff := make ( [ ] int64 , objabi . FUNCDATA_InlTree + 1 )
copy ( newfuncdata , funcdata )
copy ( newfuncdataoff , funcdataoff )
funcdata = newfuncdata
funcdataoff = newfuncdataoff
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
}
}
2020-04-07 17:08:00 -04:00
dSize := len ( ftab . Data ( ) )
funcstart := int32 ( dSize )
funcstart += int32 ( - dSize ) & ( int32 ( ctxt . Arch . PtrSize ) - 1 ) // align to ptrsize
2015-02-27 22:57:28 -05:00
2020-04-15 22:23:41 -04:00
setAddr ( ftab , ctxt . Arch , 8 + int64 ( ctxt . Arch . PtrSize ) + int64 ( nfunc ) * 2 * int64 ( ctxt . Arch . PtrSize ) , s , 0 )
2017-09-30 15:06:44 +00:00
ftab . SetUint ( ctxt . Arch , 8 + int64 ( ctxt . Arch . PtrSize ) + int64 ( nfunc ) * 2 * int64 ( ctxt . Arch . PtrSize ) + int64 ( ctxt . Arch . PtrSize ) , uint64 ( funcstart ) )
2015-02-27 22:57:28 -05:00
2016-12-14 13:24:21 -05:00
// Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
// and package debug/gosym.
2015-02-27 22:57:28 -05:00
// fixed size of struct, checked below
2016-04-20 14:22:20 -04:00
off := funcstart
2015-02-27 22:57:28 -05:00
2020-04-07 17:08:00 -04:00
end := funcstart + int32 ( ctxt . Arch . PtrSize ) + 3 * 4 + 5 * 4 + int32 ( len ( pcdata ) ) * 4 + int32 ( len ( funcdata ) ) * int32 ( ctxt . Arch . PtrSize )
if len ( funcdata ) > 0 && ( end & int32 ( ctxt . Arch . PtrSize - 1 ) != 0 ) {
2015-02-27 22:57:28 -05:00
end += 4
}
2017-09-30 15:06:44 +00:00
ftab . Grow ( int64 ( end ) )
2015-02-27 22:57:28 -05:00
// entry uintptr
2020-04-15 22:23:41 -04:00
off = int32 ( setAddr ( ftab , ctxt . Arch , int64 ( off ) , s , 0 ) )
2015-02-27 22:57:28 -05:00
// name int32
2020-04-07 17:08:00 -04:00
sn := ldr . SymName ( s )
nameoff := nameToOffset ( sn )
2017-09-30 15:06:44 +00:00
off = int32 ( ftab . SetUint32 ( ctxt . Arch , int64 ( off ) , uint32 ( nameoff ) ) )
2015-02-27 22:57:28 -05:00
// args int32
// TODO: Move into funcinfo.
2016-04-11 22:19:34 +03:00
args := uint32 ( 0 )
2020-04-07 17:08:00 -04:00
if fi . Valid ( ) {
args = uint32 ( fi . Args ( ) )
2016-04-11 22:19:34 +03:00
}
2017-09-30 15:06:44 +00:00
off = int32 ( ftab . SetUint32 ( ctxt . Arch , int64 ( off ) , args ) )
2015-02-27 22:57:28 -05:00
2018-09-11 15:14:28 -07:00
// deferreturn
2020-04-07 17:08:00 -04:00
deferreturn := state . computeDeferReturn ( & ctxt . Target , s )
2018-09-11 15:14:28 -07:00
off = int32 ( ftab . SetUint32 ( ctxt . Arch , int64 ( off ) , deferreturn ) )
2015-02-27 22:57:28 -05:00
2020-04-07 17:08:00 -04:00
if fi . Valid ( ) {
pcsp = sym . Pcdata { P : fi . Pcsp ( ) }
pcfile = sym . Pcdata { P : fi . Pcfile ( ) }
pcline = sym . Pcdata { P : fi . Pcline ( ) }
state . renumberfiles ( ctxt , fi , & pcfile )
2015-02-27 22:57:28 -05:00
if false {
// Sanity check the new numbering
2019-07-30 17:28:29 -04:00
it := obj . NewPCIter ( uint32 ( ctxt . Arch . MinLC ) )
2020-04-07 17:08:00 -04:00
for it . Init ( pcfile . P ) ; ! it . Done ; it . Next ( ) {
if it . Value < 1 || it . Value > int32 ( len ( state . numberedFiles ) ) {
ctxt . Errorf ( s , "bad file number in pcfile: %d not in range [1, %d]\n" , it . Value , len ( state . numberedFiles ) )
2015-04-09 07:37:17 -04:00
errorexit ( )
2015-02-27 22:57:28 -05:00
}
}
}
}
2020-04-07 17:08:00 -04:00
if fi . Valid ( ) && fi . NumInlTree ( ) > 0 {
2020-04-10 13:07:08 -04:00
its := state . genInlTreeSym ( fi , ctxt . Arch )
2020-04-07 17:08:00 -04:00
funcdata [ objabi . FUNCDATA_InlTree ] = its
pcdata [ objabi . PCDATA_InlTreeIndex ] = sym . Pcdata { P : fi . Pcinline ( ) }
cmd/compile,link: generate PC-value tables with inlining information
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).
To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.
Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.
To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.
For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree. These are
written to an object file, which is read by the linker. The linker
re-encodes these tables compactly by deduplicating function names and
file names.
This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:
section old bytes new bytes delta
.text 3.49M ± 0% 3.49M ± 0% +0.06%
.rodata 1.12M ± 0% 1.21M ± 0% +8.21%
.gopclntab 1.50M ± 0% 1.68M ± 0% +11.89%
.debug_line 338k ± 0% 435k ± 0% +28.78%
Total 9.21M ± 0% 9.58M ± 0% +4.01%
Updates #19348.
Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
2017-02-17 12:28:05 -05:00
}
2015-02-27 22:57:28 -05:00
// pcdata
2020-04-07 17:08:00 -04:00
off = writepctab ( off , pcsp . P )
off = writepctab ( off , pcfile . P )
off = writepctab ( off , pcline . P )
off = int32 ( ftab . SetUint32 ( ctxt . Arch , int64 ( off ) , uint32 ( len ( pcdata ) ) ) )
2018-09-11 15:14:28 -07:00
// funcID uint8
2018-12-04 07:58:18 -08:00
var file string
2020-04-07 17:08:00 -04:00
if fi . Valid ( ) && fi . NumFile ( ) > 0 {
filesymname := ldr . SymName ( fi . File ( 0 ) )
file = filesymname [ len ( src . FileSymPrefix ) : ]
2018-09-11 15:14:28 -07:00
}
2020-04-07 17:08:00 -04:00
funcID := objabi . GetFuncID ( sn , file )
2018-12-04 07:58:18 -08:00
2018-09-11 15:14:28 -07:00
off = int32 ( ftab . SetUint8 ( ctxt . Arch , int64 ( off ) , uint8 ( funcID ) ) )
// unused
off += 2
// nfuncdata must be the final entry.
2020-04-07 17:08:00 -04:00
off = int32 ( ftab . SetUint8 ( ctxt . Arch , int64 ( off ) , uint8 ( len ( funcdata ) ) ) )
for i := range pcdata {
off = writepctab ( off , pcdata [ i ] . P )
2015-02-27 22:57:28 -05:00
}
// funcdata, must be pointer-aligned and we're only int32-aligned.
// Missing funcdata will be 0 (nil pointer).
2020-04-07 17:08:00 -04:00
if len ( funcdata ) > 0 {
2017-09-30 21:10:49 +00:00
if off & int32 ( ctxt . Arch . PtrSize - 1 ) != 0 {
2015-02-27 22:57:28 -05:00
off += 4
}
2020-04-07 17:08:00 -04:00
for i := range funcdata {
2019-04-15 09:25:23 -07:00
dataoff := int64 ( off ) + int64 ( ctxt . Arch . PtrSize ) * int64 ( i )
2020-04-07 17:08:00 -04:00
if funcdata [ i ] == 0 {
ftab . SetUint ( ctxt . Arch , dataoff , uint64 ( funcdataoff [ i ] ) )
2019-04-15 09:25:23 -07:00
continue
2015-02-27 22:57:28 -05:00
}
2019-04-15 09:25:23 -07:00
// TODO: Dedup.
2020-04-07 17:08:00 -04:00
funcdataBytes += int64 ( len ( ldr . Data ( funcdata [ i ] ) ) )
2020-04-15 22:23:41 -04:00
setAddr ( ftab , ctxt . Arch , dataoff , funcdata [ i ] , funcdataoff [ i ] )
2015-02-27 22:57:28 -05:00
}
2020-04-07 17:08:00 -04:00
off += int32 ( len ( funcdata ) ) * int32 ( ctxt . Arch . PtrSize )
2015-02-27 22:57:28 -05:00
}
if off != end {
2020-04-07 17:08:00 -04:00
ctxt . Errorf ( s , "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)" , funcstart , off , end , len ( pcdata ) , len ( funcdata ) , ctxt . Arch . PtrSize )
2015-04-09 07:37:17 -04:00
errorexit ( )
2015-02-27 22:57:28 -05:00
}
nfunc ++
}
2020-05-15 18:35:05 -04:00
last := ctxt . Textp [ len ( ctxt . Textp ) - 1 ]
2020-04-20 18:42:35 -04:00
pclntabLastFunc = last
2015-02-27 22:57:28 -05:00
// Final entry of table is just end pc.
2020-04-15 22:23:41 -04:00
setAddr ( ftab , ctxt . Arch , 8 + int64 ( ctxt . Arch . PtrSize ) + int64 ( nfunc ) * 2 * int64 ( ctxt . Arch . PtrSize ) , last , ldr . SymSize ( last ) )
2015-02-27 22:57:28 -05:00
// Start file table.
2020-04-07 17:08:00 -04:00
dSize := len ( ftab . Data ( ) )
start := int32 ( dSize )
start += int32 ( - dSize ) & ( int32 ( ctxt . Arch . PtrSize ) - 1 )
2015-03-16 11:53:08 +13:00
pclntabFiletabOffset = start
2017-09-30 15:06:44 +00:00
ftab . SetUint32 ( ctxt . Arch , 8 + int64 ( ctxt . Arch . PtrSize ) + int64 ( nfunc ) * 2 * int64 ( ctxt . Arch . PtrSize ) + int64 ( ctxt . Arch . PtrSize ) , uint32 ( start ) )
2015-02-27 22:57:28 -05:00
2020-04-07 17:08:00 -04:00
nf := len ( state . numberedFiles )
ftab . Grow ( int64 ( start ) + int64 ( ( nf + 1 ) * 4 ) )
ftab . SetUint32 ( ctxt . Arch , int64 ( start ) , uint32 ( nf + 1 ) )
for i := nf ; i > 0 ; i -- {
path := state . filepaths [ i ]
val := int64 ( i )
ftab . SetUint32 ( ctxt . Arch , int64 ( start ) + val * 4 , uint32 ( state . ftabaddstring ( ftab , path ) ) )
2015-02-27 22:57:28 -05:00
}
2020-04-07 17:08:00 -04:00
ftab . SetSize ( int64 ( len ( ftab . Data ( ) ) ) )
ctxt . NumFilesyms = len ( state . numberedFiles )
2015-02-27 22:57:28 -05:00
2016-08-21 18:25:28 -04:00
if ctxt . Debugvlog != 0 {
2020-04-07 17:08:00 -04:00
ctxt . Logf ( "pclntab=%d bytes, funcdata total %d bytes\n" , ftab . Size ( ) , funcdataBytes )
2015-02-27 22:57:28 -05:00
}
2020-04-10 10:30:27 -04:00
return state . container
2015-02-27 22:57:28 -05:00
}
cmd/link: set runtime.GOROOT default during link
Suppose you build the Go toolchain in directory A,
move the whole thing to directory B, and then use
it from B to build a new program hello.exe, and then
run hello.exe, and hello.exe crashes with a stack
trace into the standard library.
Long ago, you'd have seen hello.exe print file names
in the A directory tree, even though the files had moved
to the B directory tree. About two years ago we changed
the compiler to write down these files with the name
"$GOROOT" (that literal string) instead of A, so that the
final link from B could replace "$GOROOT" with B,
so that hello.exe's crash would show the correct source
file paths in the stack trace. (golang.org/cl/18200)
Now suppose that you do the same thing but hello.exe
doesn't crash: it prints fmt.Println(runtime.GOROOT()).
And you run hello.exe after clearing $GOROOT from the
environment.
Long ago, you'd have seen hello.exe print A instead of B.
Before this CL, you'd still see hello.exe print A instead of B.
This case is the one instance where a moved toolchain
still divulges its origin. Not anymore. After this CL, hello.exe
will print B, because the linker sets runtime/internal/sys.DefaultGoroot
with the effective GOROOT from link time.
This makes the default result of runtime.GOROOT once again
match the file names recorded in the binary, after two years
of divergence.
With that cleared up, we can reintroduce GOROOT into the
link action ID and also reenable TestExecutableGOROOT/RelocatedExe.
When $GOROOT_FINAL is set during link, it is used
in preference to $GOROOT, as always, but it was easier
to explain the behavior above without introducing that
complication.
Fixes #22155.
Fixes #20284.
Fixes #22475.
Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016
Reviewed-on: https://go-review.googlesource.com/86835
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 11:59:29 -05:00
func gorootFinal ( ) string {
root := objabi . GOROOT
if final := os . Getenv ( "GOROOT_FINAL" ) ; final != "" {
root = final
}
return root
}
2015-12-29 10:16:40 -05:00
func expandGoroot ( s string ) string {
const n = len ( "$GOROOT" )
if len ( s ) >= n + 1 && s [ : n ] == "$GOROOT" && ( s [ n ] == '/' || s [ n ] == '\\' ) {
cmd/link: set runtime.GOROOT default during link
Suppose you build the Go toolchain in directory A,
move the whole thing to directory B, and then use
it from B to build a new program hello.exe, and then
run hello.exe, and hello.exe crashes with a stack
trace into the standard library.
Long ago, you'd have seen hello.exe print file names
in the A directory tree, even though the files had moved
to the B directory tree. About two years ago we changed
the compiler to write down these files with the name
"$GOROOT" (that literal string) instead of A, so that the
final link from B could replace "$GOROOT" with B,
so that hello.exe's crash would show the correct source
file paths in the stack trace. (golang.org/cl/18200)
Now suppose that you do the same thing but hello.exe
doesn't crash: it prints fmt.Println(runtime.GOROOT()).
And you run hello.exe after clearing $GOROOT from the
environment.
Long ago, you'd have seen hello.exe print A instead of B.
Before this CL, you'd still see hello.exe print A instead of B.
This case is the one instance where a moved toolchain
still divulges its origin. Not anymore. After this CL, hello.exe
will print B, because the linker sets runtime/internal/sys.DefaultGoroot
with the effective GOROOT from link time.
This makes the default result of runtime.GOROOT once again
match the file names recorded in the binary, after two years
of divergence.
With that cleared up, we can reintroduce GOROOT into the
link action ID and also reenable TestExecutableGOROOT/RelocatedExe.
When $GOROOT_FINAL is set during link, it is used
in preference to $GOROOT, as always, but it was easier
to explain the behavior above without introducing that
complication.
Fixes #22155.
Fixes #20284.
Fixes #22475.
Change-Id: Ifdaeb77fd4678fdb337cf59ee25b2cd873ec1016
Reviewed-on: https://go-review.googlesource.com/86835
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-01-08 11:59:29 -05:00
return filepath . ToSlash ( filepath . Join ( gorootFinal ( ) , s [ n : ] ) )
2015-12-29 10:16:40 -05:00
}
return s
}
2015-02-27 22:57:28 -05:00
const (
BUCKETSIZE = 256 * MINFUNC
SUBBUCKETS = 16
SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
NOIDX = 0x7fffffff
)
// findfunctab generates a lookup table to quickly find the containing
2016-03-01 23:21:55 +00:00
// function for a pc. See src/runtime/symtab.go:findfunc for details.
2020-04-10 10:30:27 -04:00
// 'container' is a bitmap indexed by global symbol holding whether
// a given text symbols is a container (outer sym).
func ( ctxt * Link ) findfunctab ( container loader . Bitmap ) {
ldr := ctxt . loader
2015-02-27 22:57:28 -05:00
// find min and max address
2020-05-15 18:35:05 -04:00
min := ldr . SymValue ( ctxt . Textp [ 0 ] )
lastp := ctxt . Textp [ len ( ctxt . Textp ) - 1 ]
2020-04-10 10:30:27 -04:00
max := ldr . SymValue ( lastp ) + ldr . SymSize ( lastp )
2015-02-27 22:57:28 -05:00
// for each subbucket, compute the minimum of all symbol indexes
// that map to that subbucket.
2015-03-02 12:35:15 -05:00
n := int32 ( ( max - min + SUBBUCKETSIZE - 1 ) / SUBBUCKETSIZE )
2015-02-27 22:57:28 -05:00
2020-06-22 11:11:12 -04:00
nbuckets := int32 ( ( max - min + BUCKETSIZE - 1 ) / BUCKETSIZE )
size := 4 * int64 ( nbuckets ) + int64 ( n )
writeFindFuncTab := func ( _ * Link , s loader . Sym ) {
t := ldr . MakeSymbolUpdater ( s )
indexes := make ( [ ] int32 , n )
for i := int32 ( 0 ) ; i < n ; i ++ {
indexes [ i ] = NOIDX
2016-04-19 14:02:21 -04:00
}
2020-06-22 11:11:12 -04:00
idx := int32 ( 0 )
for i , s := range ctxt . Textp {
if ! emitPcln ( ctxt , s , container ) {
continue
}
p := ldr . SymValue ( s )
var e loader . Sym
2016-04-19 14:02:21 -04:00
i ++
2020-06-22 11:11:12 -04:00
if i < len ( ctxt . Textp ) {
e = ctxt . Textp [ i ]
}
for e != 0 && ! emitPcln ( ctxt , e , container ) && i < len ( ctxt . Textp ) {
e = ctxt . Textp [ i ]
i ++
}
q := max
if e != 0 {
q = ldr . SymValue ( e )
}
//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
for ; p < q ; p += SUBBUCKETSIZE {
i = int ( ( p - min ) / SUBBUCKETSIZE )
if indexes [ i ] > idx {
indexes [ i ] = idx
}
}
2015-02-27 22:57:28 -05:00
2020-06-22 11:11:12 -04:00
i = int ( ( q - 1 - min ) / SUBBUCKETSIZE )
2015-02-27 22:57:28 -05:00
if indexes [ i ] > idx {
indexes [ i ] = idx
}
2020-06-22 11:11:12 -04:00
idx ++
2015-02-27 22:57:28 -05:00
}
2020-06-22 11:11:12 -04:00
// fill in table
for i := int32 ( 0 ) ; i < nbuckets ; i ++ {
base := indexes [ i * SUBBUCKETS ]
if base == NOIDX {
2016-09-17 09:39:33 -04:00
Errorf ( nil , "hole in findfunctab" )
2015-02-27 22:57:28 -05:00
}
2020-06-22 11:11:12 -04:00
t . SetUint32 ( ctxt . Arch , int64 ( i ) * ( 4 + SUBBUCKETS ) , uint32 ( base ) )
for j := int32 ( 0 ) ; j < SUBBUCKETS && i * SUBBUCKETS + j < n ; j ++ {
idx = indexes [ i * SUBBUCKETS + j ]
if idx == NOIDX {
Errorf ( nil , "hole in findfunctab" )
}
if idx - base >= 256 {
Errorf ( nil , "too many functions in a findfunc bucket! %d/%d %d %d" , i , nbuckets , j , idx - base )
}
2015-02-27 22:57:28 -05:00
2020-06-22 11:11:12 -04:00
t . SetUint8 ( ctxt . Arch , int64 ( i ) * ( 4 + SUBBUCKETS ) + 4 + int64 ( j ) , uint8 ( idx - base ) )
}
2015-02-27 22:57:28 -05:00
}
}
2020-06-22 11:11:12 -04:00
s := ctxt . createGeneratorSymbol ( "runtime.findfunctab" , 0 , sym . SRODATA , size , writeFindFuncTab )
ldr . SetAttrReachable ( s , true )
ldr . SetAttrLocal ( s , true )
2015-02-27 22:57:28 -05:00
}