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
|
|
|
|
|
Ninit *NodeList
|
2016-02-27 14:31:33 -08:00
|
|
|
Nbody Nodes
|
2015-03-05 13:57:36 -05:00
|
|
|
List *NodeList
|
|
|
|
|
Rlist *NodeList
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
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
|
|
|
|
2015-09-24 23:21:18 +02:00
|
|
|
Op Op
|
2015-05-18 15:49:02 -07:00
|
|
|
Nointerface bool
|
|
|
|
|
Ullman uint8 // sethi/ullman number
|
|
|
|
|
Addable bool // addressable
|
2015-09-24 23:21:18 +02:00
|
|
|
Etype EType // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg
|
2015-05-18 15:49:02 -07:00
|
|
|
Bounded bool // bounds check unnecessary
|
2015-10-26 14:57:36 -07:00
|
|
|
Class Class // PPARAM, PAUTO, PEXTERN, etc
|
2015-05-18 15:49:02 -07:00
|
|
|
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)
|
|
|
|
|
Walkdef uint8
|
|
|
|
|
Typecheck uint8
|
|
|
|
|
Local bool
|
|
|
|
|
Dodata uint8
|
|
|
|
|
Initorder uint8
|
|
|
|
|
Used bool
|
|
|
|
|
Isddd bool // is the argument variadic
|
|
|
|
|
Implicit bool
|
|
|
|
|
Addrtaken bool // address taken, even if not moved to heap
|
|
|
|
|
Assigned bool // is the variable ever assigned to
|
|
|
|
|
Likely int8 // likeliness of if statement
|
|
|
|
|
Hasbreak bool // has break statement
|
2015-05-27 00:47:05 -04:00
|
|
|
hasVal int8 // +1 for Val, -1 for Opt, 0 for not yet set
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
|
2015-05-26 23:18:27 -04:00
|
|
|
// Name holds Node fields used only by named nodes (ONAME, OPACK, some OLITERAL).
|
2015-04-03 17:43:38 -07:00
|
|
|
type Name struct {
|
2015-06-03 14:16:01 -04:00
|
|
|
Pack *Node // real package for import . names
|
|
|
|
|
Pkg *Pkg // pkg for OPACK nodes
|
|
|
|
|
Heapaddr *Node // temp holding heap address of param
|
|
|
|
|
Inlvar *Node // ONAME substitute while inlining
|
|
|
|
|
Defn *Node // initializing assignment
|
|
|
|
|
Curfn *Node // function for local variables
|
|
|
|
|
Param *Param
|
2015-05-14 19:33:31 -07:00
|
|
|
Decldepth int32 // declaration loop depth, increased for every loop or label
|
2015-06-03 14:16:01 -04:00
|
|
|
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
|
2015-05-26 23:56:14 -04:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
// ONAME func param with PHEAP
|
|
|
|
|
Outerexpr *Node // expression copied into closure for variable
|
|
|
|
|
Stackparam *Node // OPARAM node referring to stack copy of param
|
|
|
|
|
|
2015-05-26 21:49:31 -04:00
|
|
|
// ONAME PPARAM
|
|
|
|
|
Field *Type // TFIELD in arg struct
|
|
|
|
|
|
2015-05-18 10:27:59 -07:00
|
|
|
// ONAME closure param with PPARAMREF
|
|
|
|
|
Outer *Node // outer PPARAMREF in nested closure
|
|
|
|
|
Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-10 21:37:13 -07:00
|
|
|
// Func holds Node fields used only with function-like nodes.
|
|
|
|
|
type Func struct {
|
2015-05-26 21:49:31 -04:00
|
|
|
Shortname *Node
|
2016-02-29 13:31:48 -08:00
|
|
|
Enter Nodes // for example, allocate and initialize memory for escaping parameters
|
2016-02-26 14:28:48 -08:00
|
|
|
Exit Nodes
|
2016-02-26 17:03:58 -08:00
|
|
|
Cvars Nodes // closure params
|
2016-02-26 13:33:48 -08:00
|
|
|
Dcl []*Node // autodcl for this func/closure
|
2016-02-26 13:48:24 -08:00
|
|
|
Inldcl *[]*Node // copy of dcl for use in inlining
|
2015-05-26 21:49:31 -04:00
|
|
|
Closgen int
|
|
|
|
|
Outerfunc *Node
|
|
|
|
|
Fieldtrack []*Type
|
2015-05-27 00:44:05 -04:00
|
|
|
Outer *Node // outer func for closure
|
|
|
|
|
Ntype *Node // signature
|
|
|
|
|
Top int // top context (Ecall, Eproc, etc)
|
|
|
|
|
Closure *Node // OCLOSURE <-> ODCLFUNC
|
2015-05-27 07:31:56 -04:00
|
|
|
FCurfn *Node
|
2015-05-27 10:42:55 -04:00
|
|
|
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
|
|
|
|
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-02-26 13:32:28 -08: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)
|
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)
|
|
|
|
|
OARRAYLIT // Type{List} (composite literal, Type is array or 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)
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
ODOT // Left.Right (Left is of struct type)
|
|
|
|
|
ODOTPTR // Left.Right (Left is of pointer to struct type)
|
|
|
|
|
ODOTMETH // Left.Right (Left is non-interface, Right is method name)
|
|
|
|
|
ODOTINTER // Left.Right (Left is interface, Right is method name)
|
|
|
|
|
OXDOT // Left.Right (before rewrite to one of the preceding)
|
|
|
|
|
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)
|
|
|
|
|
OPARAM // variant of ONAME for on-stack copy of a parameter or return value that escapes.
|
|
|
|
|
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
|
2015-06-03 23:57:59 -04:00
|
|
|
OCASE // case List: Nbody (select case after processing; List==nil means default)
|
|
|
|
|
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
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// A NodeList is a linked list of nodes.
|
|
|
|
|
// TODO(rsc): Some uses of NodeList should be made into slices.
|
|
|
|
|
// The remaining ones probably just need a simple linked list,
|
|
|
|
|
// not one with concatenation support.
|
|
|
|
|
type NodeList struct {
|
|
|
|
|
N *Node
|
|
|
|
|
Next *NodeList
|
|
|
|
|
End *NodeList
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// concat returns the concatenation of the lists a and b.
|
|
|
|
|
// The storage taken by both is reused for the result.
|
|
|
|
|
func concat(a *NodeList, b *NodeList) *NodeList {
|
|
|
|
|
if a == nil {
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
if b == nil {
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.End.Next = b
|
|
|
|
|
a.End = b.End
|
|
|
|
|
b.End = nil
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// list1 returns a one-element list containing n.
|
|
|
|
|
func list1(n *Node) *NodeList {
|
|
|
|
|
if n == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
l := new(NodeList)
|
|
|
|
|
l.N = n
|
|
|
|
|
l.End = l
|
|
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// list returns the result of appending n to l.
|
|
|
|
|
func list(l *NodeList, n *Node) *NodeList {
|
|
|
|
|
return concat(l, list1(n))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// count returns the length of the list l.
|
|
|
|
|
func count(l *NodeList) int {
|
|
|
|
|
n := int64(0)
|
|
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
|
n++
|
|
|
|
|
}
|
|
|
|
|
if int64(int(n)) != n { // Overflow.
|
|
|
|
|
Yyerror("too many elements in list")
|
|
|
|
|
}
|
|
|
|
|
return int(n)
|
|
|
|
|
}
|
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.
|
|
|
|
|
func (n *Nodes) Slice() []*Node {
|
|
|
|
|
if n.slice == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return *n.slice
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NodeList returns the entries in Nodes as a NodeList.
|
|
|
|
|
// Changes to the NodeList entries (as in l.N = n) will *not* be
|
2016-02-27 14:31:33 -08:00
|
|
|
// reflected in the Nodes.
|
2016-02-26 14:28:48 -08:00
|
|
|
// This wastes memory and should be used as little as possible.
|
|
|
|
|
func (n *Nodes) NodeList() *NodeList {
|
|
|
|
|
if n.slice == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
var ret *NodeList
|
|
|
|
|
for _, n := range *n.slice {
|
|
|
|
|
ret = list(ret, n)
|
|
|
|
|
}
|
|
|
|
|
return ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set sets Nodes to a slice.
|
|
|
|
|
// This takes ownership of the slice.
|
|
|
|
|
func (n *Nodes) Set(s []*Node) {
|
|
|
|
|
if len(s) == 0 {
|
|
|
|
|
n.slice = nil
|
|
|
|
|
} else {
|
|
|
|
|
n.slice = &s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Append appends entries to Nodes.
|
|
|
|
|
// If a slice is passed in, this will take ownership of it.
|
|
|
|
|
func (n *Nodes) Append(a ...*Node) {
|
|
|
|
|
if n.slice == nil {
|
|
|
|
|
if len(a) > 0 {
|
|
|
|
|
n.slice = &a
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
*n.slice = append(*n.slice, a...)
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-27 14:31:33 -08:00
|
|
|
|
|
|
|
|
// SetToNodeList sets Nodes to the contents of a NodeList.
|
|
|
|
|
func (n *Nodes) SetToNodeList(l *NodeList) {
|
|
|
|
|
s := make([]*Node, 0, count(l))
|
|
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
|
s = append(s, l.N)
|
|
|
|
|
}
|
|
|
|
|
n.Set(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AppendNodeList appends the contents of a NodeList.
|
|
|
|
|
func (n *Nodes) AppendNodeList(l *NodeList) {
|
|
|
|
|
if n.slice == nil {
|
|
|
|
|
n.SetToNodeList(l)
|
|
|
|
|
} else {
|
|
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
|
*n.slice = append(*n.slice, l.N)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-03 11:30:17 -08:00
|
|
|
|
2016-03-03 14:11:17 -08:00
|
|
|
// nodesOrNodeList must be either type Nodes or type *NodeList, or, in
|
|
|
|
|
// some cases, []*Node. It exists during the transition from NodeList
|
|
|
|
|
// to Nodes only and then should be deleted. See nodeSeqIterate to
|
|
|
|
|
// return an iterator from a nodesOrNodeList.
|
|
|
|
|
type nodesOrNodeList interface{}
|
|
|
|
|
|
|
|
|
|
// nodesOrNodeListPtr must be type *Nodes or type **NodeList, or, in
|
|
|
|
|
// some cases, *[]*Node. It exists during the transition from NodeList
|
|
|
|
|
// to Nodes only, and then should be deleted. See setNodeSeq to assign
|
|
|
|
|
// to a generic value.
|
|
|
|
|
type nodesOrNodeListPtr interface{}
|
|
|
|
|
|
2016-03-03 11:30:17 -08:00
|
|
|
// nodeSeqIterator is an interface used to iterate over a sequence of nodes.
|
|
|
|
|
// TODO(iant): Remove after conversion from NodeList to Nodes is complete.
|
|
|
|
|
type nodeSeqIterator interface {
|
|
|
|
|
// Return whether iteration is complete.
|
|
|
|
|
Done() bool
|
|
|
|
|
// Advance to the next node.
|
|
|
|
|
Next()
|
|
|
|
|
// Return the current node.
|
|
|
|
|
N() *Node
|
|
|
|
|
// Return the address of the current node.
|
|
|
|
|
P() **Node
|
2016-03-03 14:11:17 -08:00
|
|
|
// Return the number of items remaining in the iteration.
|
|
|
|
|
Len() int
|
2016-03-03 15:08:25 -08:00
|
|
|
// Return the remaining items as a sequence.
|
|
|
|
|
// This will have the same type as that passed to nodeSeqIterate.
|
|
|
|
|
Seq() nodesOrNodeList
|
2016-03-03 11:30:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nodeListIterator is a type that implements nodeSeqIterator using a
|
|
|
|
|
// *NodeList.
|
|
|
|
|
type nodeListIterator struct {
|
|
|
|
|
l *NodeList
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (nli *nodeListIterator) Done() bool {
|
|
|
|
|
return nli.l == nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (nli *nodeListIterator) Next() {
|
|
|
|
|
nli.l = nli.l.Next
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (nli *nodeListIterator) N() *Node {
|
|
|
|
|
return nli.l.N
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (nli *nodeListIterator) P() **Node {
|
|
|
|
|
return &nli.l.N
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 14:11:17 -08:00
|
|
|
func (nli *nodeListIterator) Len() int {
|
|
|
|
|
return count(nli.l)
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 15:08:25 -08:00
|
|
|
func (nli *nodeListIterator) Seq() nodesOrNodeList {
|
|
|
|
|
return nli.l
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 11:30:17 -08:00
|
|
|
// nodesIterator implements nodeSeqIterator using a Nodes.
|
|
|
|
|
type nodesIterator struct {
|
|
|
|
|
n Nodes
|
|
|
|
|
i int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ni *nodesIterator) Done() bool {
|
|
|
|
|
return ni.i >= len(ni.n.Slice())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ni *nodesIterator) Next() {
|
|
|
|
|
ni.i++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ni *nodesIterator) N() *Node {
|
|
|
|
|
return ni.n.Slice()[ni.i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ni *nodesIterator) P() **Node {
|
|
|
|
|
return &ni.n.Slice()[ni.i]
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 14:11:17 -08:00
|
|
|
func (ni *nodesIterator) Len() int {
|
2016-03-04 13:16:48 -08:00
|
|
|
return len(ni.n.Slice()[ni.i:])
|
2016-03-03 14:11:17 -08:00
|
|
|
}
|
|
|
|
|
|
2016-03-03 15:08:25 -08:00
|
|
|
func (ni *nodesIterator) Seq() nodesOrNodeList {
|
|
|
|
|
var r Nodes
|
|
|
|
|
r.Set(ni.n.Slice()[ni.i:])
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-07 09:36:24 -08:00
|
|
|
// nodeSeqIterate returns an iterator over a *NodeList, a Nodes,
|
|
|
|
|
// a []*Node, or nil.
|
2016-03-03 14:11:17 -08:00
|
|
|
func nodeSeqIterate(ns nodesOrNodeList) nodeSeqIterator {
|
2016-03-03 11:30:17 -08:00
|
|
|
switch ns := ns.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
return &nodeListIterator{ns}
|
|
|
|
|
case Nodes:
|
|
|
|
|
return &nodesIterator{ns, 0}
|
2016-03-03 20:48:00 -08:00
|
|
|
case []*Node:
|
|
|
|
|
var r Nodes
|
|
|
|
|
r.Set(ns)
|
|
|
|
|
return &nodesIterator{r, 0}
|
2016-03-07 09:36:24 -08:00
|
|
|
case nil:
|
|
|
|
|
var r Nodes
|
|
|
|
|
return &nodesIterator{r, 0}
|
2016-03-03 11:30:17 -08:00
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-03 14:11:17 -08:00
|
|
|
|
2016-03-07 09:36:24 -08:00
|
|
|
// nodeSeqLen returns the length of a *NodeList, a Nodes, a []*Node, or nil.
|
2016-03-03 15:08:25 -08:00
|
|
|
func nodeSeqLen(ns nodesOrNodeList) int {
|
|
|
|
|
switch ns := ns.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
return count(ns)
|
|
|
|
|
case Nodes:
|
|
|
|
|
return len(ns.Slice())
|
2016-03-04 09:37:58 -08:00
|
|
|
case []*Node:
|
|
|
|
|
return len(ns)
|
2016-03-07 09:36:24 -08:00
|
|
|
case nil:
|
|
|
|
|
return 0
|
2016-03-03 15:08:25 -08:00
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 14:29:24 -08:00
|
|
|
// nodeSeqFirst returns the first element of a *NodeList, a Nodes,
|
|
|
|
|
// or a []*Node. It panics if the sequence is empty.
|
2016-03-03 15:08:25 -08:00
|
|
|
func nodeSeqFirst(ns nodesOrNodeList) *Node {
|
|
|
|
|
switch ns := ns.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
return ns.N
|
|
|
|
|
case Nodes:
|
|
|
|
|
return ns.Slice()[0]
|
2016-03-04 14:29:24 -08:00
|
|
|
case []*Node:
|
|
|
|
|
return ns[0]
|
2016-03-03 15:08:25 -08:00
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 14:29:24 -08:00
|
|
|
// nodeSeqSecond returns the second element of a *NodeList, a Nodes,
|
|
|
|
|
// or a []*Node. It panics if the sequence has fewer than two elements.
|
2016-03-03 15:08:25 -08:00
|
|
|
func nodeSeqSecond(ns nodesOrNodeList) *Node {
|
|
|
|
|
switch ns := ns.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
return ns.Next.N
|
|
|
|
|
case Nodes:
|
|
|
|
|
return ns.Slice()[1]
|
2016-03-04 14:29:24 -08:00
|
|
|
case []*Node:
|
|
|
|
|
return ns[1]
|
2016-03-03 15:08:25 -08:00
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-04 09:37:58 -08:00
|
|
|
// nodeSeqSlice returns a []*Node containing the contents of a
|
|
|
|
|
// *NodeList, a Nodes, or a []*Node.
|
|
|
|
|
// This is an interim function during the transition from NodeList to Nodes.
|
|
|
|
|
// TODO(iant): Remove when transition is complete.
|
|
|
|
|
func nodeSeqSlice(ns nodesOrNodeList) []*Node {
|
|
|
|
|
switch ns := ns.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
var s []*Node
|
|
|
|
|
for l := ns; l != nil; l = l.Next {
|
|
|
|
|
s = append(s, l.N)
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
case Nodes:
|
|
|
|
|
return ns.Slice()
|
|
|
|
|
case []*Node:
|
|
|
|
|
return ns
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 14:11:17 -08:00
|
|
|
// setNodeSeq implements *a = b.
|
|
|
|
|
// a must have type **NodeList, *Nodes, or *[]*Node.
|
2016-03-03 15:08:25 -08:00
|
|
|
// b must have type *NodeList, Nodes, []*Node, or nil.
|
2016-03-03 14:11:17 -08:00
|
|
|
// This is an interim function during the transition from NodeList to Nodes.
|
|
|
|
|
// TODO(iant): Remove when transition is complete.
|
|
|
|
|
func setNodeSeq(a nodesOrNodeListPtr, b nodesOrNodeList) {
|
2016-03-03 15:08:25 -08:00
|
|
|
if b == nil {
|
|
|
|
|
switch a := a.(type) {
|
|
|
|
|
case **NodeList:
|
|
|
|
|
*a = nil
|
|
|
|
|
case *Nodes:
|
|
|
|
|
a.Set(nil)
|
|
|
|
|
case *[]*Node:
|
|
|
|
|
*a = nil
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-03 16:27:05 -08:00
|
|
|
// Simplify b to either *NodeList or []*Node.
|
2016-03-03 14:11:17 -08:00
|
|
|
if n, ok := b.(Nodes); ok {
|
|
|
|
|
b = n.Slice()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l, ok := a.(**NodeList); ok {
|
|
|
|
|
switch b := b.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
*l = b
|
|
|
|
|
case []*Node:
|
|
|
|
|
var ll *NodeList
|
|
|
|
|
for _, n := range b {
|
|
|
|
|
ll = list(ll, n)
|
|
|
|
|
}
|
|
|
|
|
*l = ll
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
var s []*Node
|
|
|
|
|
switch b := b.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
for l := b; l != nil; l = l.Next {
|
|
|
|
|
s = append(s, l.N)
|
|
|
|
|
}
|
|
|
|
|
case []*Node:
|
|
|
|
|
s = b
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch a := a.(type) {
|
|
|
|
|
case *Nodes:
|
|
|
|
|
a.Set(s)
|
|
|
|
|
case *[]*Node:
|
|
|
|
|
*a = s
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-03 16:27:05 -08:00
|
|
|
|
|
|
|
|
// setNodeSeqNode sets the node sequence a to the node n.
|
|
|
|
|
// a must have type **NodeList, *Nodes, or *[]*Node.
|
|
|
|
|
// This is an interim function during the transition from NodeList to Nodes.
|
|
|
|
|
// TODO(iant): Remove when transition is complete.
|
|
|
|
|
func setNodeSeqNode(a nodesOrNodeListPtr, n *Node) {
|
|
|
|
|
switch a := a.(type) {
|
|
|
|
|
case **NodeList:
|
|
|
|
|
*a = list1(n)
|
|
|
|
|
case *Nodes:
|
|
|
|
|
a.Set([]*Node{n})
|
|
|
|
|
case *[]*Node:
|
|
|
|
|
*a = []*Node{n}
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// appendNodeSeq appends the node sequence b to the node sequence a.
|
|
|
|
|
// a must have type **NodeList, *Nodes, or *[]*Node.
|
|
|
|
|
// b must have type *NodeList, Nodes, or []*Node.
|
|
|
|
|
// This is an interim function during the transition from NodeList to Nodes.
|
|
|
|
|
// TODO(iant): Remove when transition is complete.
|
|
|
|
|
func appendNodeSeq(a nodesOrNodeListPtr, b nodesOrNodeList) {
|
|
|
|
|
// Simplify b to either *NodeList or []*Node.
|
|
|
|
|
if n, ok := b.(Nodes); ok {
|
|
|
|
|
b = n.Slice()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l, ok := a.(**NodeList); ok {
|
|
|
|
|
switch b := b.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
*l = concat(*l, b)
|
|
|
|
|
case []*Node:
|
|
|
|
|
for _, n := range b {
|
|
|
|
|
*l = list(*l, n)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
var s []*Node
|
|
|
|
|
switch a := a.(type) {
|
|
|
|
|
case *Nodes:
|
|
|
|
|
s = a.Slice()
|
|
|
|
|
case *[]*Node:
|
|
|
|
|
s = *a
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch b := b.(type) {
|
|
|
|
|
case *NodeList:
|
|
|
|
|
for l := b; l != nil; l = l.Next {
|
|
|
|
|
s = append(s, l.N)
|
|
|
|
|
}
|
|
|
|
|
case []*Node:
|
|
|
|
|
s = append(s, b...)
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch a := a.(type) {
|
|
|
|
|
case *Nodes:
|
|
|
|
|
a.Set(s)
|
|
|
|
|
case *[]*Node:
|
|
|
|
|
*a = s
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// appendNodeSeqNode appends n to the node sequence a.
|
|
|
|
|
// a must have type **NodeList, *Nodes, or *[]*Node.
|
|
|
|
|
// This is an interim function during the transition from NodeList to Nodes.
|
|
|
|
|
// TODO(iant): Remove when transition is complete.
|
|
|
|
|
func appendNodeSeqNode(a nodesOrNodeListPtr, n *Node) {
|
|
|
|
|
switch a := a.(type) {
|
|
|
|
|
case **NodeList:
|
|
|
|
|
*a = list(*a, n)
|
|
|
|
|
case *Nodes:
|
|
|
|
|
a.Append(n)
|
|
|
|
|
case *[]*Node:
|
|
|
|
|
*a = append(*a, n)
|
|
|
|
|
default:
|
|
|
|
|
panic("can't happen")
|
|
|
|
|
}
|
|
|
|
|
}
|