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
// 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.
// The same is true for Op=OTYPE and Op=OLITERAL.
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
2015-05-27 10:44:43 -04:00
Type * Type
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
// ONAME
2015-05-27 07:31:56 -04:00
Name * Name
2015-03-05 13:57:36 -05:00
2015-05-27 00:47:05 -04:00
Sym * Sym // various
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.
// - ODOT, ODOTPTR, and OINDREG use it to indicate offset relative to their base address.
// - OSTRUCTKEY uses it to store the named field's offset.
// - OXCASE and OXFALL use it to validate the use of fallthrough.
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
2015-05-27 00:44:05 -04:00
Lineno int32
2015-05-18 15:49:02 -07:00
// OREGISTER, OINDREG
Reg int16
2015-05-26 23:42:41 -04:00
Esc uint16 // EscXXX
2015-05-18 15:49:02 -07:00
2016-03-31 11:42:35 -07:00
Op Op
Ullman uint8 // sethi/ullman number
Addable bool // addressable
2016-10-11 08:36:38 -07:00
Etype EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg, ChanDir for OTCHAN, for OINDEXMAP 1=LHS,0=RHS
2016-03-31 11:42:35 -07:00
Bounded bool // bounds check unnecessary
2016-04-19 21:06:53 -07:00
NonNil bool // guaranteed to be non-nil
2016-03-31 11:42:35 -07:00
Class Class // PPARAM, PAUTO, PEXTERN, etc
Embedded uint8 // ODCLFIELD embedded type
Colas bool // OAS resulting from :=
Diag uint8 // already printed error about this
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
2016-08-18 09:14:22 -07:00
Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected
Typecheck uint8 // tracks state during typechecking; 2 == loop detected
2016-03-31 11:42:35 -07:00
Local bool
2016-05-16 14:23:12 -07:00
IsStatic bool // whether this Node will be converted to purely static data
2016-03-31 11:42:35 -07:00
Initorder uint8
2016-08-18 09:14:22 -07:00
Used bool // for variable/label declared and not used error
2016-03-31 11:42:35 -07:00
Isddd bool // is the argument variadic
Implicit bool
2016-04-21 19:28:28 -07:00
Addrtaken bool // address taken, even if not moved to heap
Assigned bool // is the variable ever assigned to
Likely int8 // likeliness of if statement
hasVal int8 // +1 for Val, -1 for Opt, 0 for not yet set
flags uint8 // TODO: store more bool fields in this flag field
}
const (
hasBreak = 1 << iota
2016-05-27 00:56:19 -04:00
isClosureVar
2016-06-18 19:40:57 -07:00
isOutputParamHeapAddr
2016-05-06 09:24:16 -07:00
noInline // used internally by inliner to indicate that a function call should not be inlined; set for OCALLFUNC and OCALLMETH only
2016-04-21 19:28:28 -07:00
)
func ( n * Node ) HasBreak ( ) bool {
return n . flags & hasBreak != 0
}
func ( n * Node ) SetHasBreak ( b bool ) {
if b {
n . flags |= hasBreak
} else {
n . flags &^= hasBreak
}
}
2016-05-27 00:56:19 -04:00
func ( n * Node ) isClosureVar ( ) bool {
return n . flags & isClosureVar != 0
2016-05-25 10:29:50 -04:00
}
2016-05-27 00:56:19 -04:00
func ( n * Node ) setIsClosureVar ( b bool ) {
2016-05-25 10:29:50 -04:00
if b {
2016-05-27 00:56:19 -04:00
n . flags |= isClosureVar
2016-05-25 10:29:50 -04:00
} else {
2016-05-27 00:56:19 -04:00
n . flags &^= isClosureVar
2016-05-25 10:29:50 -04:00
}
}
2016-05-06 09:24:16 -07:00
func ( n * Node ) noInline ( ) bool {
return n . flags & noInline != 0
}
func ( n * Node ) setNoInline ( b bool ) {
if b {
n . flags |= noInline
} else {
n . flags &^= noInline
}
}
2015-05-27 00:47:05 -04:00
2016-06-18 19:40:57 -07:00
func ( n * Node ) IsOutputParamHeapAddr ( ) bool {
return n . flags & isOutputParamHeapAddr != 0
}
func ( n * Node ) setIsOutputParamHeapAddr ( b bool ) {
if b {
n . flags |= isOutputParamHeapAddr
} else {
n . flags &^= isOutputParamHeapAddr
}
}
2015-05-27 00:47:05 -04:00
// Val returns the Val for the node.
func ( n * Node ) Val ( ) Val {
if n . hasVal != + 1 {
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 ) {
if n . hasVal == - 1 {
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
}
n . hasVal = + 1
n . E = v . U
}
// Opt returns the optimizer data for the node.
func ( n * Node ) Opt ( ) interface { } {
if n . hasVal != - 1 {
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 { } ) {
if x == nil && n . hasVal >= 0 {
return
}
if n . hasVal == + 1 {
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
}
n . hasVal = - 1
n . E = x
2015-03-10 21:37:13 -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
// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, ODCLFIELD, some OLITERAL).
2015-04-03 17:43:38 -07:00
type Name struct {
2016-05-27 14:24:26 -04:00
Pack * Node // real package for import . names
Pkg * Pkg // pkg for OPACK nodes
Heapaddr * Node // temp holding heap address of param (could move to Param?)
Inlvar * Node // ONAME substitute while inlining (could move to Param?)
Defn * Node // initializing assignment
Curfn * Node // function for local variables
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
Param * Param // additional fields for ONAME, ODCLFIELD
2016-05-27 14:24:26 -04:00
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.
Iota int32 // value if this name is iota
2015-05-27 00:44:05 -04:00
Funcdepth int32
Method bool // OCALLMETH name
2015-04-03 17:43:38 -07:00
Readonly bool
Captured bool // is the variable captured by a closure
Byval bool // is the variable captured by value or by reference
Needzero bool // if it contains pointers, needs to be zeroed on function entry
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
Keepalive bool // mark value live across unknown assembly call
2015-04-03 17:43:38 -07:00
}
2015-05-18 10:27:59 -07:00
type Param struct {
Ntype * Node
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
2015-05-26 21:49:31 -04:00
// ONAME PPARAM
2016-03-14 01:20:49 -07:00
Field * Field // TFIELD in arg struct
2015-05-26 21:49:31 -04: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
// - x1.isClosureVar() = false
//
// - xN.Defn = x1, N > 1
// - xN.isClosureVar() = true, N > 1
// - 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
2015-05-18 10:27:59 -07:00
}
2015-03-10 21:37:13 -07:00
// Func holds Node fields used only with function-like nodes.
type Func struct {
2016-10-03 16:09:13 -07:00
Shortname * Node
Enter Nodes // for example, allocate and initialize memory for escaping parameters
Exit Nodes
Cvars Nodes // closure params
Dcl [ ] * Node // autodcl for this func/closure
Inldcl Nodes // copy of dcl for use in inlining
Closgen int
Outerfunc * Node // outer function (for closure)
FieldTrack map [ * Sym ] struct { }
Ntype * Node // signature
Top int // top context (Ecall, Eproc, etc)
Closure * Node // OCLOSURE <-> ODCLFUNC
FCurfn * Node
Nname * Node
2015-03-10 21:37:13 -07:00
2016-02-27 14:31:33 -08:00
Inl Nodes // copy of the body for use in inlining
2015-03-10 21:37:13 -07:00
InlCost int32
2015-05-27 00:44:05 -04:00
Depth int32
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
2015-03-05 13:57:36 -05:00
Endlineno int32
2016-02-26 13:32:28 -08:00
WBLineno int32 // line number of first write barrier
2015-03-10 21:37:13 -07:00
2016-03-10 16:15:26 -05:00
Pragma Pragma // go:xxx function annotations
Dupok bool // duplicate definitions ok
Wrapper bool // is method wrapper
Needctxt bool // function uses context register (has closure variables)
ReflectMethod bool // function calls reflect.Type.Method or MethodByName
2015-03-05 10:45:56 -05:00
}
2015-09-24 23:21:18 +02:00
type Op uint8
2015-03-05 10:45:56 -05:00
// Node ops.
const (
2015-09-24 23:21:18 +02: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
OAPPEND // append(List)
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)
OASWB // Left = Right (with write barrier)
OCALL // Left(List) (function call, method call or type conversion)
OCALLFUNC // Left(List) (function call f(args))
OCALLMETH // Left(List) (direct method call x.Method(args))
OCALLINTER // Left(List) (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)
OCMPIFACE // Left Etype Right (interface comparison, x == y or x != y)
OCMPSTR // Left Etype Right (string comparison, x == y, x < y, etc)
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)
2016-06-19 07:20:28 -07:00
OARRAYLIT // Type{List} (composite literal, Type is array)
OSLICELIT // Type{List} (composite literal, Type is slice)
2015-06-03 23:57:59 -04:00
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)
// 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
ODCLTYPE // type Int int
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)
2015-06-03 23:57:59 -04:00
ODOTTYPE // Left.Right or Left.Type (.Right during parsing, .Type once resolved)
ODOTTYPE2 // Left.Right or Left.Type (.Right during parsing, .Type once resolved; on rhs of OAS2DOTTYPE)
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)
OKEY // Left:Right (key:value in struct/array/map literal, or slice index pair)
2016-06-06 12:38:19 -07:00
OIDATA // data word of an interface value in Left; TODO: move next to OITAB once it is easier to regenerate the binary blob in builtin.go (issues 15835, 15839)
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
OSLICE // Left[Right.Left : Right.Right] (Left is untypechecked or slice; Right.Op==OKEY)
OSLICEARR // Left[Right.Left : Right.Right] (Left is array)
OSLICESTR // Left[Right.Left : Right.Right] (Left is string)
OSLICE3 // Left[R.Left : R.R.Left : R.R.R] (R=Right; Left is untypedchecked or slice; R.Op and R.R.Op==OKEY)
OSLICE3ARR // Left[R.Left : R.R.Left : R.R.R] (R=Right; Left is array; R.Op and R.R.Op==OKEY)
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)
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)
OFALL // fallthrough (after processing)
OXFALL // fallthrough (before processing)
OFOR // for Ninit; Left; Right { Nbody }
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 // List = Left.(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.
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
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
2015-03-05 13:57:36 -05:00
// thearch-specific registers
OREGISTER // a register, such as AX.
OINDREG // offset plus indirect of a register, such as 8(SP).
2015-04-01 16:02:34 -04:00
// arch-specific opcodes
2015-03-05 13:57:36 -05:00
OCMP // compare: ACMP.
ODEC // decrement: ADEC.
OINC // increment: AINC.
OEXTEND // extend: ACWD/ACDQ/ACQO.
OHMUL // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both).
OLROT // left rotate: AROL.
ORROTC // right rotate-carry: ARCR.
ORETJMP // return to other function
2015-03-18 17:26:36 -04:00
OPS // compare parity set (for x86 NaN check)
cmd/internal/gc, cmd/6g: generate boolean values without jumps
Use SETcc instructions instead of Jcc to generate boolean values.
This generates shorter, jump-free code, which may in turn enable other
peephole optimizations.
For example, given
func f(i, j int) bool {
return i == j
}
Before
"".f t=1 size=32 value=0 args=0x18 locals=0x0
0x0000 00000 (x.go:3) TEXT "".f(SB), $0-24
0x0000 00000 (x.go:3) FUNCDATA $0, gclocals·b4c25e9b09fd0cf9bb429dcefe91c353(SB)
0x0000 00000 (x.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (x.go:4) MOVQ "".i+8(FP), BX
0x0005 00005 (x.go:4) MOVQ "".j+16(FP), BP
0x000a 00010 (x.go:4) CMPQ BX, BP
0x000d 00013 (x.go:4) JEQ 21
0x000f 00015 (x.go:4) MOVB $0, "".~r2+24(FP)
0x0014 00020 (x.go:4) RET
0x0015 00021 (x.go:4) MOVB $1, "".~r2+24(FP)
0x001a 00026 (x.go:4) JMP 20
After
"".f t=1 size=32 value=0 args=0x18 locals=0x0
0x0000 00000 (x.go:3) TEXT "".f(SB), $0-24
0x0000 00000 (x.go:3) FUNCDATA $0, gclocals·b4c25e9b09fd0cf9bb429dcefe91c353(SB)
0x0000 00000 (x.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (x.go:4) MOVQ "".i+8(FP), BX
0x0005 00005 (x.go:4) MOVQ "".j+16(FP), BP
0x000a 00010 (x.go:4) CMPQ BX, BP
0x000d 00013 (x.go:4) SETEQ "".~r2+24(FP)
0x0012 00018 (x.go:4) RET
regexp benchmarks, best of 12 runs:
benchmark old ns/op new ns/op delta
BenchmarkNotOnePassShortB 782 733 -6.27%
BenchmarkLiteral 180 171 -5.00%
BenchmarkNotLiteral 2855 2721 -4.69%
BenchmarkMatchHard_32 2672 2557 -4.30%
BenchmarkMatchHard_1K 80182 76732 -4.30%
BenchmarkMatchEasy1_32M 76440180 73304748 -4.10%
BenchmarkMatchEasy1_32K 68798 66350 -3.56%
BenchmarkAnchoredLongMatch 482 465 -3.53%
BenchmarkMatchEasy1_1M 2373042 2292692 -3.39%
BenchmarkReplaceAll 2776 2690 -3.10%
BenchmarkNotOnePassShortA 1397 1360 -2.65%
BenchmarkMatchClass_InRange 3842 3742 -2.60%
BenchmarkMatchEasy0_32 125 122 -2.40%
BenchmarkMatchEasy0_32K 11414 11164 -2.19%
BenchmarkMatchEasy0_1K 668 654 -2.10%
BenchmarkAnchoredShortMatch 260 255 -1.92%
BenchmarkAnchoredLiteralShortNonMatch 164 161 -1.83%
BenchmarkOnePassShortB 623 612 -1.77%
BenchmarkOnePassShortA 801 788 -1.62%
BenchmarkMatchClass 4094 4033 -1.49%
BenchmarkMatchEasy0_32M 14078800 13890704 -1.34%
BenchmarkMatchHard_32K 4095844 4045820 -1.22%
BenchmarkMatchEasy1_1K 1663 1643 -1.20%
BenchmarkMatchHard_1M 131261708 129708215 -1.18%
BenchmarkMatchHard_32M 4210112412 4169292003 -0.97%
BenchmarkMatchMedium_32K 2460752 2438611 -0.90%
BenchmarkMatchEasy0_1M 422914 419672 -0.77%
BenchmarkMatchMedium_1M 78581121 78040160 -0.69%
BenchmarkMatchMedium_32M 2515287278 2498464906 -0.67%
BenchmarkMatchMedium_32 1754 1746 -0.46%
BenchmarkMatchMedium_1K 52105 52106 +0.00%
BenchmarkAnchoredLiteralLongNonMatch 185 185 +0.00%
BenchmarkMatchEasy1_32 107 107 +0.00%
BenchmarkOnePassLongNotPrefix 505 505 +0.00%
BenchmarkOnePassLongPrefix 147 147 +0.00%
The godoc binary is ~0.12% smaller after this CL.
Updates #5729.
toolstash -cmp passes for all architectures other than amd64 and amd64p32.
Other architectures can be done in follow-up CLs.
Change-Id: I0e167e259274b722958567fc0af83a17ca002da7
Reviewed-on: https://go-review.googlesource.com/2284
Reviewed-by: Russ Cox <rsc@golang.org>
2015-04-08 09:54:15 -07:00
OPC // compare parity clear (for x86 NaN check)
2015-04-01 16:02:34 -04:00
OSQRT // sqrt(float64), on systems that have hw support
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-10-12 15:48:18 -07:00
// TODO(mdempsky): Hack to appease toolstash; move up next to OKEY.
OSTRUCTKEY // Sym:Left (key:value in struct literal, after type checking)
2015-03-05 10:45:56 -05:00
)
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 }
// 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.
func ( n * Nodes ) Set1 ( node * Node ) {
n . slice = & [ ] * Node { node }
}
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 }
}
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
}
// 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.
// If a slice is passed in, this will take ownership of it.
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 {
2016-09-14 13:19:20 -07:00
n . slice = & a
2016-02-26 14:28:48 -08:00
} else {
* n . slice = append ( * n . slice , a ... )
}
}
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
}