go/src/cmd/compile/internal/gc/pgen.go

475 lines
11 KiB
Go
Raw Normal View History

// Copyright 2011 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 gc
import (
"cmd/compile/internal/ssa"
"cmd/internal/obj"
"cmd/internal/src"
"cmd/internal/sys"
"fmt"
"sort"
"strings"
)
// "Portable" code generation.
var makefuncdatasym_nsym int
func makefuncdatasym(nameprefix string, funcdatakind int64) *Sym {
sym := lookupN(nameprefix, makefuncdatasym_nsym)
makefuncdatasym_nsym++
pnod := newname(sym)
pnod.Class = PEXTERN
p := Gins(obj.AFUNCDATA, nil, pnod)
Addrconst(&p.From, funcdatakind)
return sym
}
// gvardef inserts a VARDEF for n into the instruction stream.
// VARDEF is an annotation for the liveness analysis, marking a place
// where a complete initialization (definition) of a variable begins.
// Since the liveness analysis can see initialization of single-word
// variables quite easy, gvardef is usually only called for multi-word
// or 'fat' variables, those satisfying isfat(n->type).
// However, gvardef is also called when a non-fat variable is initialized
// via a block move; the only time this happens is when you have
// return f()
// for a function with multiple return values exactly matching the return
// types of the current function.
//
// A 'VARDEF x' annotation in the instruction stream tells the liveness
// analysis to behave as though the variable x is being initialized at that
// point in the instruction stream. The VARDEF must appear before the
// actual (multi-instruction) initialization, and it must also appear after
// any uses of the previous value, if any. For example, if compiling:
//
// x = x[1:]
//
// it is important to generate code like:
//
// base, len, cap = pieces of x[1:]
// VARDEF x
// x = {base, len, cap}
//
// If instead the generated code looked like:
//
// VARDEF x
// base, len, cap = pieces of x[1:]
// x = {base, len, cap}
//
// then the liveness analysis would decide the previous value of x was
// unnecessary even though it is about to be used by the x[1:] computation.
// Similarly, if the generated code looked like:
//
// base, len, cap = pieces of x[1:]
// x = {base, len, cap}
// VARDEF x
//
// then the liveness analysis will not preserve the new value of x, because
// the VARDEF appears to have "overwritten" it.
//
// VARDEF is a bit of a kludge to work around the fact that the instruction
// stream is working on single-word values but the liveness analysis
// wants to work on individual variables, which might be multi-word
// aggregates. It might make sense at some point to look into letting
// the liveness analysis work on single-word values as well, although
// there are complications around interface values, slices, and strings,
// all of which cannot be treated as individual words.
//
// VARKILL is the opposite of VARDEF: it marks a value as no longer needed,
// even if its address has been taken. That is, a VARKILL annotation asserts
// that its argument is certainly dead, for use when the liveness analysis
// would not otherwise be able to deduce that fact.
func gvardefx(n *Node, as obj.As) {
if n == nil {
Fatalf("gvardef nil")
}
if n.Op != ONAME {
yyerror("gvardef %#v; %v", n.Op, n)
return
}
switch n.Class {
case PAUTO, PPARAM, PPARAMOUT:
if !n.Used {
Prog(obj.ANOP)
return
}
cmd/compile: recognize Syscall-like functions for liveness analysis Consider this code: func f(*int) func g() { p := new(int) f(p) } where f is an assembly function. In general liveness analysis assumes that during the call to f, p is dead in this frame. If f has retained p, p will be found alive in f's frame and keep the new(int) from being garbage collected. This is all correct and works. We use the Go func declaration for f to give the assembly function liveness information (the arguments are assumed live for the entire call). Now consider this code: func h1() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) } Here syscall.Syscall is taking the place of f, but because its arguments are uintptr, the liveness analysis and the garbage collector ignore them. Since p is no longer live in h once the call starts, if the garbage collector scans the stack while the system call is blocked, it will find no reference to the new(int) and reclaim it. If the kernel is going to write to *p once the call finishes, reclaiming the memory is a mistake. We can't change the arguments or the liveness information for syscall.Syscall itself, both for compatibility and because sometimes the arguments really are integers, and the garbage collector will get quite upset if it finds an integer where it expects a pointer. The problem is that these arguments are fundamentally untyped. The solution we have taken in the syscall package's wrappers in past releases is to insert a call to a dummy function named "use", to make it look like the argument is live during the call to syscall.Syscall: func h2() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) use(unsafe.Pointer(p)) } Keeping p alive during the call means that if the garbage collector scans the stack during the system call now, it will find the reference to p. Unfortunately, this approach is not available to users outside syscall, because 'use' is unexported, and people also have to realize they need to use it and do so. There is much existing code using syscall.Syscall without a 'use'-like function. That code will fail very occasionally in mysterious ways (see #13372). This CL fixes all that existing code by making the compiler do the right thing automatically, without any code modifications. That is, it takes h1 above, which is incorrect code today, and makes it correct code. Specifically, if the compiler sees a foreign func definition (one without a body) that has uintptr arguments, it marks those arguments as "unsafe uintptrs". If it later sees the function being called with uintptr(unsafe.Pointer(x)) as an argument, it arranges to mark x as having escaped, and it makes sure to hold x in a live temporary variable until the call returns, so that the garbage collector cannot reclaim whatever heap memory x points to. For now I am leaving the explicit calls to use in package syscall, but they can be removed early in a future cycle (likely Go 1.7). The rule has no effect on escape analysis, only on liveness analysis. Fixes #13372. Change-Id: I2addb83f70d08db08c64d394f9d06ff0a063c500 Reviewed-on: https://go-review.googlesource.com/18584 Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-01-13 00:46:28 -05:00
if as == obj.AVARLIVE {
Gins(as, n, nil)
cmd/compile: recognize Syscall-like functions for liveness analysis Consider this code: func f(*int) func g() { p := new(int) f(p) } where f is an assembly function. In general liveness analysis assumes that during the call to f, p is dead in this frame. If f has retained p, p will be found alive in f's frame and keep the new(int) from being garbage collected. This is all correct and works. We use the Go func declaration for f to give the assembly function liveness information (the arguments are assumed live for the entire call). Now consider this code: func h1() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) } Here syscall.Syscall is taking the place of f, but because its arguments are uintptr, the liveness analysis and the garbage collector ignore them. Since p is no longer live in h once the call starts, if the garbage collector scans the stack while the system call is blocked, it will find no reference to the new(int) and reclaim it. If the kernel is going to write to *p once the call finishes, reclaiming the memory is a mistake. We can't change the arguments or the liveness information for syscall.Syscall itself, both for compatibility and because sometimes the arguments really are integers, and the garbage collector will get quite upset if it finds an integer where it expects a pointer. The problem is that these arguments are fundamentally untyped. The solution we have taken in the syscall package's wrappers in past releases is to insert a call to a dummy function named "use", to make it look like the argument is live during the call to syscall.Syscall: func h2() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) use(unsafe.Pointer(p)) } Keeping p alive during the call means that if the garbage collector scans the stack during the system call now, it will find the reference to p. Unfortunately, this approach is not available to users outside syscall, because 'use' is unexported, and people also have to realize they need to use it and do so. There is much existing code using syscall.Syscall without a 'use'-like function. That code will fail very occasionally in mysterious ways (see #13372). This CL fixes all that existing code by making the compiler do the right thing automatically, without any code modifications. That is, it takes h1 above, which is incorrect code today, and makes it correct code. Specifically, if the compiler sees a foreign func definition (one without a body) that has uintptr arguments, it marks those arguments as "unsafe uintptrs". If it later sees the function being called with uintptr(unsafe.Pointer(x)) as an argument, it arranges to mark x as having escaped, and it makes sure to hold x in a live temporary variable until the call returns, so that the garbage collector cannot reclaim whatever heap memory x points to. For now I am leaving the explicit calls to use in package syscall, but they can be removed early in a future cycle (likely Go 1.7). The rule has no effect on escape analysis, only on liveness analysis. Fixes #13372. Change-Id: I2addb83f70d08db08c64d394f9d06ff0a063c500 Reviewed-on: https://go-review.googlesource.com/18584 Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-01-13 00:46:28 -05:00
} else {
Gins(as, nil, n)
cmd/compile: recognize Syscall-like functions for liveness analysis Consider this code: func f(*int) func g() { p := new(int) f(p) } where f is an assembly function. In general liveness analysis assumes that during the call to f, p is dead in this frame. If f has retained p, p will be found alive in f's frame and keep the new(int) from being garbage collected. This is all correct and works. We use the Go func declaration for f to give the assembly function liveness information (the arguments are assumed live for the entire call). Now consider this code: func h1() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) } Here syscall.Syscall is taking the place of f, but because its arguments are uintptr, the liveness analysis and the garbage collector ignore them. Since p is no longer live in h once the call starts, if the garbage collector scans the stack while the system call is blocked, it will find no reference to the new(int) and reclaim it. If the kernel is going to write to *p once the call finishes, reclaiming the memory is a mistake. We can't change the arguments or the liveness information for syscall.Syscall itself, both for compatibility and because sometimes the arguments really are integers, and the garbage collector will get quite upset if it finds an integer where it expects a pointer. The problem is that these arguments are fundamentally untyped. The solution we have taken in the syscall package's wrappers in past releases is to insert a call to a dummy function named "use", to make it look like the argument is live during the call to syscall.Syscall: func h2() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) use(unsafe.Pointer(p)) } Keeping p alive during the call means that if the garbage collector scans the stack during the system call now, it will find the reference to p. Unfortunately, this approach is not available to users outside syscall, because 'use' is unexported, and people also have to realize they need to use it and do so. There is much existing code using syscall.Syscall without a 'use'-like function. That code will fail very occasionally in mysterious ways (see #13372). This CL fixes all that existing code by making the compiler do the right thing automatically, without any code modifications. That is, it takes h1 above, which is incorrect code today, and makes it correct code. Specifically, if the compiler sees a foreign func definition (one without a body) that has uintptr arguments, it marks those arguments as "unsafe uintptrs". If it later sees the function being called with uintptr(unsafe.Pointer(x)) as an argument, it arranges to mark x as having escaped, and it makes sure to hold x in a live temporary variable until the call returns, so that the garbage collector cannot reclaim whatever heap memory x points to. For now I am leaving the explicit calls to use in package syscall, but they can be removed early in a future cycle (likely Go 1.7). The rule has no effect on escape analysis, only on liveness analysis. Fixes #13372. Change-Id: I2addb83f70d08db08c64d394f9d06ff0a063c500 Reviewed-on: https://go-review.googlesource.com/18584 Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-01-13 00:46:28 -05:00
}
}
}
func Gvardef(n *Node) {
gvardefx(n, obj.AVARDEF)
}
func Gvarkill(n *Node) {
gvardefx(n, obj.AVARKILL)
}
func Gvarlive(n *Node) {
cmd/compile: recognize Syscall-like functions for liveness analysis Consider this code: func f(*int) func g() { p := new(int) f(p) } where f is an assembly function. In general liveness analysis assumes that during the call to f, p is dead in this frame. If f has retained p, p will be found alive in f's frame and keep the new(int) from being garbage collected. This is all correct and works. We use the Go func declaration for f to give the assembly function liveness information (the arguments are assumed live for the entire call). Now consider this code: func h1() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) } Here syscall.Syscall is taking the place of f, but because its arguments are uintptr, the liveness analysis and the garbage collector ignore them. Since p is no longer live in h once the call starts, if the garbage collector scans the stack while the system call is blocked, it will find no reference to the new(int) and reclaim it. If the kernel is going to write to *p once the call finishes, reclaiming the memory is a mistake. We can't change the arguments or the liveness information for syscall.Syscall itself, both for compatibility and because sometimes the arguments really are integers, and the garbage collector will get quite upset if it finds an integer where it expects a pointer. The problem is that these arguments are fundamentally untyped. The solution we have taken in the syscall package's wrappers in past releases is to insert a call to a dummy function named "use", to make it look like the argument is live during the call to syscall.Syscall: func h2() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) use(unsafe.Pointer(p)) } Keeping p alive during the call means that if the garbage collector scans the stack during the system call now, it will find the reference to p. Unfortunately, this approach is not available to users outside syscall, because 'use' is unexported, and people also have to realize they need to use it and do so. There is much existing code using syscall.Syscall without a 'use'-like function. That code will fail very occasionally in mysterious ways (see #13372). This CL fixes all that existing code by making the compiler do the right thing automatically, without any code modifications. That is, it takes h1 above, which is incorrect code today, and makes it correct code. Specifically, if the compiler sees a foreign func definition (one without a body) that has uintptr arguments, it marks those arguments as "unsafe uintptrs". If it later sees the function being called with uintptr(unsafe.Pointer(x)) as an argument, it arranges to mark x as having escaped, and it makes sure to hold x in a live temporary variable until the call returns, so that the garbage collector cannot reclaim whatever heap memory x points to. For now I am leaving the explicit calls to use in package syscall, but they can be removed early in a future cycle (likely Go 1.7). The rule has no effect on escape analysis, only on liveness analysis. Fixes #13372. Change-Id: I2addb83f70d08db08c64d394f9d06ff0a063c500 Reviewed-on: https://go-review.googlesource.com/18584 Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-01-13 00:46:28 -05:00
gvardefx(n, obj.AVARLIVE)
}
func removevardef(firstp *obj.Prog) {
for p := firstp; p != nil; p = p.Link {
cmd/compile: recognize Syscall-like functions for liveness analysis Consider this code: func f(*int) func g() { p := new(int) f(p) } where f is an assembly function. In general liveness analysis assumes that during the call to f, p is dead in this frame. If f has retained p, p will be found alive in f's frame and keep the new(int) from being garbage collected. This is all correct and works. We use the Go func declaration for f to give the assembly function liveness information (the arguments are assumed live for the entire call). Now consider this code: func h1() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) } Here syscall.Syscall is taking the place of f, but because its arguments are uintptr, the liveness analysis and the garbage collector ignore them. Since p is no longer live in h once the call starts, if the garbage collector scans the stack while the system call is blocked, it will find no reference to the new(int) and reclaim it. If the kernel is going to write to *p once the call finishes, reclaiming the memory is a mistake. We can't change the arguments or the liveness information for syscall.Syscall itself, both for compatibility and because sometimes the arguments really are integers, and the garbage collector will get quite upset if it finds an integer where it expects a pointer. The problem is that these arguments are fundamentally untyped. The solution we have taken in the syscall package's wrappers in past releases is to insert a call to a dummy function named "use", to make it look like the argument is live during the call to syscall.Syscall: func h2() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) use(unsafe.Pointer(p)) } Keeping p alive during the call means that if the garbage collector scans the stack during the system call now, it will find the reference to p. Unfortunately, this approach is not available to users outside syscall, because 'use' is unexported, and people also have to realize they need to use it and do so. There is much existing code using syscall.Syscall without a 'use'-like function. That code will fail very occasionally in mysterious ways (see #13372). This CL fixes all that existing code by making the compiler do the right thing automatically, without any code modifications. That is, it takes h1 above, which is incorrect code today, and makes it correct code. Specifically, if the compiler sees a foreign func definition (one without a body) that has uintptr arguments, it marks those arguments as "unsafe uintptrs". If it later sees the function being called with uintptr(unsafe.Pointer(x)) as an argument, it arranges to mark x as having escaped, and it makes sure to hold x in a live temporary variable until the call returns, so that the garbage collector cannot reclaim whatever heap memory x points to. For now I am leaving the explicit calls to use in package syscall, but they can be removed early in a future cycle (likely Go 1.7). The rule has no effect on escape analysis, only on liveness analysis. Fixes #13372. Change-Id: I2addb83f70d08db08c64d394f9d06ff0a063c500 Reviewed-on: https://go-review.googlesource.com/18584 Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-01-13 00:46:28 -05:00
for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
p.Link = p.Link.Link
}
if p.To.Type == obj.TYPE_BRANCH {
cmd/compile: recognize Syscall-like functions for liveness analysis Consider this code: func f(*int) func g() { p := new(int) f(p) } where f is an assembly function. In general liveness analysis assumes that during the call to f, p is dead in this frame. If f has retained p, p will be found alive in f's frame and keep the new(int) from being garbage collected. This is all correct and works. We use the Go func declaration for f to give the assembly function liveness information (the arguments are assumed live for the entire call). Now consider this code: func h1() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) } Here syscall.Syscall is taking the place of f, but because its arguments are uintptr, the liveness analysis and the garbage collector ignore them. Since p is no longer live in h once the call starts, if the garbage collector scans the stack while the system call is blocked, it will find no reference to the new(int) and reclaim it. If the kernel is going to write to *p once the call finishes, reclaiming the memory is a mistake. We can't change the arguments or the liveness information for syscall.Syscall itself, both for compatibility and because sometimes the arguments really are integers, and the garbage collector will get quite upset if it finds an integer where it expects a pointer. The problem is that these arguments are fundamentally untyped. The solution we have taken in the syscall package's wrappers in past releases is to insert a call to a dummy function named "use", to make it look like the argument is live during the call to syscall.Syscall: func h2() { p := new(int) syscall.Syscall(1, 2, 3, uintptr(unsafe.Pointer(p))) use(unsafe.Pointer(p)) } Keeping p alive during the call means that if the garbage collector scans the stack during the system call now, it will find the reference to p. Unfortunately, this approach is not available to users outside syscall, because 'use' is unexported, and people also have to realize they need to use it and do so. There is much existing code using syscall.Syscall without a 'use'-like function. That code will fail very occasionally in mysterious ways (see #13372). This CL fixes all that existing code by making the compiler do the right thing automatically, without any code modifications. That is, it takes h1 above, which is incorrect code today, and makes it correct code. Specifically, if the compiler sees a foreign func definition (one without a body) that has uintptr arguments, it marks those arguments as "unsafe uintptrs". If it later sees the function being called with uintptr(unsafe.Pointer(x)) as an argument, it arranges to mark x as having escaped, and it makes sure to hold x in a live temporary variable until the call returns, so that the garbage collector cannot reclaim whatever heap memory x points to. For now I am leaving the explicit calls to use in package syscall, but they can be removed early in a future cycle (likely Go 1.7). The rule has no effect on escape analysis, only on liveness analysis. Fixes #13372. Change-Id: I2addb83f70d08db08c64d394f9d06ff0a063c500 Reviewed-on: https://go-review.googlesource.com/18584 Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-01-13 00:46:28 -05:00
for p.To.Val.(*obj.Prog) != nil && (p.To.Val.(*obj.Prog).As == obj.AVARDEF || p.To.Val.(*obj.Prog).As == obj.AVARKILL || p.To.Val.(*obj.Prog).As == obj.AVARLIVE) {
p.To.Val = p.To.Val.(*obj.Prog).Link
}
}
}
}
func emitptrargsmap() {
if Curfn.Func.Nname.Sym.Name == "_" {
return
}
sym := lookup(fmt.Sprintf("%s.args_stackmap", Curfn.Func.Nname.Sym.Name))
nptr := int(Curfn.Type.ArgWidth() / int64(Widthptr))
bv := bvalloc(int32(nptr) * 2)
nbitmap := 1
if Curfn.Type.Results().NumFields() > 0 {
nbitmap = 2
}
off := duint32(sym, 0, uint32(nbitmap))
off = duint32(sym, off, uint32(bv.n))
var xoffset int64
if Curfn.IsMethod() {
xoffset = 0
onebitwalktype1(Curfn.Type.Recvs(), &xoffset, bv)
}
if Curfn.Type.Params().NumFields() > 0 {
xoffset = 0
onebitwalktype1(Curfn.Type.Params(), &xoffset, bv)
}
off = dbvec(sym, off, bv)
if Curfn.Type.Results().NumFields() > 0 {
xoffset = 0
onebitwalktype1(Curfn.Type.Results(), &xoffset, bv)
off = dbvec(sym, off, bv)
}
ggloblsym(sym, int32(off), obj.RODATA|obj.LOCAL)
}
// cmpstackvarlt reports whether the stack variable a sorts before b.
//
// Sort the list of stack variables. Autos after anything else,
// within autos, unused after used, within used, things with
// pointers first, zeroed things first, and then decreasing size.
// Because autos are laid out in decreasing addresses
// on the stack, pointers first, zeroed things first and decreasing size
// really means, in memory, things with pointers needing zeroing at
// the top of the stack and increasing in size.
// Non-autos sort on offset.
func cmpstackvarlt(a, b *Node) bool {
if (a.Class == PAUTO) != (b.Class == PAUTO) {
return b.Class == PAUTO
}
if a.Class != PAUTO {
return a.Xoffset < b.Xoffset
}
if a.Used != b.Used {
return a.Used
}
ap := haspointers(a.Type)
bp := haspointers(b.Type)
if ap != bp {
return ap
}
ap = a.Name.Needzero
bp = b.Name.Needzero
if ap != bp {
return ap
}
if a.Type.Width != b.Type.Width {
return a.Type.Width > b.Type.Width
}
return a.Sym.Name < b.Sym.Name
}
// byStackvar implements sort.Interface for []*Node using cmpstackvarlt.
type byStackVar []*Node
func (s byStackVar) Len() int { return len(s) }
func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
var scratchFpMem *Node
func (s *ssaExport) AllocFrame(f *ssa.Func) {
Stksize = 0
stkptrsize = 0
// Mark the PAUTO's unused.
for _, ln := range Curfn.Func.Dcl {
if ln.Class == PAUTO {
ln.Used = false
}
}
for _, l := range f.RegAlloc {
if ls, ok := l.(ssa.LocalSlot); ok {
ls.N.(*Node).Used = true
}
}
scratchUsed := false
for _, b := range f.Blocks {
for _, v := range b.Values {
switch a := v.Aux.(type) {
case *ssa.ArgSymbol:
a.Node.(*Node).Used = true
case *ssa.AutoSymbol:
a.Node.(*Node).Used = true
}
if !scratchUsed {
scratchUsed = v.Op.UsesScratch()
}
}
}
if f.Config.NeedsFpScratch {
scratchFpMem = temp(Types[TUINT64])
scratchFpMem.Used = scratchUsed
}
sort.Sort(byStackVar(Curfn.Func.Dcl))
// Reassign stack offsets of the locals that are used.
for i, n := range Curfn.Func.Dcl {
if n.Op != ONAME || n.Class != PAUTO {
continue
}
if !n.Used {
Curfn.Func.Dcl = Curfn.Func.Dcl[:i]
break
}
dowidth(n.Type)
w := n.Type.Width
if w >= Thearch.MAXWIDTH || w < 0 {
Fatalf("bad width")
}
Stksize += w
Stksize = Rnd(Stksize, int64(n.Type.Align))
if haspointers(n.Type) {
stkptrsize = Stksize
}
if Thearch.LinkArch.InFamily(sys.MIPS, sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64, sys.S390X) {
Stksize = Rnd(Stksize, int64(Widthptr))
}
if Stksize >= 1<<31 {
setlineno(Curfn)
yyerror("stack frame too large (>2GB)")
}
n.Xoffset = -Stksize
}
Stksize = Rnd(Stksize, int64(Widthreg))
stkptrsize = Rnd(stkptrsize, int64(Widthreg))
}
func compile(fn *Node) {
if Newproc == nil {
Newproc = Sysfunc("newproc")
Deferproc = Sysfunc("deferproc")
Deferreturn = Sysfunc("deferreturn")
Duffcopy = Sysfunc("duffcopy")
Duffzero = Sysfunc("duffzero")
panicindex = Sysfunc("panicindex")
panicslice = Sysfunc("panicslice")
panicdivide = Sysfunc("panicdivide")
growslice = Sysfunc("growslice")
panicdottypeE = Sysfunc("panicdottypeE")
panicdottypeI = Sysfunc("panicdottypeI")
panicnildottype = Sysfunc("panicnildottype")
assertE2I = Sysfunc("assertE2I")
assertE2I2 = Sysfunc("assertE2I2")
assertI2I = Sysfunc("assertI2I")
assertI2I2 = Sysfunc("assertI2I2")
}
2016-12-15 17:17:01 -08:00
defer func(lno src.XPos) {
lineno = lno
}(setlineno(fn))
Curfn = fn
dowidth(Curfn.Type)
if fn.Nbody.Len() == 0 {
if pure_go || strings.HasPrefix(fn.Func.Nname.Sym.Name, "init.") {
yyerror("missing function body for %q", fn.Func.Nname.Sym.Name)
return
}
emitptrargsmap()
return
}
saveerrors()
cmd/compile: shrink gc.Type in half Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-04-01 20:11:30 -07:00
if Curfn.Type.FuncType().Outnamed {
// add clearing of the output parameters
for _, t := range Curfn.Type.Results().Fields().Slice() {
if t.Nname != nil {
n := nod(OAS, t.Nname, nil)
cmd/compile: reduce use of **Node parameters Escape analysis has a hard time with tree-like structures (see #13493 and #14858). This is unlikely to change. As a result, when invoking a function that accepts a **Node parameter, we usually allocate a *Node on the heap. This happens a whole lot. This CL changes functions from taking a **Node to acting more like append: It both modifies the input and returns a replacement for it. Because of the cascading nature of escape analysis, in order to get the benefits, I had to modify almost all such functions. The remaining functions are in racewalk and the backend. I would be happy to update them as well in a separate CL. This CL was created by manually updating the function signatures and the directly impacted bits of code. The callsites were then automatically updated using a bespoke script: https://gist.github.com/josharian/046b1be7aceae244de39 For ease of reviewing and future understanding, this CL is also broken down into four CLs, mailed separately, which show the manual and the automated changes separately. They are CLs 20990, 20991, 20992, and 20993. Passes toolstash -cmp. name old time/op new time/op delta Template 335ms ± 5% 324ms ± 5% -3.35% (p=0.000 n=23+24) Unicode 176ms ± 9% 165ms ± 6% -6.12% (p=0.000 n=23+24) GoTypes 1.10s ± 4% 1.07s ± 2% -2.77% (p=0.000 n=24+24) Compiler 5.31s ± 3% 5.15s ± 3% -2.95% (p=0.000 n=24+24) MakeBash 41.6s ± 1% 41.7s ± 2% ~ (p=0.586 n=23+23) name old alloc/op new alloc/op delta Template 63.3MB ± 0% 62.4MB ± 0% -1.36% (p=0.000 n=25+23) Unicode 42.4MB ± 0% 41.6MB ± 0% -1.99% (p=0.000 n=24+25) GoTypes 220MB ± 0% 217MB ± 0% -1.11% (p=0.000 n=25+25) Compiler 994MB ± 0% 973MB ± 0% -2.08% (p=0.000 n=24+25) name old allocs/op new allocs/op delta Template 681k ± 0% 574k ± 0% -15.71% (p=0.000 n=24+25) Unicode 518k ± 0% 413k ± 0% -20.34% (p=0.000 n=25+24) GoTypes 2.08M ± 0% 1.78M ± 0% -14.62% (p=0.000 n=25+25) Compiler 9.26M ± 0% 7.64M ± 0% -17.48% (p=0.000 n=25+25) name old text-bytes new text-bytes delta HelloSize 578k ± 0% 578k ± 0% ~ (all samples are equal) CmdGoSize 6.46M ± 0% 6.46M ± 0% ~ (all samples are equal) name old data-bytes new data-bytes delta HelloSize 128k ± 0% 128k ± 0% ~ (all samples are equal) CmdGoSize 281k ± 0% 281k ± 0% ~ (all samples are equal) name old exe-bytes new exe-bytes delta HelloSize 921k ± 0% 921k ± 0% ~ (all samples are equal) CmdGoSize 9.86M ± 0% 9.86M ± 0% ~ (all samples are equal) Change-Id: I277d95bd56d51c166ef7f560647aeaa092f3f475 Reviewed-on: https://go-review.googlesource.com/20959 Reviewed-by: Dave Cheney <dave@cheney.net> Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-03-20 08:03:31 -07:00
n = typecheck(n, Etop)
Curfn.Nbody.Prepend(n)
}
}
}
order(Curfn)
if nerrors != 0 {
return
}
hasdefer = false
walk(Curfn)
if nerrors != 0 {
return
}
if instrumenting {
instrument(Curfn)
}
if nerrors != 0 {
return
}
[dev.ssa] cmd/compile/ssa: separate logging, work in progress, and fatal errors The SSA implementation logs for three purposes: * debug logging * fatal errors * unimplemented features Separating these three uses lets us attempt an SSA implementation for all functions, not just _ssa functions. This turns the entire standard library into a compilation test, and makes it easy to figure out things like "how much coverage does SSA have now" and "what should we do next to get more coverage?". Functions called _ssa are still special. They log profusely by default and the output of the SSA implementation is used. For all other functions, logging is off, and the implementation is built and discarded, due to lack of support for the runtime. While we're here, fix a few minor bugs and add some extra Unimplementeds to allow all.bash to pass. As of now, SSA handles 20.79% of the functions in the standard library (689 of 3314). The top missing features are: 10.03% 2597 SSA unimplemented: zero for type error not implemented 7.79% 2016 SSA unimplemented: addr: bad op DOTPTR 7.33% 1898 SSA unimplemented: unhandled expr EQ 6.10% 1579 SSA unimplemented: unhandled expr OROR 4.91% 1271 SSA unimplemented: unhandled expr NE 4.49% 1163 SSA unimplemented: unhandled expr LROT 4.00% 1036 SSA unimplemented: unhandled expr LEN 3.56% 923 SSA unimplemented: unhandled stmt CALLFUNC 2.37% 615 SSA unimplemented: zero for type []byte not implemented 1.90% 492 SSA unimplemented: unhandled stmt CALLMETH 1.74% 450 SSA unimplemented: unhandled expr CALLINTER 1.74% 450 SSA unimplemented: unhandled expr DOT 1.71% 444 SSA unimplemented: unhandled expr ANDAND 1.65% 426 SSA unimplemented: unhandled expr CLOSUREVAR 1.54% 400 SSA unimplemented: unhandled expr CALLMETH 1.51% 390 SSA unimplemented: unhandled stmt SWITCH 1.47% 380 SSA unimplemented: unhandled expr CONV 1.33% 345 SSA unimplemented: addr: bad op * 1.30% 336 SSA unimplemented: unhandled OLITERAL 6 Change-Id: I4ca07951e276714dc13c31de28640aead17a1be7 Reviewed-on: https://go-review.googlesource.com/11160 Reviewed-by: Keith Randall <khr@golang.org>
2015-06-12 11:01:13 -07:00
// Build an SSA backend function.
ssafn := buildssa(Curfn)
if nerrors != 0 {
return
}
newplist()
setlineno(Curfn)
nam := Curfn.Func.Nname
if isblank(nam) {
nam = nil
}
ptxt := Gins(obj.ATEXT, nam, nil)
ptxt.From3 = new(obj.Addr)
if fn.Func.Dupok {
ptxt.From3.Offset |= obj.DUPOK
}
if fn.Func.Wrapper {
ptxt.From3.Offset |= obj.WRAPPER
}
if fn.Func.NoFramePointer {
ptxt.From3.Offset |= obj.NOFRAME
}
if fn.Func.Needctxt {
ptxt.From3.Offset |= obj.NEEDCTXT
}
if fn.Func.Pragma&Nosplit != 0 {
ptxt.From3.Offset |= obj.NOSPLIT
}
if fn.Func.ReflectMethod {
ptxt.From3.Offset |= obj.REFLECTMETHOD
}
if fn.Func.Pragma&Systemstack != 0 {
ptxt.From.Sym.Set(obj.AttrCFunc, true)
if fn.Func.Pragma&Nosplit != 0 {
yyerror("go:nosplit and go:systemstack cannot be combined")
}
}
// Clumsy but important.
// See test/recover.go for test cases and src/reflect/value.go
// for the actual functions being considered.
if myimportpath == "reflect" {
if Curfn.Func.Nname.Sym.Name == "callReflect" || Curfn.Func.Nname.Sym.Name == "callMethod" {
ptxt.From3.Offset |= obj.WRAPPER
}
}
gcargs := makefuncdatasym("gcargs·", obj.FUNCDATA_ArgsPointerMaps)
gclocals := makefuncdatasym("gclocals·", obj.FUNCDATA_LocalsPointerMaps)
if obj.Fieldtrack_enabled != 0 && len(Curfn.Func.FieldTrack) > 0 {
trackSyms := make([]*Sym, 0, len(Curfn.Func.FieldTrack))
for sym := range Curfn.Func.FieldTrack {
trackSyms = append(trackSyms, sym)
}
sort.Sort(symByName(trackSyms))
for _, sym := range trackSyms {
gtrack(sym)
}
}
gendebug(ptxt.From.Sym, fn.Func.Dcl)
genssa(ssafn, ptxt, gcargs, gclocals)
ssafn.Free()
}
func gendebug(fn *obj.LSym, decls []*Node) {
if fn == nil {
return
}
for _, n := range decls {
if n.Op != ONAME { // might be OTYPE or OLITERAL
continue
}
var name int16
switch n.Class {
case PAUTO:
if !n.Used {
continue
}
name = obj.NAME_AUTO
case PPARAM, PPARAMOUT:
name = obj.NAME_PARAM
default:
continue
}
a := &obj.Auto{
Asym: obj.Linklookup(Ctxt, n.Sym.Name, 0),
Aoffset: int32(n.Xoffset),
Name: name,
Gotype: Linksym(ngotype(n)),
}
a.Link = fn.Autom
fn.Autom = a
}
}
cmd/compile: export inlined function bodies Completed implementation for exporting inlined functions using the new binary export format. This change passes (export GO_GCFLAGS=-newexport; make all.bash) but for gc's builtin_test.go which we need to adjust before enabling this code by default. For a high-level description of the export format see the comment at the top of bexport.go. Major changes: 1) The export format for the platform independent export data changed: When we export inlined function bodies, additional objects (other functions, types, etc.) that are referred to by the function bodies will need to be exported. While this doesn't affect the platform-independent portion directly, it adds more objects to the exportlist while we are exporting. Instead of trying to sort the objects into groups, just export objects as they appear in the export list. This is slightly less compact (one extra byte per object), but it is simpler and much more flexible. 2) The export format contains now three sections: 1) The plat- form independent objects, 2) the objects pulled in for export via inlined function bodies, and 3) the inlined function bodies. 3) Completed the exporting and importing code for inlined function bodies. The format is completely compiler-specific and easily changeable w/o affecting other tools. There is still quite a bit of room for denser encoding. This can happen at any time in the future. This change contains also the adjustments for go/internal/gcimporter, necessary because of the export format change 1) mentioned above. For #13241. Change-Id: I86bca0bd984b12ccf13d0d30892e6e25f6d04ed5 Reviewed-on: https://go-review.googlesource.com/21172 Run-TryBot: Robert Griesemer <gri@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-03-18 17:21:32 -07:00
type symByName []*Sym
func (a symByName) Len() int { return len(a) }
func (a symByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
func (a symByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }