2015-03-05 10:45:56 -05:00
// Copyright 2009 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.
// “Abstract” syntax representation.
package gc
2017-01-11 15:48:30 -08:00
import (
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
"cmd/compile/internal/ssa"
2017-01-11 15:48:30 -08:00
"cmd/compile/internal/syntax"
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
"cmd/compile/internal/types"
2017-04-12 13:23:07 -07:00
"cmd/internal/obj"
2017-01-11 15:48:30 -08:00
"cmd/internal/src"
)
2016-12-06 17:08:06 -08:00
2015-03-05 10:45:56 -05:00
// A Node is a single node in the syntax tree.
// Actually the syntax tree is a syntax DAG, because there is only one
// node with Op=ONAME for a given instance of a variable x.
2017-03-28 07:12:57 -07:00
// The same is true for Op=OTYPE and Op=OLITERAL. See Node.mayBeShared.
2015-03-05 10:45:56 -05:00
type Node struct {
2015-03-05 13:57:36 -05:00
// Tree structure.
// Generic recursive walks should follow these fields.
Left * Node
Right * Node
2016-03-07 14:32:18 -08:00
Ninit Nodes
2016-02-27 14:31:33 -08:00
Nbody Nodes
2016-03-07 14:32:18 -08:00
List Nodes
Rlist Nodes
2015-03-05 13:57:36 -05:00
// most nodes
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
Type * types . Type
2015-05-27 10:44:43 -04:00
Orig * Node // original form, for printing, and tracking copies of ONAMEs
2015-03-05 13:57:36 -05:00
// func
2015-03-25 19:33:01 -07:00
Func * Func
2015-03-05 13:57:36 -05:00
2017-01-11 11:24:35 -08:00
// ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
2015-05-27 07:31:56 -04:00
Name * Name
2015-03-05 13:57:36 -05:00
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
Sym * types . Sym // various
2015-05-27 00:47:05 -04:00
E interface { } // Opt or Val, see methods below
2015-03-05 13:57:36 -05:00
2016-10-12 15:48:18 -07:00
// Various. Usually an offset into a struct. For example:
// - ONAME nodes that refer to local variables use it to identify their stack frame position.
2016-10-24 14:33:22 -07:00
// - ODOT, ODOTPTR, and OINDREGSP use it to indicate offset relative to their base address.
2016-10-12 15:48:18 -07:00
// - OSTRUCTKEY uses it to store the named field's offset.
2018-10-06 06:10:25 +00:00
// - Named OLITERALs use it to store their ambient iota value.
2016-03-14 00:24:43 -07:00
// Possibly still more uses. If you find any, document them.
2015-05-26 21:30:20 -04:00
Xoffset int64
2015-05-18 15:49:02 -07:00
2016-12-15 17:17:01 -08:00
Pos src . XPos
2015-05-18 15:49:02 -07:00
2017-03-03 13:38:49 -08:00
flags bitset32
2017-02-27 19:56:38 +02:00
2015-05-26 23:42:41 -04:00
Esc uint16 // EscXXX
2015-05-18 15:49:02 -07:00
2018-03-08 04:18:18 -08:00
Op Op
aux uint8
}
func ( n * Node ) ResetAux ( ) {
n . aux = 0
}
func ( n * Node ) SubOp ( ) Op {
switch n . Op {
2018-10-10 16:47:47 -07:00
case OASOP , ONAME :
2018-03-08 04:18:18 -08:00
default :
Fatalf ( "unexpected op: %v" , n . Op )
}
return Op ( n . aux )
}
func ( n * Node ) SetSubOp ( op Op ) {
switch n . Op {
2018-10-10 16:47:47 -07:00
case OASOP , ONAME :
2018-03-08 04:18:18 -08:00
default :
Fatalf ( "unexpected op: %v" , n . Op )
}
n . aux = uint8 ( op )
}
func ( n * Node ) IndexMapLValue ( ) bool {
if n . Op != OINDEXMAP {
Fatalf ( "unexpected op: %v" , n . Op )
}
return n . aux != 0
}
func ( n * Node ) SetIndexMapLValue ( b bool ) {
if n . Op != OINDEXMAP {
Fatalf ( "unexpected op: %v" , n . Op )
}
if b {
n . aux = 1
} else {
n . aux = 0
}
}
func ( n * Node ) TChanDir ( ) types . ChanDir {
if n . Op != OTCHAN {
Fatalf ( "unexpected op: %v" , n . Op )
}
return types . ChanDir ( n . aux )
}
func ( n * Node ) SetTChanDir ( dir types . ChanDir ) {
if n . Op != OTCHAN {
Fatalf ( "unexpected op: %v" , n . Op )
}
n . aux = uint8 ( dir )
2016-04-21 19:28:28 -07:00
}
2018-02-02 16:26:58 -05:00
func ( n * Node ) IsSynthetic ( ) bool {
name := n . Sym . Name
return name [ 0 ] == '.' || name [ 0 ] == '~'
}
2016-10-28 13:33:57 -04:00
// IsAutoTmp indicates if n was created by the compiler as a temporary,
// based on the setting of the .AutoTemp flag in n's Name.
func ( n * Node ) IsAutoTmp ( ) bool {
if n == nil || n . Op != ONAME {
return false
}
2017-02-27 19:56:38 +02:00
return n . Name . AutoTemp ( )
2016-10-28 13:33:57 -04:00
}
2016-04-21 19:28:28 -07:00
const (
2017-04-25 18:14:12 -07:00
nodeClass , _ = iota , 1 << iota // PPARAM, PAUTO, PEXTERN, etc; three bits; first in the list because frequently accessed
_ , _ // second nodeClass bit
_ , _ // third nodeClass bit
nodeWalkdef , _ // tracks state during typecheckdef; 2 == loop detected; two bits
2017-04-25 17:55:12 -07:00
_ , _ // second nodeWalkdef bit
2017-04-25 18:02:43 -07:00
nodeTypecheck , _ // tracks state during typechecking; 2 == loop detected; two bits
_ , _ // second nodeTypecheck bit
2017-04-25 17:55:12 -07:00
nodeInitorder , _ // tracks state during init1; two bits
_ , _ // second nodeInitorder bit
2017-04-25 17:30:08 -07:00
_ , nodeHasBreak
_ , nodeIsClosureVar
_ , nodeIsOutputParamHeapAddr
_ , nodeNoInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
_ , nodeAssigned // is the variable ever assigned to
_ , nodeAddrtaken // address taken, even if not moved to heap
_ , nodeImplicit
2017-10-06 11:32:28 -04:00
_ , nodeIsddd // is the argument variadic
_ , nodeDiag // already printed error about this
_ , nodeColas // OAS resulting from :=
_ , nodeNonNil // guaranteed to be non-nil
_ , nodeNoescape // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
_ , nodeBounded // bounds check unnecessary
_ , nodeAddable // addressable
_ , nodeHasCall // expression contains a function call
_ , nodeLikely // if statement condition likely
_ , nodeHasVal // node.E contains a Val
_ , nodeHasOpt // node.E contains an Opt
_ , nodeEmbedded // ODCLFIELD embedded type
_ , nodeInlFormal // OPAUTO created by inliner, derived from callee formal
_ , nodeInlLocal // OPAUTO created by inliner, derived from callee local
2016-04-21 19:28:28 -07:00
)
2017-04-25 18:14:12 -07:00
func ( n * Node ) Class ( ) Class { return Class ( n . flags . get3 ( nodeClass ) ) }
2017-04-25 17:55:12 -07:00
func ( n * Node ) Walkdef ( ) uint8 { return n . flags . get2 ( nodeWalkdef ) }
2017-04-25 18:02:43 -07:00
func ( n * Node ) Typecheck ( ) uint8 { return n . flags . get2 ( nodeTypecheck ) }
2017-04-25 17:55:12 -07:00
func ( n * Node ) Initorder ( ) uint8 { return n . flags . get2 ( nodeInitorder ) }
2017-04-25 17:30:08 -07:00
2017-02-27 19:56:38 +02:00
func ( n * Node ) HasBreak ( ) bool { return n . flags & nodeHasBreak != 0 }
func ( n * Node ) IsClosureVar ( ) bool { return n . flags & nodeIsClosureVar != 0 }
func ( n * Node ) NoInline ( ) bool { return n . flags & nodeNoInline != 0 }
func ( n * Node ) IsOutputParamHeapAddr ( ) bool { return n . flags & nodeIsOutputParamHeapAddr != 0 }
func ( n * Node ) Assigned ( ) bool { return n . flags & nodeAssigned != 0 }
func ( n * Node ) Addrtaken ( ) bool { return n . flags & nodeAddrtaken != 0 }
func ( n * Node ) Implicit ( ) bool { return n . flags & nodeImplicit != 0 }
func ( n * Node ) Isddd ( ) bool { return n . flags & nodeIsddd != 0 }
func ( n * Node ) Diag ( ) bool { return n . flags & nodeDiag != 0 }
func ( n * Node ) Colas ( ) bool { return n . flags & nodeColas != 0 }
func ( n * Node ) NonNil ( ) bool { return n . flags & nodeNonNil != 0 }
func ( n * Node ) Noescape ( ) bool { return n . flags & nodeNoescape != 0 }
func ( n * Node ) Bounded ( ) bool { return n . flags & nodeBounded != 0 }
func ( n * Node ) Addable ( ) bool { return n . flags & nodeAddable != 0 }
2017-03-03 13:38:49 -08:00
func ( n * Node ) HasCall ( ) bool { return n . flags & nodeHasCall != 0 }
2017-04-25 08:46:00 -07:00
func ( n * Node ) Likely ( ) bool { return n . flags & nodeLikely != 0 }
2017-04-25 08:51:52 -07:00
func ( n * Node ) HasVal ( ) bool { return n . flags & nodeHasVal != 0 }
func ( n * Node ) HasOpt ( ) bool { return n . flags & nodeHasOpt != 0 }
2017-04-25 17:44:40 -07:00
func ( n * Node ) Embedded ( ) bool { return n . flags & nodeEmbedded != 0 }
2017-10-06 11:32:28 -04:00
func ( n * Node ) InlFormal ( ) bool { return n . flags & nodeInlFormal != 0 }
func ( n * Node ) InlLocal ( ) bool { return n . flags & nodeInlLocal != 0 }
2017-02-27 19:56:38 +02:00
2017-04-25 18:14:12 -07:00
func ( n * Node ) SetClass ( b Class ) { n . flags . set3 ( nodeClass , uint8 ( b ) ) }
2017-04-25 17:55:12 -07:00
func ( n * Node ) SetWalkdef ( b uint8 ) { n . flags . set2 ( nodeWalkdef , b ) }
2017-04-25 18:02:43 -07:00
func ( n * Node ) SetTypecheck ( b uint8 ) { n . flags . set2 ( nodeTypecheck , b ) }
2017-04-25 17:55:12 -07:00
func ( n * Node ) SetInitorder ( b uint8 ) { n . flags . set2 ( nodeInitorder , b ) }
2017-04-25 17:30:08 -07:00
2017-02-27 19:56:38 +02:00
func ( n * Node ) SetHasBreak ( b bool ) { n . flags . set ( nodeHasBreak , b ) }
func ( n * Node ) SetIsClosureVar ( b bool ) { n . flags . set ( nodeIsClosureVar , b ) }
func ( n * Node ) SetNoInline ( b bool ) { n . flags . set ( nodeNoInline , b ) }
func ( n * Node ) SetIsOutputParamHeapAddr ( b bool ) { n . flags . set ( nodeIsOutputParamHeapAddr , b ) }
func ( n * Node ) SetAssigned ( b bool ) { n . flags . set ( nodeAssigned , b ) }
func ( n * Node ) SetAddrtaken ( b bool ) { n . flags . set ( nodeAddrtaken , b ) }
func ( n * Node ) SetImplicit ( b bool ) { n . flags . set ( nodeImplicit , b ) }
func ( n * Node ) SetIsddd ( b bool ) { n . flags . set ( nodeIsddd , b ) }
func ( n * Node ) SetDiag ( b bool ) { n . flags . set ( nodeDiag , b ) }
func ( n * Node ) SetColas ( b bool ) { n . flags . set ( nodeColas , b ) }
func ( n * Node ) SetNonNil ( b bool ) { n . flags . set ( nodeNonNil , b ) }
func ( n * Node ) SetNoescape ( b bool ) { n . flags . set ( nodeNoescape , b ) }
func ( n * Node ) SetBounded ( b bool ) { n . flags . set ( nodeBounded , b ) }
func ( n * Node ) SetAddable ( b bool ) { n . flags . set ( nodeAddable , b ) }
2017-03-03 13:38:49 -08:00
func ( n * Node ) SetHasCall ( b bool ) { n . flags . set ( nodeHasCall , b ) }
2017-04-25 08:46:00 -07:00
func ( n * Node ) SetLikely ( b bool ) { n . flags . set ( nodeLikely , b ) }
2017-04-25 08:51:52 -07:00
func ( n * Node ) SetHasVal ( b bool ) { n . flags . set ( nodeHasVal , b ) }
func ( n * Node ) SetHasOpt ( b bool ) { n . flags . set ( nodeHasOpt , b ) }
2017-04-25 17:44:40 -07:00
func ( n * Node ) SetEmbedded ( b bool ) { n . flags . set ( nodeEmbedded , b ) }
2017-10-06 11:32:28 -04:00
func ( n * Node ) SetInlFormal ( b bool ) { n . flags . set ( nodeInlFormal , b ) }
func ( n * Node ) SetInlLocal ( b bool ) { n . flags . set ( nodeInlLocal , b ) }
2016-06-18 19:40:57 -07:00
2015-05-27 00:47:05 -04:00
// Val returns the Val for the node.
func ( n * Node ) Val ( ) Val {
2017-04-25 08:51:52 -07:00
if ! n . HasVal ( ) {
2015-05-27 00:47:05 -04:00
return Val { }
}
return Val { n . E }
}
// SetVal sets the Val for the node, which must not have been used with SetOpt.
func ( n * Node ) SetVal ( v Val ) {
2017-04-25 08:51:52 -07:00
if n . HasOpt ( ) {
2015-05-27 00:47:05 -04:00
Debug [ 'h' ] = 1
Dump ( "have Opt" , n )
2015-08-30 23:10:03 +02:00
Fatalf ( "have Opt" )
2015-05-27 00:47:05 -04:00
}
2017-04-25 08:51:52 -07:00
n . SetHasVal ( true )
2015-05-27 00:47:05 -04:00
n . E = v . U
}
// Opt returns the optimizer data for the node.
func ( n * Node ) Opt ( ) interface { } {
2017-04-25 08:51:52 -07:00
if ! n . HasOpt ( ) {
2015-05-27 00:47:05 -04:00
return nil
}
return n . E
}
// SetOpt sets the optimizer data for the node, which must not have been used with SetVal.
// SetOpt(nil) is ignored for Vals to simplify call sites that are clearing Opts.
func ( n * Node ) SetOpt ( x interface { } ) {
2017-04-25 08:51:52 -07:00
if x == nil && n . HasVal ( ) {
2015-05-27 00:47:05 -04:00
return
}
2017-04-25 08:51:52 -07:00
if n . HasVal ( ) {
2015-05-27 00:47:05 -04:00
Debug [ 'h' ] = 1
Dump ( "have Val" , n )
2015-08-30 23:10:03 +02:00
Fatalf ( "have Val" )
2015-05-27 00:47:05 -04:00
}
2017-04-25 08:51:52 -07:00
n . SetHasOpt ( true )
2015-05-27 00:47:05 -04:00
n . E = x
2015-03-10 21:37:13 -07:00
}
2016-10-27 20:15:29 -07:00
func ( n * Node ) Iota ( ) int64 {
return n . Xoffset
}
func ( n * Node ) SetIota ( x int64 ) {
n . Xoffset = x
}
2017-03-28 07:12:57 -07:00
// mayBeShared reports whether n may occur in multiple places in the AST.
// Extra care must be taken when mutating such a node.
func ( n * Node ) mayBeShared ( ) bool {
switch n . Op {
case ONAME , OLITERAL , OTYPE :
return true
}
return false
}
2017-10-24 14:45:41 -07:00
// isMethodExpression reports whether n represents a method expression T.M.
func ( n * Node ) isMethodExpression ( ) bool {
return n . Op == ONAME && n . Left != nil && n . Left . Op == OTYPE && n . Right != nil && n . Right . Op == ONAME
}
2017-04-23 05:10:21 -07:00
// funcname returns the name of the function n.
func ( n * Node ) funcname ( ) string {
2017-04-27 16:27:47 -07:00
if n == nil || n . Func == nil || n . Func . Nname == nil {
return "<nil>"
}
2017-04-23 05:10:21 -07:00
return n . Func . Nname . Sym . Name
}
2017-01-11 11:24:35 -08:00
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
2015-04-03 17:43:38 -07:00
type Name struct {
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
Pack * Node // real package for import . names
Pkg * types . Pkg // pkg for OPACK nodes
Defn * Node // initializing assignment
Curfn * Node // function for local variables
Param * Param // additional fields for ONAME, OTYPE
Decldepth int32 // declaration loop depth, increased for every loop or label
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
2018-04-10 17:02:07 -07:00
flags bitset8
2015-04-03 17:43:38 -07:00
}
2017-02-27 19:56:38 +02:00
const (
nameCaptured = 1 << iota // is the variable captured by a closure
nameReadonly
nameByval // is the variable captured by value or by reference
nameNeedzero // if it contains pointers, needs to be zeroed on function entry
nameKeepalive // mark value live across unknown assembly call
nameAutoTemp // is the variable a temporary (implies no dwarf info. reset if escapes to heap)
2018-04-10 17:02:07 -07:00
nameUsed // for variable declared and not used error
2017-02-27 19:56:38 +02:00
)
func ( n * Name ) Captured ( ) bool { return n . flags & nameCaptured != 0 }
func ( n * Name ) Readonly ( ) bool { return n . flags & nameReadonly != 0 }
func ( n * Name ) Byval ( ) bool { return n . flags & nameByval != 0 }
func ( n * Name ) Needzero ( ) bool { return n . flags & nameNeedzero != 0 }
func ( n * Name ) Keepalive ( ) bool { return n . flags & nameKeepalive != 0 }
func ( n * Name ) AutoTemp ( ) bool { return n . flags & nameAutoTemp != 0 }
2018-04-10 17:02:07 -07:00
func ( n * Name ) Used ( ) bool { return n . flags & nameUsed != 0 }
2017-02-27 19:56:38 +02:00
func ( n * Name ) SetCaptured ( b bool ) { n . flags . set ( nameCaptured , b ) }
func ( n * Name ) SetReadonly ( b bool ) { n . flags . set ( nameReadonly , b ) }
func ( n * Name ) SetByval ( b bool ) { n . flags . set ( nameByval , b ) }
func ( n * Name ) SetNeedzero ( b bool ) { n . flags . set ( nameNeedzero , b ) }
func ( n * Name ) SetKeepalive ( b bool ) { n . flags . set ( nameKeepalive , b ) }
func ( n * Name ) SetAutoTemp ( b bool ) { n . flags . set ( nameAutoTemp , b ) }
2018-04-10 17:02:07 -07:00
func ( n * Name ) SetUsed ( b bool ) { n . flags . set ( nameUsed , b ) }
2017-02-27 19:56:38 +02:00
2015-05-18 10:27:59 -07:00
type Param struct {
2016-10-28 11:42:40 -07:00
Ntype * Node
Heapaddr * Node // temp holding heap address of param
2015-05-18 10:27:59 -07:00
cmd/compile: fix liveness computation for heap-escaped parameters
The liveness computation of parameters generally was never
correct, but forcing all parameters to be live throughout the
function covered up that problem. The new SSA back end is
too clever: even though it currently keeps the parameter values live
throughout the function, it may find optimizations that mean
the current values are not written back to the original parameter
stack slots immediately or ever (for example if a parameter is set
to nil, SSA constant propagation may replace all later uses of the
parameter with a constant nil, eliminating the need to write the nil
value back to the stack slot), so the liveness code must now
track the actual operations on the stack slots, exposing these
problems.
One small problem in the handling of arguments is that nodarg
can return ONAME PPARAM nodes with adjusted offsets, so that
there are actually multiple *Node pointers for the same parameter
in the instruction stream. This might be possible to correct, but
not in this CL. For now, we fix this by using n.Orig instead of n
when considering PPARAM and PPARAMOUT nodes.
The major problem in the handling of arguments is general
confusion in the liveness code about the meaning of PPARAM|PHEAP
and PPARAMOUT|PHEAP nodes, especially as contrasted with PAUTO|PHEAP.
The difference between these two is that when a local variable "moves"
to the heap, it's really just allocated there to start with; in contrast,
when an argument moves to the heap, the actual data has to be copied
there from the stack at the beginning of the function, and when a
result "moves" to the heap the value in the heap has to be copied
back to the stack when the function returns
This general confusion is also present in the SSA back end.
The PHEAP bit worked decently when I first introduced it 7 years ago (!)
in 391425ae. The back end did nothing sophisticated, and in particular
there was no analysis at all: no escape analysis, no liveness analysis,
and certainly no SSA back end. But the complications caused in the
various downstream consumers suggest that this should be a detail
kept mainly in the front end.
This CL therefore eliminates both the PHEAP bit and even the idea of
"heap variables" from the back ends.
First, it replaces the PPARAM|PHEAP, PPARAMOUT|PHEAP, and PAUTO|PHEAP
variable classes with the single PAUTOHEAP, a pseudo-class indicating
a variable maintained on the heap and available by indirecting a
local variable kept on the stack (a plain PAUTO).
Second, walkexpr replaces all references to PAUTOHEAP variables
with indirections of the corresponding PAUTO variable.
The back ends and the liveness code now just see plain indirected
variables. This may actually produce better code, but the real goal
here is to eliminate these little-used and somewhat suspect code
paths in the back end analyses.
The OPARAM node type goes away too.
A followup CL will do the same to PPARAMREF. I'm not sure that
the back ends (SSA in particular) are handling those right either,
and with the framework established in this CL that change is trivial
and the result clearly more correct.
Fixes #15747.
Change-Id: I2770b1ce3cbc93981bfc7166be66a9da12013d74
Reviewed-on: https://go-review.googlesource.com/23393
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2016-05-25 01:33:24 -04:00
// ONAME PAUTOHEAP
Stackcopy * Node // the PPARAM/PPARAMOUT on-stack slot (moved func params only)
2015-05-18 10:27:59 -07:00
2016-05-25 10:29:50 -04:00
// ONAME closure linkage
2016-05-27 00:56:19 -04:00
// Consider:
//
// func f() {
// x := 1 // x1
// func() {
// use(x) // x2
// func() {
// use(x) // x3
// --- parser is here ---
// }()
// }()
// }
//
// There is an original declaration of x and then a chain of mentions of x
// leading into the current function. Each time x is mentioned in a new closure,
// we create a variable representing x for use in that specific closure,
// since the way you get to x is different in each closure.
//
// Let's number the specific variables as shown in the code:
// x1 is the original x, x2 is when mentioned in the closure,
// and x3 is when mentioned in the closure in the closure.
//
// We keep these linked (assume N > 1):
//
// - x1.Defn = original declaration statement for x (like most variables)
// - x1.Innermost = current innermost closure x (in this case x3), or nil for none
2017-02-27 19:56:38 +02:00
// - x1.IsClosureVar() = false
2016-05-27 00:56:19 -04:00
//
// - xN.Defn = x1, N > 1
2017-02-27 19:56:38 +02:00
// - xN.IsClosureVar() = true, N > 1
2016-05-27 00:56:19 -04:00
// - x2.Outer = nil
// - xN.Outer = x(N-1), N > 2
//
//
// When we look up x in the symbol table, we always get x1.
// Then we can use x1.Innermost (if not nil) to get the x
// for the innermost known closure function,
// but the first reference in a closure will find either no x1.Innermost
// or an x1.Innermost with .Funcdepth < Funcdepth.
// In that case, a new xN must be created, linked in with:
//
// xN.Defn = x1
// xN.Outer = x1.Innermost
// x1.Innermost = xN
//
// When we finish the function, we'll process its closure variables
// and find xN and pop it off the list using:
//
// x1 := xN.Defn
// x1.Innermost = xN.Outer
//
// We leave xN.Innermost set so that we can still get to the original
// variable quickly. Not shown here, but once we're
// done parsing a function and no longer need xN.Outer for the
// lexical x reference links as described above, closurebody
// recomputes xN.Outer as the semantic x reference link tree,
// even filling in x in intermediate closures that might not
// have mentioned it along the way to inner closures that did.
// See closurebody for details.
//
// During the eventual compilation, then, for closure variables we have:
//
// xN.Defn = original variable
// xN.Outer = variable captured in next outward scope
// to make closure where xN appears
//
// Because of the sharding of pieces of the node, x.Defn means x.Name.Defn
// and x.Innermost/Outer means x.Name.Param.Innermost/Outer.
Innermost * Node
2016-05-27 14:24:26 -04:00
Outer * Node
cmd/compile: add go:notinheap type pragma
This adds a //go:notinheap pragma for declarations of types that must
not be heap allocated. We ensure these rules by disallowing new(T),
make([]T), append([]T), or implicit allocation of T, by disallowing
conversions to notinheap types, and by propagating notinheap to any
struct or array that contains notinheap elements.
The utility of this pragma is that we can eliminate write barriers for
writes to pointers to go:notinheap types, since the write barrier is
guaranteed to be a no-op. This will let us mark several scheduler and
memory allocator structures as go:notinheap, which will let us
disallow write barriers in the scheduler and memory allocator much
more thoroughly and also eliminate some problematic hybrid write
barriers.
This also makes go:nowritebarrierrec and go:yeswritebarrierrec much
more powerful. Currently we use go:nowritebarrier all over the place,
but it's almost never what you actually want: when write barriers are
illegal, they're typically illegal for a whole dynamic scope. Partly
this is because go:nowritebarrier has been around longer, but it's
also because go:nowritebarrierrec couldn't be used in situations that
had no-op write barriers or where some nested scope did allow write
barriers. go:notinheap eliminates many no-op write barriers and
go:yeswritebarrierrec makes it possible to opt back in to write
barriers, so these two changes will let us use go:nowritebarrierrec
far more liberally.
This updates #13386, which is about controlling pointers from non-GC'd
memory to GC'd memory. That would require some additional pragma (or
pragmas), but could build on this pragma.
Change-Id: I6314f8f4181535dd166887c9ec239977b54940bd
Reviewed-on: https://go-review.googlesource.com/30939
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-10-11 22:53:27 -04:00
2017-01-11 11:24:35 -08:00
// OTYPE
cmd/compile: add go:notinheap type pragma
This adds a //go:notinheap pragma for declarations of types that must
not be heap allocated. We ensure these rules by disallowing new(T),
make([]T), append([]T), or implicit allocation of T, by disallowing
conversions to notinheap types, and by propagating notinheap to any
struct or array that contains notinheap elements.
The utility of this pragma is that we can eliminate write barriers for
writes to pointers to go:notinheap types, since the write barrier is
guaranteed to be a no-op. This will let us mark several scheduler and
memory allocator structures as go:notinheap, which will let us
disallow write barriers in the scheduler and memory allocator much
more thoroughly and also eliminate some problematic hybrid write
barriers.
This also makes go:nowritebarrierrec and go:yeswritebarrierrec much
more powerful. Currently we use go:nowritebarrier all over the place,
but it's almost never what you actually want: when write barriers are
illegal, they're typically illegal for a whole dynamic scope. Partly
this is because go:nowritebarrier has been around longer, but it's
also because go:nowritebarrierrec couldn't be used in situations that
had no-op write barriers or where some nested scope did allow write
barriers. go:notinheap eliminates many no-op write barriers and
go:yeswritebarrierrec makes it possible to opt back in to write
barriers, so these two changes will let us use go:nowritebarrierrec
far more liberally.
This updates #13386, which is about controlling pointers from non-GC'd
memory to GC'd memory. That would require some additional pragma (or
pragmas), but could build on this pragma.
Change-Id: I6314f8f4181535dd166887c9ec239977b54940bd
Reviewed-on: https://go-review.googlesource.com/30939
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-10-11 22:53:27 -04:00
//
// TODO: Should Func pragmas also be stored on the Name?
2017-01-11 15:48:30 -08:00
Pragma syntax . Pragma
2017-01-12 15:21:21 -08:00
Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE)
2015-05-18 10:27:59 -07:00
}
2017-10-25 13:52:26 -07:00
// Functions
//
// A simple function declaration is represented as an ODCLFUNC node f
// and an ONAME node n. They're linked to one another through
// f.Func.Nname == n and n.Name.Defn == f. When functions are
// referenced by name in an expression, the function's ONAME node is
// used directly.
//
// Function names have n.Class() == PFUNC. This distinguishes them
// from variables of function type.
//
// Confusingly, n.Func and f.Func both exist, but commonly point to
// different Funcs. (Exception: an OCALLPART's Func does point to its
// ODCLFUNC's Func.)
//
// A method declaration is represented like functions, except n.Sym
// will be the qualified method name (e.g., "T.m") and
// f.Func.Shortname is the bare method name (e.g., "m").
//
// Method expressions are represented as ONAME/PFUNC nodes like
// function names, but their Left and Right fields still point to the
// type and method, respectively. They can be distinguished from
// normal functions with isMethodExpression. Also, unlike function
// name nodes, method expression nodes exist for each method
// expression. The declaration ONAME can be accessed with
// x.Type.Nname(), where x is the method expression ONAME node.
//
// Method values are represented by ODOTMETH/ODOTINTER when called
// immediately, and OCALLPART otherwise. They are like method
// expressions, except that for ODOTMETH/ODOTINTER the method name is
// stored in Sym instead of Right.
//
// Closures are represented by OCLOSURE node c. They link back and
// forth with the ODCLFUNC via Func.Closure; that is, c.Func.Closure
// == f and f.Func.Closure == c.
//
// Function bodies are stored in f.Nbody, and inline function bodies
// are stored in n.Func.Inl. Pragmas are stored in f.Func.Pragma.
//
// Imported functions skip the ODCLFUNC, so n.Name.Defn is nil. They
// also use Dcl instead of Inldcl.
2015-03-10 21:37:13 -07:00
// Func holds Node fields used only with function-like nodes.
type Func struct {
2017-05-02 16:46:01 +02:00
Shortname * types . Sym
Enter Nodes // for example, allocate and initialize memory for escaping parameters
Exit Nodes
Cvars Nodes // closure params
Dcl [ ] * Node // autodcl for this func/closure
// Parents records the parent scope of each scope within a
// function. The root scope (0) has no parent, so the i'th
// scope's parent is stored at Parents[i-1].
Parents [ ] ScopeID
// Marks records scope boundary changes.
Marks [ ] Mark
cmd/compile: cleanup closure.go
The main thing is we now eagerly create the ODCLFUNC node for
closures, immediately cross-link them, and assign fields (e.g., Nbody,
Dcl, Parents, Marks) directly on the ODCLFUNC (previously they were
assigned on the OCLOSURE and later moved to the ODCLFUNC).
This allows us to set Curfn to the ODCLFUNC instead of the OCLOSURE,
which makes things more consistent with normal function declarations.
(Notably, this means Cvars now hang off the ODCLFUNC instead of the
OCLOSURE.)
Assignment of xfunc symbol names also now happens before typechecking
their body, which means debugging output now provides a more helpful
name than "<S>".
In golang.org/cl/66810, we changed "x := y" statements to avoid
creating false closure variables for x, but we still create them for
struct literals like "s{f: x}". Update comment in capturevars
accordingly.
More opportunity for cleanups still, but this makes some substantial
progress, IMO.
Passes toolstash-check.
Change-Id: I65a4efc91886e3dcd1000561348af88297775cd7
Reviewed-on: https://go-review.googlesource.com/100197
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2018-03-08 06:25:04 -08:00
// Closgen tracks how many closures have been generated within
// this function. Used by closurename for creating unique
// function names.
Closgen int
cmd/compile: factor out Pkg, Sym, and Type into package types
- created new package cmd/compile/internal/types
- moved Pkg, Sym, Type to new package
- to break cycles, for now we need the (ugly) types/utils.go
file which contains a handful of functions that must be installed
early by the gc frontend
- to break cycles, for now we need two functions to convert between
*gc.Node and *types.Node (the latter is a dummy type)
- adjusted the gc's code to use the new package and the conversion
functions as needed
- made several Pkg, Sym, and Type methods functions as needed
- renamed constructors typ, typPtr, typArray, etc. to types.New,
types.NewPtr, types.NewArray, etc.
Passes toolstash-check -all.
Change-Id: I8adfa5e85c731645d0a7fd2030375ed6ebf54b72
Reviewed-on: https://go-review.googlesource.com/39855
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-04-04 17:54:02 -07:00
FieldTrack map [ * types . Sym ] struct { }
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
DebugInfo * ssa . FuncDebug
2016-10-03 16:09:13 -07:00
Ntype * Node // signature
Top int // top context (Ecall, Eproc, etc)
Closure * Node // OCLOSURE <-> ODCLFUNC
Nname * Node
2017-04-12 13:23:07 -07:00
lsym * obj . LSym
2015-03-10 21:37:13 -07:00
2018-04-04 15:53:27 -07:00
Inl * Inline
2015-03-10 21:37:13 -07:00
2016-06-01 10:15:02 -07:00
Label int32 // largest auto-generated label in this function
2016-12-15 17:17:01 -08:00
Endlineno src . XPos
2017-10-24 17:10:02 -04:00
WBPos src . XPos // position of first write barrier; see SetWBPos
2015-03-10 21:37:13 -07:00
2017-02-27 19:56:38 +02:00
Pragma syntax . Pragma // go:xxx function annotations
2017-09-18 14:54:10 -07:00
flags bitset16
cmd/compile: improve coverage of nowritebarrierrec check
The current go:nowritebarrierrec checker has two problems that limit
its coverage:
1. It doesn't understand that systemstack calls its argument, which
means there are several cases where we fail to detect prohibited write
barriers.
2. It only observes calls in the AST, so calls constructed during
lowering by SSA aren't followed.
This CL completely rewrites this checker to address these issues.
The current checker runs entirely after walk and uses visitBottomUp,
which introduces several problems for checking across systemstack.
First, visitBottomUp itself doesn't understand systemstack calls, so
the callee may be ordered after the caller, causing the checker to
fail to propagate constraints. Second, many systemstack calls are
passed a closure, which is quite difficult to resolve back to the
function definition after transformclosure and walk have run. Third,
visitBottomUp works exclusively on the AST, so it can't observe calls
created by SSA.
To address these problems, this commit splits the check into two
phases and rewrites it to use a call graph generated during SSA
lowering. The first phase runs before transformclosure/walk and simply
records systemstack arguments when they're easy to get. Then, it
modifies genssa to record static call edges at the point where we're
lowering to Progs (which is the latest point at which position
information is conveniently available). Finally, the second phase runs
after all functions have been lowered and uses a direct BFS walk of
the call graph (combining systemstack calls with static calls) to find
prohibited write barriers and construct nice error messages.
Fixes #22384.
For #22460.
Change-Id: I39668f7f2366ab3c1ab1a71eaf25484d25349540
Reviewed-on: https://go-review.googlesource.com/72773
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-10-22 16:36:27 -04:00
// nwbrCalls records the LSyms of functions called by this
// function for go:nowritebarrierrec analysis. Only filled in
// if nowritebarrierrecCheck != nil.
nwbrCalls * [ ] nowritebarrierrecCallSym
2015-03-05 10:45:56 -05:00
}
2018-04-04 15:53:27 -07:00
// An Inline holds fields used for function bodies that can be inlined.
type Inline struct {
Cost int32 // heuristic cost of inlining this function
// Copies of Func.Dcl and Nbody for use during inlining.
Dcl [ ] * Node
Body [ ] * Node
}
2017-05-02 16:46:01 +02:00
// A Mark represents a scope boundary.
type Mark struct {
// Pos is the position of the token that marks the scope
// change.
Pos src . XPos
// Scope identifies the innermost scope to the right of Pos.
Scope ScopeID
}
// A ScopeID represents a lexical scope within a function.
type ScopeID int32
2017-02-27 19:56:38 +02:00
const (
funcDupok = 1 << iota // duplicate definitions ok
funcWrapper // is method wrapper
funcNeedctxt // function uses context register (has closure variables)
funcReflectMethod // function calls reflect.Type.Method or MethodByName
funcIsHiddenClosure
2017-09-18 14:54:10 -07:00
funcHasDefer // contains a defer statement
funcNilCheckDisabled // disable nil checks when compiling this function
funcInlinabilityChecked // inliner has already determined whether the function is inlinable
cmd/compile: don't export unreachable inline method bodies
Previously, anytime we exported a function or method declaration
(which includes methods for every type transitively exported), we
included the inline function bodies, if any. However, in many cases,
it's impossible (or at least very unlikely) for the importing package
to call the method.
For example:
package p
type T int
func (t T) M() { t.u() }
func (t T) u() {}
func (t T) v() {}
T.M and T.u are inlineable, and they're both reachable through calls
to T.M, which is exported. However, t.v is also inlineable, but cannot
be reached.
Exception: if p.T is embedded in another type q.U, p.T.v will be
promoted to q.U.v, and the generated wrapper function could have
inlined the call to p.T.v. However, in practice, this doesn't happen,
and a missed inlining opportunity doesn't affect correctness.
To implement this, this CL introduces an extra flood fill pass before
exporting to mark inline bodies that are actually reachable, so the
exporter can skip over methods like t.v.
This reduces Kubernetes build time (as measured by "time go build -a
k8s.io/kubernetes/cmd/...") on an HP Z620 measurably:
== before ==
real 0m44.658s
user 11m19.136s
sys 0m53.844s
== after ==
real 0m41.702s
user 10m29.732s
sys 0m50.908s
It also significantly cuts down the cost of enabling mid-stack
inlining (-l=4):
== before (-l=4) ==
real 1m19.236s
user 20m6.528s
sys 1m17.328s
== after (-l=4) ==
real 0m59.100s
user 13m12.808s
sys 0m58.776s
Updates #19348.
Change-Id: Iade58233ca42af823a1630517a53848b5d3c7a7e
Reviewed-on: https://go-review.googlesource.com/74110
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2017-10-27 15:36:59 -07:00
funcExportInline // include inline body in export data
2018-03-27 13:50:08 -07:00
funcInstrumentBody // add race/msan instrumentation during SSA construction
2017-02-27 19:56:38 +02:00
)
2017-09-18 14:54:10 -07:00
func ( f * Func ) Dupok ( ) bool { return f . flags & funcDupok != 0 }
func ( f * Func ) Wrapper ( ) bool { return f . flags & funcWrapper != 0 }
func ( f * Func ) Needctxt ( ) bool { return f . flags & funcNeedctxt != 0 }
func ( f * Func ) ReflectMethod ( ) bool { return f . flags & funcReflectMethod != 0 }
func ( f * Func ) IsHiddenClosure ( ) bool { return f . flags & funcIsHiddenClosure != 0 }
func ( f * Func ) HasDefer ( ) bool { return f . flags & funcHasDefer != 0 }
func ( f * Func ) NilCheckDisabled ( ) bool { return f . flags & funcNilCheckDisabled != 0 }
func ( f * Func ) InlinabilityChecked ( ) bool { return f . flags & funcInlinabilityChecked != 0 }
cmd/compile: don't export unreachable inline method bodies
Previously, anytime we exported a function or method declaration
(which includes methods for every type transitively exported), we
included the inline function bodies, if any. However, in many cases,
it's impossible (or at least very unlikely) for the importing package
to call the method.
For example:
package p
type T int
func (t T) M() { t.u() }
func (t T) u() {}
func (t T) v() {}
T.M and T.u are inlineable, and they're both reachable through calls
to T.M, which is exported. However, t.v is also inlineable, but cannot
be reached.
Exception: if p.T is embedded in another type q.U, p.T.v will be
promoted to q.U.v, and the generated wrapper function could have
inlined the call to p.T.v. However, in practice, this doesn't happen,
and a missed inlining opportunity doesn't affect correctness.
To implement this, this CL introduces an extra flood fill pass before
exporting to mark inline bodies that are actually reachable, so the
exporter can skip over methods like t.v.
This reduces Kubernetes build time (as measured by "time go build -a
k8s.io/kubernetes/cmd/...") on an HP Z620 measurably:
== before ==
real 0m44.658s
user 11m19.136s
sys 0m53.844s
== after ==
real 0m41.702s
user 10m29.732s
sys 0m50.908s
It also significantly cuts down the cost of enabling mid-stack
inlining (-l=4):
== before (-l=4) ==
real 1m19.236s
user 20m6.528s
sys 1m17.328s
== after (-l=4) ==
real 0m59.100s
user 13m12.808s
sys 0m58.776s
Updates #19348.
Change-Id: Iade58233ca42af823a1630517a53848b5d3c7a7e
Reviewed-on: https://go-review.googlesource.com/74110
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2017-10-27 15:36:59 -07:00
func ( f * Func ) ExportInline ( ) bool { return f . flags & funcExportInline != 0 }
2018-03-27 13:50:08 -07:00
func ( f * Func ) InstrumentBody ( ) bool { return f . flags & funcInstrumentBody != 0 }
2017-09-18 14:54:10 -07:00
func ( f * Func ) SetDupok ( b bool ) { f . flags . set ( funcDupok , b ) }
func ( f * Func ) SetWrapper ( b bool ) { f . flags . set ( funcWrapper , b ) }
func ( f * Func ) SetNeedctxt ( b bool ) { f . flags . set ( funcNeedctxt , b ) }
func ( f * Func ) SetReflectMethod ( b bool ) { f . flags . set ( funcReflectMethod , b ) }
func ( f * Func ) SetIsHiddenClosure ( b bool ) { f . flags . set ( funcIsHiddenClosure , b ) }
func ( f * Func ) SetHasDefer ( b bool ) { f . flags . set ( funcHasDefer , b ) }
func ( f * Func ) SetNilCheckDisabled ( b bool ) { f . flags . set ( funcNilCheckDisabled , b ) }
func ( f * Func ) SetInlinabilityChecked ( b bool ) { f . flags . set ( funcInlinabilityChecked , b ) }
cmd/compile: don't export unreachable inline method bodies
Previously, anytime we exported a function or method declaration
(which includes methods for every type transitively exported), we
included the inline function bodies, if any. However, in many cases,
it's impossible (or at least very unlikely) for the importing package
to call the method.
For example:
package p
type T int
func (t T) M() { t.u() }
func (t T) u() {}
func (t T) v() {}
T.M and T.u are inlineable, and they're both reachable through calls
to T.M, which is exported. However, t.v is also inlineable, but cannot
be reached.
Exception: if p.T is embedded in another type q.U, p.T.v will be
promoted to q.U.v, and the generated wrapper function could have
inlined the call to p.T.v. However, in practice, this doesn't happen,
and a missed inlining opportunity doesn't affect correctness.
To implement this, this CL introduces an extra flood fill pass before
exporting to mark inline bodies that are actually reachable, so the
exporter can skip over methods like t.v.
This reduces Kubernetes build time (as measured by "time go build -a
k8s.io/kubernetes/cmd/...") on an HP Z620 measurably:
== before ==
real 0m44.658s
user 11m19.136s
sys 0m53.844s
== after ==
real 0m41.702s
user 10m29.732s
sys 0m50.908s
It also significantly cuts down the cost of enabling mid-stack
inlining (-l=4):
== before (-l=4) ==
real 1m19.236s
user 20m6.528s
sys 1m17.328s
== after (-l=4) ==
real 0m59.100s
user 13m12.808s
sys 0m58.776s
Updates #19348.
Change-Id: Iade58233ca42af823a1630517a53848b5d3c7a7e
Reviewed-on: https://go-review.googlesource.com/74110
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2017-10-27 15:36:59 -07:00
func ( f * Func ) SetExportInline ( b bool ) { f . flags . set ( funcExportInline , b ) }
2018-03-27 13:50:08 -07:00
func ( f * Func ) SetInstrumentBody ( b bool ) { f . flags . set ( funcInstrumentBody , b ) }
2017-02-27 19:56:38 +02:00
2017-10-24 17:10:02 -04:00
func ( f * Func ) setWBPos ( pos src . XPos ) {
if Debug_wb != 0 {
Warnl ( pos , "write barrier" )
}
if ! f . WBPos . IsKnown ( ) {
f . WBPos = pos
}
}
2017-11-09 23:10:43 +00:00
//go:generate stringer -type=Op -trimprefix=O
2015-09-24 23:21:18 +02:00
type Op uint8
2015-03-05 10:45:56 -05:00
// Node ops.
const (
2017-11-09 23:10:43 +00:00
OXXX Op = iota
2015-03-05 13:57:36 -05:00
// names
ONAME // var, const or func name
ONONAME // unnamed arg or return value: f(int, string) (int, error) { etc }
OTYPE // type name
OPACK // import
OLITERAL // literal
// expressions
2015-06-03 23:57:59 -04:00
OADD // Left + Right
OSUB // Left - Right
OOR // Left | Right
OXOR // Left ^ Right
2015-10-22 18:56:45 -07:00
OADDSTR // +{List} (string addition, list elements are strings)
2015-06-03 23:57:59 -04:00
OADDR // &Left
OANDAND // Left && Right
2017-03-27 14:48:24 -07:00
OAPPEND // append(List); after walk, Left may contain elem type descriptor
2015-06-03 23:57:59 -04:00
OARRAYBYTESTR // Type(Left) (Type is string, Left is a []byte)
OARRAYBYTESTRTMP // Type(Left) (Type is string, Left is a []byte, ephemeral)
OARRAYRUNESTR // Type(Left) (Type is string, Left is a []rune)
OSTRARRAYBYTE // Type(Left) (Type is []byte, Left is a string)
OSTRARRAYBYTETMP // Type(Left) (Type is []byte, Left is a string, ephemeral)
OSTRARRAYRUNE // Type(Left) (Type is []rune, Left is a string)
OAS // Left = Right or (if Colas=true) Left := Right
OAS2 // List = Rlist (x, y, z = a, b, c)
OAS2FUNC // List = Rlist (x, y = f())
OAS2RECV // List = Rlist (x, ok = <-c)
OAS2MAPR // List = Rlist (x, ok = m["foo"])
OAS2DOTTYPE // List = Rlist (x, ok = I.(int))
OASOP // Left Etype= Right (x += y)
OCALL // Left(List) (function call, method call or type conversion)
cmd/compile: move argument stack construction to SSA generation
The goal of this change is to move work from walk to SSA,
and simplify things along the way.
This is hard to accomplish cleanly with small incremental changes,
so this large commit message aims to provide a roadmap to the diff.
High level description:
Prior to this change, walk was responsible for constructing (most of) the stack for function calls.
ascompatte gathered variadic arguments into a slice.
It also rewrote n.List from a list of arguments to a list of assignments to stack slots.
ascompatte was called multiple times to handle the receiver in a method call.
reorder1 then introduced temporaries into n.List as needed to avoid smashing the stack.
adjustargs then made extra stack space for go/defer args as needed.
Node to SSA construction evaluated all the statements in n.List,
and issued the function call, assuming that the stack was correctly constructed.
Intrinsic calls had to dig around inside n.List to extract the arguments,
since intrinsics don't use the stack to make function calls.
This change moves stack construction to the SSA construction phase.
ascompatte, now called walkParams, does all the work that ascompatte and reorder1 did.
It handles variadic arguments, inserts the method receiver if needed, and allocates temporaries.
It does not, however, make any assignments to stack slots.
Instead, it moves the function arguments to n.Rlist, leaving assignments to temporaries in n.List.
(It would be better to use Ninit instead of List; future work.)
During SSA construction, after doing all the temporary assignments in n.List,
the function arguments are assigned to stack slots by
constructing the appropriate SSA Value, using (*state).storeArg.
SSA construction also now handles adjustments for go/defer args.
This change also simplifies intrinsic calls, since we no longer need to undo walk's work.
Along the way, we simplify nodarg by pushing the fp==1 case to its callers, where it fits nicely.
Generated code differences:
There were a few optimizations applied along the way, the old way.
f(g()) was rewritten to do a block copy of function results to function arguments.
And reorder1 avoided introducing the final "save the stack" temporary in n.List.
The f(g()) block copy optimization never actually triggered; the order pass rewrote away g(), so that has been removed.
SSA optimizations mostly obviated the need for reorder1's optimization of avoiding the final temporary.
The exception was when the temporary's type was not SSA-able;
in that case, we got a Move into an autotmp and then an immediate Move onto the stack,
with the autotmp never read or used again.
This change introduces a new rewrite rule to detect such pointless double Moves
and collapse them into a single Move.
This is actually more powerful than the original optimization,
since the original optimization relied on the imprecise Node.HasCall calculation.
The other significant difference in the generated code is that the stack is now constructed
completely in SP-offset order. Prior to this change, the stack was constructed somewhat
haphazardly: first the final argument that Node.HasCall deemed to require a temporary,
then other arguments, then the method receiver, then the defer/go args.
SP-offset is probably a good default order. See future work.
There are a few minor object file size changes as a result of this change.
I investigated some regressions in early versions of this change.
One regression (in archive/tar) was the addition of a single CMPQ instruction,
which would be eliminated were this TODO from flagalloc to be done:
// TODO: Remove original instructions if they are never used.
One regression (in text/template) was an ADDQconstmodify that is now
a regular MOVQLoad+ADDQconst+MOVQStore, due to an unlucky change
in the order in which arguments are written. The argument change
order can also now be luckier, so this appears to be a wash.
All in all, though there will be minor winners and losers,
this change appears to be performance neutral.
Future work:
Move loading the result of function calls to SSA construction; eliminate OINDREGSP.
Consider pushing stack construction deeper into SSA world, perhaps in an arch-specific pass.
Among other benefits, this would make it easier to transition to a new calling convention.
This would require rethinking the handling of stack conflicts and is non-trivial.
Figure out some clean way to indicate that stack construction Stores/Moves
do not alias each other, so that subsequent passes may do things like
CSE+tighten shared stack setup, do DSE using non-first Stores, etc.
This would allow us to eliminate the minor text/template regression.
Possibly make assignments to stack slots not treated as statements by DWARF.
Compiler benchmarks:
name old time/op new time/op delta
Template 182ms ± 2% 179ms ± 2% -1.69% (p=0.000 n=47+48)
Unicode 86.3ms ± 5% 85.1ms ± 4% -1.36% (p=0.001 n=50+50)
GoTypes 646ms ± 1% 642ms ± 1% -0.63% (p=0.000 n=49+48)
Compiler 2.89s ± 1% 2.86s ± 2% -1.36% (p=0.000 n=48+50)
SSA 8.47s ± 1% 8.37s ± 2% -1.22% (p=0.000 n=47+50)
Flate 122ms ± 2% 121ms ± 2% -0.66% (p=0.000 n=47+45)
GoParser 147ms ± 2% 146ms ± 2% -0.53% (p=0.006 n=46+49)
Reflect 406ms ± 2% 403ms ± 2% -0.76% (p=0.000 n=48+43)
Tar 162ms ± 3% 162ms ± 4% ~ (p=0.191 n=46+50)
XML 223ms ± 2% 222ms ± 2% -0.37% (p=0.031 n=45+49)
[Geo mean] 382ms 378ms -0.89%
name old user-time/op new user-time/op delta
Template 219ms ± 3% 216ms ± 3% -1.56% (p=0.000 n=50+48)
Unicode 109ms ± 6% 109ms ± 5% ~ (p=0.190 n=50+49)
GoTypes 836ms ± 2% 828ms ± 2% -0.96% (p=0.000 n=49+48)
Compiler 3.87s ± 2% 3.80s ± 1% -1.81% (p=0.000 n=49+46)
SSA 12.0s ± 1% 11.8s ± 1% -2.01% (p=0.000 n=48+50)
Flate 142ms ± 3% 141ms ± 3% -0.85% (p=0.003 n=50+48)
GoParser 178ms ± 4% 175ms ± 4% -1.66% (p=0.000 n=48+46)
Reflect 520ms ± 2% 512ms ± 2% -1.44% (p=0.000 n=45+48)
Tar 200ms ± 3% 198ms ± 4% -0.61% (p=0.037 n=47+50)
XML 277ms ± 3% 275ms ± 3% -0.85% (p=0.000 n=49+48)
[Geo mean] 482ms 476ms -1.23%
name old alloc/op new alloc/op delta
Template 36.1MB ± 0% 35.3MB ± 0% -2.18% (p=0.008 n=5+5)
Unicode 29.8MB ± 0% 29.3MB ± 0% -1.58% (p=0.008 n=5+5)
GoTypes 125MB ± 0% 123MB ± 0% -2.13% (p=0.008 n=5+5)
Compiler 531MB ± 0% 513MB ± 0% -3.40% (p=0.008 n=5+5)
SSA 2.00GB ± 0% 1.93GB ± 0% -3.34% (p=0.008 n=5+5)
Flate 24.5MB ± 0% 24.3MB ± 0% -1.18% (p=0.008 n=5+5)
GoParser 29.4MB ± 0% 28.7MB ± 0% -2.34% (p=0.008 n=5+5)
Reflect 87.1MB ± 0% 86.0MB ± 0% -1.33% (p=0.008 n=5+5)
Tar 35.3MB ± 0% 34.8MB ± 0% -1.44% (p=0.008 n=5+5)
XML 47.9MB ± 0% 47.1MB ± 0% -1.86% (p=0.008 n=5+5)
[Geo mean] 82.8MB 81.1MB -2.08%
name old allocs/op new allocs/op delta
Template 352k ± 0% 347k ± 0% -1.32% (p=0.008 n=5+5)
Unicode 342k ± 0% 339k ± 0% -0.66% (p=0.008 n=5+5)
GoTypes 1.29M ± 0% 1.27M ± 0% -1.30% (p=0.008 n=5+5)
Compiler 4.98M ± 0% 4.87M ± 0% -2.14% (p=0.008 n=5+5)
SSA 15.7M ± 0% 15.2M ± 0% -2.86% (p=0.008 n=5+5)
Flate 233k ± 0% 231k ± 0% -0.83% (p=0.008 n=5+5)
GoParser 296k ± 0% 291k ± 0% -1.54% (p=0.016 n=5+4)
Reflect 1.05M ± 0% 1.04M ± 0% -0.65% (p=0.008 n=5+5)
Tar 343k ± 0% 339k ± 0% -0.97% (p=0.008 n=5+5)
XML 432k ± 0% 426k ± 0% -1.19% (p=0.008 n=5+5)
[Geo mean] 815k 804k -1.35%
name old object-bytes new object-bytes delta
Template 505kB ± 0% 505kB ± 0% -0.01% (p=0.008 n=5+5)
Unicode 224kB ± 0% 224kB ± 0% ~ (all equal)
GoTypes 1.82MB ± 0% 1.83MB ± 0% +0.06% (p=0.008 n=5+5)
Flate 324kB ± 0% 324kB ± 0% +0.00% (p=0.008 n=5+5)
GoParser 402kB ± 0% 402kB ± 0% +0.04% (p=0.008 n=5+5)
Reflect 1.39MB ± 0% 1.39MB ± 0% -0.01% (p=0.008 n=5+5)
Tar 449kB ± 0% 449kB ± 0% -0.02% (p=0.008 n=5+5)
XML 598kB ± 0% 597kB ± 0% -0.05% (p=0.008 n=5+5)
Change-Id: Ifc9d5c1bd01f90171414b8fb18ffe2290d271143
Reviewed-on: https://go-review.googlesource.com/c/114797
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2018-05-06 12:58:53 -07:00
// OCALLFUNC, OCALLMETH, and OCALLINTER have the same structure.
// Prior to walk, they are: Left(List), where List is all regular arguments.
// If present, Right is an ODDDARG that holds the
// generated slice used in a call to a variadic function.
// After walk, List is a series of assignments to temporaries,
// and Rlist is an updated set of arguments, including any ODDDARG slice.
// TODO(josharian/khr): Use Ninit instead of List for the assignments to temporaries. See CL 114797.
OCALLFUNC // Left(List/Rlist) (function call f(args))
OCALLMETH // Left(List/Rlist) (direct method call x.Method(args))
OCALLINTER // Left(List/Rlist) (interface method call x.Method(args))
OCALLPART // Left.Right (method expression x.Method, not called)
OCAP // cap(Left)
OCLOSE // close(Left)
OCLOSURE // func Type { Body } (func literal)
OCOMPLIT // Right{List} (composite literal, not yet lowered to specific form)
OMAPLIT // Type{List} (composite literal, Type is map)
OSTRUCTLIT // Type{List} (composite literal, Type is struct)
OARRAYLIT // Type{List} (composite literal, Type is array)
OSLICELIT // Type{List} (composite literal, Type is slice)
OPTRLIT // &Left (left is composite literal)
OCONV // Type(Left) (type conversion)
OCONVIFACE // Type(Left) (type conversion, to interface)
OCONVNOP // Type(Left) (type conversion, no effect)
OCOPY // copy(Left, Right)
ODCL // var Left (declares Left of type Left.Type)
2015-06-03 23:57:59 -04:00
// Used during parsing but don't last.
ODCLFUNC // func f() or func (r) f()
ODCLFIELD // struct field, interface field, or func/method argument/return value.
ODCLCONST // const pi = 3.14
2017-01-11 11:24:35 -08:00
ODCLTYPE // type Int int or type Int = int
2015-06-03 23:57:59 -04:00
ODELETE // delete(Left, Right)
cmd/compile: change ODOT and friends to use Sym, not Right
The Node type ODOT and its variants all represent a selector, with a
simple name to the right of the dot. Before this change this was
represented by using an ONAME Node in the Right field. This ONAME node
served no useful purpose. This CL changes these Node types to store the
symbol in the Sym field instead, thus not requiring allocating a Node
for each selector.
When compiling x/tools/go/types this CL eliminates nearly 5000 calls to
newname and reduces the total number of Nodes allocated by about 6.6%.
It seems to cut compilation time by 1 to 2 percent.
Getting this right was somewhat subtle, and I added two dubious changes
to produce the exact same output as before. One is to ishairy in
inl.go: the ONAME node increased the cost of ODOT and friends by 1, and
I retained that, although really ODOT is not more expensive than any
other node. The other is to varexpr in walk.go: because the ONAME in
the Right field of an ODOT has no class, varexpr would always return
false for an ODOT, although in fact for some ODOT's it seemingly ought
to return true; I added an && false for now. I will send separate CLs,
that will break toolstash -cmp, to clean these up.
This CL passes toolstash -cmp.
Change-Id: I4af8a10cc59078c436130ce472f25abc3a9b2f80
Reviewed-on: https://go-review.googlesource.com/20890
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2016-03-18 16:52:30 -07:00
ODOT // Left.Sym (Left is of struct type)
ODOTPTR // Left.Sym (Left is of pointer to struct type)
ODOTMETH // Left.Sym (Left is non-interface, Right is method name)
ODOTINTER // Left.Sym (Left is interface, Right is method name)
OXDOT // Left.Sym (before rewrite to one of the preceding)
2017-03-27 14:48:24 -07:00
ODOTTYPE // Left.Right or Left.Type (.Right during parsing, .Type once resolved); after walk, .Right contains address of interface type descriptor and .Right.Right contains address of concrete type descriptor
ODOTTYPE2 // Left.Right or Left.Type (.Right during parsing, .Type once resolved; on rhs of OAS2DOTTYPE); after walk, .Right contains address of interface type descriptor
2015-06-03 23:57:59 -04:00
OEQ // Left == Right
ONE // Left != Right
OLT // Left < Right
OLE // Left <= Right
OGE // Left >= Right
OGT // Left > Right
OIND // *Left
OINDEX // Left[Right] (index of array or slice)
OINDEXMAP // Left[Right] (index of map)
2016-10-27 02:02:30 -07:00
OKEY // Left:Right (key:value in struct/array/map literal)
2016-10-12 17:39:28 -07:00
OSTRUCTKEY // Sym:Left (key:value in struct literal, after type checking)
2015-06-03 23:57:59 -04:00
OLEN // len(Left)
OMAKE // make(List) (before type checking converts to one of the following)
OMAKECHAN // make(Type, Left) (type is chan)
OMAKEMAP // make(Type, Left) (type is map)
OMAKESLICE // make(Type, Left, Right) (type is slice)
OMUL // Left * Right
ODIV // Left / Right
OMOD // Left % Right
OLSH // Left << Right
ORSH // Left >> Right
OAND // Left & Right
OANDNOT // Left &^ Right
ONEW // new(Left)
ONOT // !Left
OCOM // ^Left
OPLUS // +Left
OMINUS // -Left
OOROR // Left || Right
OPANIC // panic(Left)
OPRINT // print(List)
OPRINTN // println(List)
OPAREN // (Left)
OSEND // Left <- Right
2016-04-21 19:35:26 -07:00
OSLICE // Left[List[0] : List[1]] (Left is untypechecked or slice)
OSLICEARR // Left[List[0] : List[1]] (Left is array)
OSLICESTR // Left[List[0] : List[1]] (Left is string)
OSLICE3 // Left[List[0] : List[1] : List[2]] (Left is untypedchecked or slice)
OSLICE3ARR // Left[List[0] : List[1] : List[2]] (Left is array)
2015-06-03 23:57:59 -04:00
ORECOVER // recover()
ORECV // <-Left
ORUNESTR // Type(Left) (Type is string, Left is rune)
OSELRECV // Left = <-Right.Left: (appears as .Left of OCASE; Right.Op == ORECV)
OSELRECV2 // List = <-Right.Left: (apperas as .Left of OCASE; count(List) == 2, Right.Op == ORECV)
OIOTA // iota
OREAL // real(Left)
OIMAG // imag(Left)
OCOMPLEX // complex(Left, Right)
2016-10-18 14:17:05 -07:00
OALIGNOF // unsafe.Alignof(Left)
OOFFSETOF // unsafe.Offsetof(Left)
OSIZEOF // unsafe.Sizeof(Left)
2015-03-05 13:57:36 -05:00
// statements
2015-06-03 23:57:59 -04:00
OBLOCK // { List } (block of code)
2015-03-05 13:57:36 -05:00
OBREAK // break
cmd/compile: recognize integer ranges in switch statements
Consider a switch statement like:
switch x {
case 1:
// ...
case 2, 3, 4, 5, 6:
// ...
case 5:
// ...
}
Prior to this CL, the generated code treated
2, 3, 4, 5, and 6 independently in a binary search.
With this CL, the generated code checks whether
2 <= x && x <= 6.
walkinrange then optimizes that range check
into a single unsigned comparison.
Experiments suggest that the best min range size
is 2, using binary size as a proxy for optimization.
Binary sizes before/after this CL:
cmd/compile: 14209728 / 14165360
cmd/go: 9543100 / 9539004
Change-Id: If2f7fb97ca80468fa70351ef540866200c4c996c
Reviewed-on: https://go-review.googlesource.com/26770
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-06-17 16:27:23 -07:00
OCASE // case Left or List[0]..List[1]: Nbody (select case after processing; Left==nil and List==nil means default)
2015-06-03 23:57:59 -04:00
OXCASE // case List: Nbody (select case before processing; List==nil means default)
2015-03-05 13:57:36 -05:00
OCONTINUE // continue
2015-06-03 23:57:59 -04:00
ODEFER // defer Left (Left must be call)
OEMPTY // no-op (empty statement)
2017-09-01 14:55:15 -07:00
OFALL // fallthrough
2015-06-03 23:57:59 -04:00
OFOR // for Ninit; Left; Right { Nbody }
cmd/compile: don't produce a past-the-end pointer in range loops
Currently, range loops over slices and arrays are compiled roughly
like:
for i, x := range s { b }
⇓
for i, _n, _p := 0, len(s), &s[0]; i < _n; i, _p = i+1, _p + unsafe.Sizeof(s[0]) { b }
⇓
i, _n, _p := 0, len(s), &s[0]
goto cond
body:
{ b }
i, _p = i+1, _p + unsafe.Sizeof(s[0])
cond:
if i < _n { goto body } else { goto end }
end:
The problem with this lowering is that _p may temporarily point past
the end of the allocation the moment before the loop terminates. Right
now this isn't a problem because there's never a safe-point during
this brief moment.
We're about to introduce safe-points everywhere, so this bad pointer
is going to be a problem. We could mark the increment as an unsafe
block, but this inhibits reordering opportunities and could result in
infrequent safe-points if the body is short.
Instead, this CL fixes this by changing how we compile range loops to
never produce this past-the-end pointer. It changes the lowering to
roughly:
i, _n, _p := 0, len(s), &s[0]
if i < _n { goto body } else { goto end }
top:
_p += unsafe.Sizeof(s[0])
body:
{ b }
i++
if i < _n { goto top } else { goto end }
end:
Notably, the increment is split into two parts: we increment the index
before checking the condition, but increment the pointer only *after*
the condition check has succeeded.
The implementation builds on the OFORUNTIL construct that was
introduced during the loop preemption experiments, since OFORUNTIL
places the increment and condition after the loop body. To support the
extra "late increment" step, we further define OFORUNTIL's "List"
field to contain the late increment statements. This makes all of this
a relatively small change.
This depends on the improvements to the prove pass in CL 102603. With
the current lowering, bounds-check elimination knows that i < _n in
the body because the body block is dominated by the cond block. In the
new lowering, deriving this fact requires detecting that i < _n on
*both* paths into body and hence is true in body. CL 102603 made prove
able to detect this.
The code size effect of this is minimal. The cmd/go binary on
linux/amd64 increases by 0.17%. Performance-wise, this actually
appears to be a net win, though it's mostly noise:
name old time/op new time/op delta
BinaryTree17-12 2.80s ± 0% 2.61s ± 1% -6.88% (p=0.000 n=20+18)
Fannkuch11-12 2.41s ± 0% 2.42s ± 0% +0.05% (p=0.005 n=20+20)
FmtFprintfEmpty-12 41.6ns ± 5% 41.4ns ± 6% ~ (p=0.765 n=20+19)
FmtFprintfString-12 69.4ns ± 3% 69.3ns ± 1% ~ (p=0.084 n=19+17)
FmtFprintfInt-12 76.1ns ± 1% 77.3ns ± 1% +1.57% (p=0.000 n=19+19)
FmtFprintfIntInt-12 122ns ± 2% 123ns ± 3% +0.95% (p=0.015 n=20+20)
FmtFprintfPrefixedInt-12 153ns ± 2% 151ns ± 3% -1.27% (p=0.013 n=20+20)
FmtFprintfFloat-12 215ns ± 0% 216ns ± 0% +0.47% (p=0.000 n=20+16)
FmtManyArgs-12 486ns ± 1% 498ns ± 0% +2.40% (p=0.000 n=20+17)
GobDecode-12 6.43ms ± 0% 6.50ms ± 0% +1.08% (p=0.000 n=18+19)
GobEncode-12 5.43ms ± 1% 5.47ms ± 0% +0.76% (p=0.000 n=20+20)
Gzip-12 218ms ± 1% 218ms ± 1% ~ (p=0.883 n=20+20)
Gunzip-12 38.8ms ± 0% 38.9ms ± 0% ~ (p=0.644 n=19+19)
HTTPClientServer-12 76.2µs ± 1% 76.4µs ± 2% ~ (p=0.218 n=20+20)
JSONEncode-12 12.2ms ± 0% 12.3ms ± 1% +0.45% (p=0.000 n=19+19)
JSONDecode-12 54.2ms ± 1% 53.3ms ± 0% -1.67% (p=0.000 n=20+20)
Mandelbrot200-12 3.71ms ± 0% 3.71ms ± 0% ~ (p=0.143 n=19+20)
GoParse-12 3.22ms ± 0% 3.19ms ± 1% -0.72% (p=0.000 n=20+20)
RegexpMatchEasy0_32-12 76.7ns ± 1% 75.8ns ± 1% -1.19% (p=0.000 n=20+17)
RegexpMatchEasy0_1K-12 245ns ± 1% 243ns ± 0% -0.72% (p=0.000 n=18+17)
RegexpMatchEasy1_32-12 71.9ns ± 0% 71.7ns ± 1% -0.39% (p=0.006 n=12+18)
RegexpMatchEasy1_1K-12 358ns ± 1% 354ns ± 1% -1.13% (p=0.000 n=20+19)
RegexpMatchMedium_32-12 105ns ± 2% 105ns ± 1% -0.63% (p=0.007 n=19+20)
RegexpMatchMedium_1K-12 31.9µs ± 1% 31.9µs ± 1% ~ (p=1.000 n=17+17)
RegexpMatchHard_32-12 1.51µs ± 1% 1.52µs ± 2% +0.46% (p=0.042 n=18+18)
RegexpMatchHard_1K-12 45.3µs ± 1% 45.5µs ± 2% +0.44% (p=0.029 n=18+19)
Revcomp-12 388ms ± 1% 385ms ± 0% -0.57% (p=0.000 n=19+18)
Template-12 63.0ms ± 1% 63.3ms ± 0% +0.50% (p=0.000 n=19+20)
TimeParse-12 309ns ± 1% 307ns ± 0% -0.62% (p=0.000 n=20+20)
TimeFormat-12 328ns ± 0% 333ns ± 0% +1.35% (p=0.000 n=19+19)
[Geo mean] 47.0µs 46.9µs -0.20%
(https://perf.golang.org/search?q=upload:20180326.1)
For #10958.
For #24543.
Change-Id: Icbd52e711fdbe7938a1fea3e6baca1104b53ac3a
Reviewed-on: https://go-review.googlesource.com/102604
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2018-03-22 12:04:51 -04:00
// OFORUNTIL is like OFOR, but the test (Left) is applied after the body:
// Ninit
// top: { Nbody } // Execute the body at least once
// cont: Right
// if Left { // And then test the loop condition
// List // Before looping to top, execute List
// goto top
// }
// OFORUNTIL is created by walk. There's no way to write this in Go code.
OFORUNTIL
OGOTO // goto Left
OIF // if Ninit; Left { Nbody } else { Rlist }
OLABEL // Left:
OPROC // go Left (Left must be call)
ORANGE // for List = range Right { Nbody }
ORETURN // return List
OSELECT // select { List } (List is list of OXCASE or OCASE)
OSWITCH // switch Ninit; Left { List } (List is a list of OXCASE or OCASE)
OTYPESW // Left = Right.(type) (appears as .Left of OSWITCH)
2015-03-05 13:57:36 -05:00
// types
OTCHAN // chan int
OTMAP // map[string]int
OTSTRUCT // struct{}
OTINTER // interface{}
OTFUNC // func()
OTARRAY // []int, [8]int, [N]int or [...]int
// misc
ODDD // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}.
ODDDARG // func f(args ...int), introduced by escape analysis.
OINLCALL // intermediary representation of an inlined call.
OEFACE // itable and data words of an empty-interface value.
OITAB // itable word of an interface value.
2016-10-25 06:46:51 -07:00
OIDATA // data word of an interface value in Left
2015-03-05 13:57:36 -05:00
OSPTR // base pointer of a slice or string.
OCLOSUREVAR // variable reference at beginning of closure function
OCFUNC // reference to c function pointer (not go func value)
OCHECKNIL // emit code to ensure pointer/interface not nil
2018-09-07 14:55:09 -07:00
OVARDEF // variable is about to be fully initialized
2015-03-05 13:57:36 -05:00
OVARKILL // variable is dead
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
OVARLIVE // variable is alive
2016-10-24 14:33:22 -07:00
OINDREGSP // offset plus indirect of REGSP, such as 8(SP).
2015-03-05 13:57:36 -05:00
2015-04-01 16:02:34 -04:00
// arch-specific opcodes
2015-03-05 13:57:36 -05:00
ORETJMP // return to other function
2015-04-03 12:23:28 -04:00
OGETG // runtime.getg() (read g pointer)
2015-03-05 13:57:36 -05:00
2015-03-05 10:45:56 -05:00
OEND
)
2016-02-26 14:28:48 -08:00
// Nodes is a pointer to a slice of *Node.
// For fields that are not used in most nodes, this is used instead of
// a slice to save space.
type Nodes struct { slice * [ ] * Node }
2018-04-04 15:53:27 -07:00
// asNodes returns a slice of *Node as a Nodes value.
func asNodes ( s [ ] * Node ) Nodes {
return Nodes { & s }
}
2016-02-26 14:28:48 -08:00
// Slice returns the entries in Nodes as a slice.
// Changes to the slice entries (as in s[i] = n) will be reflected in
// the Nodes.
2016-03-23 15:27:23 -07:00
func ( n Nodes ) Slice ( ) [ ] * Node {
2016-02-26 14:28:48 -08:00
if n . slice == nil {
return nil
}
return * n . slice
}
2016-03-08 15:10:26 -08:00
// Len returns the number of entries in Nodes.
2016-03-23 15:27:23 -07:00
func ( n Nodes ) Len ( ) int {
2016-03-08 15:10:26 -08:00
if n . slice == nil {
return 0
}
return len ( * n . slice )
}
2016-03-09 12:39:36 -08:00
// Index returns the i'th element of Nodes.
// It panics if n does not have at least i+1 elements.
2016-03-23 15:27:23 -07:00
func ( n Nodes ) Index ( i int ) * Node {
2016-03-09 12:39:36 -08:00
return ( * n . slice ) [ i ]
}
// First returns the first element of Nodes (same as n.Index(0)).
// It panics if n has no elements.
2016-03-23 15:27:23 -07:00
func ( n Nodes ) First ( ) * Node {
2016-03-08 15:10:26 -08:00
return ( * n . slice ) [ 0 ]
}
2016-03-09 12:39:36 -08:00
// Second returns the second element of Nodes (same as n.Index(1)).
// It panics if n has fewer than two elements.
2016-03-23 15:27:23 -07:00
func ( n Nodes ) Second ( ) * Node {
2016-03-08 15:10:26 -08:00
return ( * n . slice ) [ 1 ]
}
2016-03-09 12:39:36 -08:00
// Set sets n to a slice.
2016-02-26 14:28:48 -08:00
// This takes ownership of the slice.
func ( n * Nodes ) Set ( s [ ] * Node ) {
if len ( s ) == 0 {
n . slice = nil
} else {
2016-03-28 14:12:10 -07:00
// Copy s and take address of t rather than s to avoid
// allocation in the case where len(s) == 0 (which is
// over 3x more common, dynamically, for make.bash).
t := s
n . slice = & t
2016-02-26 14:28:48 -08:00
}
}
2016-03-10 10:13:42 -08:00
// Set1 sets n to a slice containing a single node.
2017-02-19 15:57:58 +01:00
func ( n * Nodes ) Set1 ( n1 * Node ) {
n . slice = & [ ] * Node { n1 }
2016-03-10 10:13:42 -08:00
}
2016-05-29 11:16:13 -07:00
// Set2 sets n to a slice containing two nodes.
func ( n * Nodes ) Set2 ( n1 , n2 * Node ) {
n . slice = & [ ] * Node { n1 , n2 }
}
2017-02-19 15:57:58 +01:00
// Set3 sets n to a slice containing three nodes.
func ( n * Nodes ) Set3 ( n1 , n2 , n3 * Node ) {
n . slice = & [ ] * Node { n1 , n2 , n3 }
}
2016-03-09 12:39:36 -08:00
// MoveNodes sets n to the contents of n2, then clears n2.
func ( n * Nodes ) MoveNodes ( n2 * Nodes ) {
n . slice = n2 . slice
n2 . slice = nil
}
// SetIndex sets the i'th element of Nodes to node.
// It panics if n does not have at least i+1 elements.
2016-03-23 15:27:23 -07:00
func ( n Nodes ) SetIndex ( i int , node * Node ) {
2016-03-09 12:39:36 -08:00
( * n . slice ) [ i ] = node
}
2017-02-19 15:57:58 +01:00
// SetFirst sets the first element of Nodes to node.
// It panics if n does not have at least one elements.
func ( n Nodes ) SetFirst ( node * Node ) {
( * n . slice ) [ 0 ] = node
}
// SetSecond sets the second element of Nodes to node.
// It panics if n does not have at least two elements.
func ( n Nodes ) SetSecond ( node * Node ) {
( * n . slice ) [ 1 ] = node
}
2016-03-09 12:39:36 -08:00
// Addr returns the address of the i'th element of Nodes.
// It panics if n does not have at least i+1 elements.
2016-03-23 15:27:23 -07:00
func ( n Nodes ) Addr ( i int ) * * Node {
2016-03-09 12:39:36 -08:00
return & ( * n . slice ) [ i ]
}
2016-02-26 14:28:48 -08:00
// Append appends entries to Nodes.
func ( n * Nodes ) Append ( a ... * Node ) {
2016-09-14 13:19:20 -07:00
if len ( a ) == 0 {
return
}
2016-02-26 14:28:48 -08:00
if n . slice == nil {
2017-02-25 00:34:21 +01:00
s := make ( [ ] * Node , len ( a ) )
copy ( s , a )
n . slice = & s
return
2016-02-26 14:28:48 -08:00
}
2017-02-25 00:34:21 +01:00
* n . slice = append ( * n . slice , a ... )
2016-02-26 14:28:48 -08:00
}
2016-02-27 14:31:33 -08:00
2016-09-14 13:19:20 -07:00
// Prepend prepends entries to Nodes.
// If a slice is passed in, this will take ownership of it.
func ( n * Nodes ) Prepend ( a ... * Node ) {
if len ( a ) == 0 {
return
}
if n . slice == nil {
n . slice = & a
} else {
* n . slice = append ( a , * n . slice ... )
}
}
2016-03-07 22:54:46 -08:00
// AppendNodes appends the contents of *n2 to n, then clears n2.
func ( n * Nodes ) AppendNodes ( n2 * Nodes ) {
switch {
case n2 . slice == nil :
case n . slice == nil :
n . slice = n2 . slice
default :
* n . slice = append ( * n . slice , * n2 . slice ... )
}
n2 . slice = nil
}
2017-10-19 11:23:12 -07:00
// inspect invokes f on each node in an AST in depth-first order.
// If f(n) returns false, inspect skips visiting n's children.
func inspect ( n * Node , f func ( * Node ) bool ) {
if n == nil || ! f ( n ) {
return
}
inspectList ( n . Ninit , f )
inspect ( n . Left , f )
inspect ( n . Right , f )
inspectList ( n . List , f )
inspectList ( n . Nbody , f )
inspectList ( n . Rlist , f )
}
func inspectList ( l Nodes , f func ( * Node ) bool ) {
for _ , n := range l . Slice ( ) {
inspect ( n , f )
}
}
cmd/compile: improve coverage of nowritebarrierrec check
The current go:nowritebarrierrec checker has two problems that limit
its coverage:
1. It doesn't understand that systemstack calls its argument, which
means there are several cases where we fail to detect prohibited write
barriers.
2. It only observes calls in the AST, so calls constructed during
lowering by SSA aren't followed.
This CL completely rewrites this checker to address these issues.
The current checker runs entirely after walk and uses visitBottomUp,
which introduces several problems for checking across systemstack.
First, visitBottomUp itself doesn't understand systemstack calls, so
the callee may be ordered after the caller, causing the checker to
fail to propagate constraints. Second, many systemstack calls are
passed a closure, which is quite difficult to resolve back to the
function definition after transformclosure and walk have run. Third,
visitBottomUp works exclusively on the AST, so it can't observe calls
created by SSA.
To address these problems, this commit splits the check into two
phases and rewrites it to use a call graph generated during SSA
lowering. The first phase runs before transformclosure/walk and simply
records systemstack arguments when they're easy to get. Then, it
modifies genssa to record static call edges at the point where we're
lowering to Progs (which is the latest point at which position
information is conveniently available). Finally, the second phase runs
after all functions have been lowered and uses a direct BFS walk of
the call graph (combining systemstack calls with static calls) to find
prohibited write barriers and construct nice error messages.
Fixes #22384.
For #22460.
Change-Id: I39668f7f2366ab3c1ab1a71eaf25484d25349540
Reviewed-on: https://go-review.googlesource.com/72773
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2017-10-22 16:36:27 -04:00
// nodeQueue is a FIFO queue of *Node. The zero value of nodeQueue is
// a ready-to-use empty queue.
type nodeQueue struct {
ring [ ] * Node
head , tail int
}
// empty returns true if q contains no Nodes.
func ( q * nodeQueue ) empty ( ) bool {
return q . head == q . tail
}
// pushRight appends n to the right of the queue.
func ( q * nodeQueue ) pushRight ( n * Node ) {
if len ( q . ring ) == 0 {
q . ring = make ( [ ] * Node , 16 )
} else if q . head + len ( q . ring ) == q . tail {
// Grow the ring.
nring := make ( [ ] * Node , len ( q . ring ) * 2 )
// Copy the old elements.
part := q . ring [ q . head % len ( q . ring ) : ]
if q . tail - q . head <= len ( part ) {
part = part [ : q . tail - q . head ]
copy ( nring , part )
} else {
pos := copy ( nring , part )
copy ( nring [ pos : ] , q . ring [ : q . tail % len ( q . ring ) ] )
}
q . ring , q . head , q . tail = nring , 0 , q . tail - q . head
}
q . ring [ q . tail % len ( q . ring ) ] = n
q . tail ++
}
// popLeft pops a node from the left of the queue. It panics if q is
// empty.
func ( q * nodeQueue ) popLeft ( ) * Node {
if q . empty ( ) {
panic ( "dequeue empty" )
}
n := q . ring [ q . head % len ( q . ring ) ]
q . head ++
return n
}