blob: dfb965e35ec48ccd3f391083a496b1a448af535e [file] [log] [blame]
// 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.
package gc
import (
"cmd/internal/obj"
"fmt"
"strings"
)
var mpzero Mpint
// The constant is known to runtime.
const (
tmpstringbufsize = 32
)
func walk(fn *Node) {
var s string
var l *NodeList
var lno int
Curfn = fn
if Debug['W'] != 0 {
s = fmt.Sprintf("\nbefore %v", Sconv(Curfn.Nname.Sym, 0))
dumplist(s, Curfn.Nbody)
}
lno = int(lineno)
// Final typecheck for any unused variables.
// It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
for l = fn.Dcl; l != nil; l = l.Next {
if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO {
typecheck(&l.N, Erv|Easgn)
}
}
// Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
for l = fn.Dcl; l != nil; l = l.Next {
if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Defn != nil && l.N.Defn.Op == OTYPESW && l.N.Used != 0 {
l.N.Defn.Left.Used++
}
}
for l = fn.Dcl; l != nil; l = l.Next {
if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used != 0 {
continue
}
if l.N.Defn != nil && l.N.Defn.Op == OTYPESW {
if l.N.Defn.Left.Used != 0 {
continue
}
lineno = l.N.Defn.Left.Lineno
Yyerror("%v declared and not used", Sconv(l.N.Sym, 0))
l.N.Defn.Left.Used = 1 // suppress repeats
} else {
lineno = l.N.Lineno
Yyerror("%v declared and not used", Sconv(l.N.Sym, 0))
}
}
lineno = int32(lno)
if nerrors != 0 {
return
}
walkstmtlist(Curfn.Nbody)
if Debug['W'] != 0 {
s = fmt.Sprintf("after walk %v", Sconv(Curfn.Nname.Sym, 0))
dumplist(s, Curfn.Nbody)
}
heapmoves()
if Debug['W'] != 0 && Curfn.Enter != nil {
s = fmt.Sprintf("enter %v", Sconv(Curfn.Nname.Sym, 0))
dumplist(s, Curfn.Enter)
}
}
func walkstmtlist(l *NodeList) {
for ; l != nil; l = l.Next {
walkstmt(&l.N)
}
}
func samelist(a *NodeList, b *NodeList) bool {
for ; a != nil && b != nil; (func() { a = a.Next; b = b.Next })() {
if a.N != b.N {
return false
}
}
return a == b
}
func paramoutheap(fn *Node) int {
var l *NodeList
for l = fn.Dcl; l != nil; l = l.Next {
switch l.N.Class {
case PPARAMOUT,
PPARAMOUT | PHEAP:
return int(l.N.Addrtaken)
// stop early - parameters are over
case PAUTO,
PAUTO | PHEAP:
return 0
}
}
return 0
}
// adds "adjust" to all the argument locations for the call n.
// n must be a defer or go node that has already been walked.
func adjustargs(n *Node, adjust int) {
var callfunc *Node
var arg *Node
var lhs *Node
var args *NodeList
callfunc = n.Left
for args = callfunc.List; args != nil; args = args.Next {
arg = args.N
if arg.Op != OAS {
Yyerror("call arg not assignment")
}
lhs = arg.Left
if lhs.Op == ONAME {
// This is a temporary introduced by reorder1.
// The real store to the stack appears later in the arg list.
continue
}
if lhs.Op != OINDREG {
Yyerror("call argument store does not use OINDREG")
}
// can't really check this in machine-indep code.
//if(lhs->val.u.reg != D_SP)
// yyerror("call arg assign not indreg(SP)");
lhs.Xoffset += int64(adjust)
}
}
func walkstmt(np **Node) {
var init *NodeList
var ll *NodeList
var rl *NodeList
var cl int
var n *Node
var f *Node
n = *np
if n == nil {
return
}
if n.Dodata == 2 { // don't walk, generated by anylit.
return
}
setlineno(n)
walkstmtlist(n.Ninit)
switch n.Op {
default:
if n.Op == ONAME {
Yyerror("%v is not a top level statement", Sconv(n.Sym, 0))
} else {
Yyerror("%v is not a top level statement", Oconv(int(n.Op), 0))
}
Dump("nottop", n)
case OAS,
OASOP,
OAS2,
OAS2DOTTYPE,
OAS2RECV,
OAS2FUNC,
OAS2MAPR,
OCLOSE,
OCOPY,
OCALLMETH,
OCALLINTER,
OCALL,
OCALLFUNC,
ODELETE,
OSEND,
OPRINT,
OPRINTN,
OPANIC,
OEMPTY,
ORECOVER:
if n.Typecheck == 0 {
Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign))
}
init = n.Ninit
n.Ninit = nil
walkexpr(&n, &init)
addinit(&n, init)
if (*np).Op == OCOPY && n.Op == OCONVNOP {
n.Op = OEMPTY // don't leave plain values as statements.
}
// special case for a receive where we throw away
// the value received.
case ORECV:
if n.Typecheck == 0 {
Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign))
}
init = n.Ninit
n.Ninit = nil
walkexpr(&n.Left, &init)
n = mkcall1(chanfn("chanrecv1", 2, n.Left.Type), nil, &init, typename(n.Left.Type), n.Left, nodnil())
walkexpr(&n, &init)
addinit(&n, init)
case OBREAK,
ODCL,
OCONTINUE,
OFALL,
OGOTO,
OLABEL,
ODCLCONST,
ODCLTYPE,
OCHECKNIL,
OVARKILL:
break
case OBLOCK:
walkstmtlist(n.List)
case OXCASE:
Yyerror("case statement out of place")
n.Op = OCASE
fallthrough
case OCASE:
walkstmt(&n.Right)
case ODEFER:
Hasdefer = 1
switch n.Left.Op {
case OPRINT,
OPRINTN:
walkprintfunc(&n.Left, &n.Ninit)
case OCOPY:
n.Left = copyany(n.Left, &n.Ninit, 1)
default:
walkexpr(&n.Left, &n.Ninit)
}
// make room for size & fn arguments.
adjustargs(n, 2*Widthptr)
case OFOR:
if n.Ntest != nil {
walkstmtlist(n.Ntest.Ninit)
init = n.Ntest.Ninit
n.Ntest.Ninit = nil
walkexpr(&n.Ntest, &init)
addinit(&n.Ntest, init)
}
walkstmt(&n.Nincr)
walkstmtlist(n.Nbody)
case OIF:
walkexpr(&n.Ntest, &n.Ninit)
walkstmtlist(n.Nbody)
walkstmtlist(n.Nelse)
case OPROC:
switch n.Left.Op {
case OPRINT,
OPRINTN:
walkprintfunc(&n.Left, &n.Ninit)
case OCOPY:
n.Left = copyany(n.Left, &n.Ninit, 1)
default:
walkexpr(&n.Left, &n.Ninit)
}
// make room for size & fn arguments.
adjustargs(n, 2*Widthptr)
case ORETURN:
walkexprlist(n.List, &n.Ninit)
if n.List == nil {
break
}
if (Curfn.Type.Outnamed != 0 && count(n.List) > 1) || paramoutheap(Curfn) != 0 {
// assign to the function out parameters,
// so that reorder3 can fix up conflicts
rl = nil
for ll = Curfn.Dcl; ll != nil; ll = ll.Next {
cl = int(ll.N.Class) &^ PHEAP
if cl == PAUTO {
break
}
if cl == PPARAMOUT {
rl = list(rl, ll.N)
}
}
if samelist(rl, n.List) {
// special return in disguise
n.List = nil
break
}
if count(n.List) == 1 && count(rl) > 1 {
// OAS2FUNC in disguise
f = n.List.N
if f.Op != OCALLFUNC && f.Op != OCALLMETH && f.Op != OCALLINTER {
Fatal("expected return of call, have %v", Nconv(f, 0))
}
n.List = concat(list1(f), ascompatet(int(n.Op), rl, &f.Type, 0, &n.Ninit))
break
}
// move function calls out, to make reorder3's job easier.
walkexprlistsafe(n.List, &n.Ninit)
ll = ascompatee(int(n.Op), rl, n.List, &n.Ninit)
n.List = reorder3(ll)
break
}
ll = ascompatte(int(n.Op), nil, 0, Getoutarg(Curfn.Type), n.List, 1, &n.Ninit)
n.List = ll
case ORETJMP:
break
case OSELECT:
walkselect(n)
case OSWITCH:
walkswitch(n)
case ORANGE:
walkrange(n)
case OXFALL:
Yyerror("fallthrough statement out of place")
n.Op = OFALL
}
if n.Op == ONAME {
Fatal("walkstmt ended up with name: %v", Nconv(n, obj.FmtSign))
}
*np = n
}
/*
* walk the whole tree of the body of an
* expression or simple statement.
* the types expressions are calculated.
* compile-time constants are evaluated.
* complex side effects like statements are appended to init
*/
func walkexprlist(l *NodeList, init **NodeList) {
for ; l != nil; l = l.Next {
walkexpr(&l.N, init)
}
}
func walkexprlistsafe(l *NodeList, init **NodeList) {
for ; l != nil; l = l.Next {
l.N = safeexpr(l.N, init)
walkexpr(&l.N, init)
}
}
func walkexprlistcheap(l *NodeList, init **NodeList) {
for ; l != nil; l = l.Next {
l.N = cheapexpr(l.N, init)
walkexpr(&l.N, init)
}
}
func walkexpr(np **Node, init **NodeList) {
var r *Node
var l *Node
var var_ *Node
var a *Node
var ok *Node
var map_ *Node
var key *Node
var ll *NodeList
var lr *NodeList
var t *Type
var et int
var old_safemode int
var v int64
var lno int32
var n *Node
var fn *Node
var n1 *Node
var n2 *Node
var sym *Sym
var buf string
var p string
var from string
var to string
n = *np
if n == nil {
return
}
if init == &n.Ninit {
// not okay to use n->ninit when walking n,
// because we might replace n with some other node
// and would lose the init list.
Fatal("walkexpr init == &n->ninit")
}
if n.Ninit != nil {
walkstmtlist(n.Ninit)
*init = concat(*init, n.Ninit)
n.Ninit = nil
}
// annoying case - not typechecked
if n.Op == OKEY {
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
return
}
lno = setlineno(n)
if Debug['w'] > 1 {
Dump("walk-before", n)
}
if n.Typecheck != 1 {
Fatal("missed typecheck: %v\n", Nconv(n, obj.FmtSign))
}
switch n.Op {
default:
Dump("walk", n)
Fatal("walkexpr: switch 1 unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign))
case OTYPE,
ONONAME,
OINDREG,
OEMPTY,
OPARAM:
goto ret
case ONOT,
OMINUS,
OPLUS,
OCOM,
OREAL,
OIMAG,
ODOTMETH,
ODOTINTER:
walkexpr(&n.Left, init)
goto ret
case OIND:
walkexpr(&n.Left, init)
goto ret
case ODOT:
usefield(n)
walkexpr(&n.Left, init)
goto ret
case ODOTPTR:
usefield(n)
if n.Op == ODOTPTR && n.Left.Type.Type.Width == 0 {
// No actual copy will be generated, so emit an explicit nil check.
n.Left = cheapexpr(n.Left, init)
checknil(n.Left, init)
}
walkexpr(&n.Left, init)
goto ret
case OEFACE:
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
goto ret
case OSPTR,
OITAB:
walkexpr(&n.Left, init)
goto ret
case OLEN,
OCAP:
walkexpr(&n.Left, init)
// replace len(*[10]int) with 10.
// delayed until now to preserve side effects.
t = n.Left.Type
if Isptr[t.Etype] != 0 {
t = t.Type
}
if Isfixedarray(t) {
safeexpr(n.Left, init)
Nodconst(n, n.Type, t.Bound)
n.Typecheck = 1
}
goto ret
case OLSH,
ORSH:
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
t = n.Left.Type
n.Bounded = bounded(n.Right, 8*t.Width)
if Debug['m'] != 0 && n.Etype != 0 && !Isconst(n.Right, CTINT) {
Warn("shift bounds check elided")
}
goto ret
// Use results from call expression as arguments for complex.
case OAND,
OSUB,
OHMUL,
OLT,
OLE,
OGE,
OGT,
OADD,
OCOMPLEX,
OLROT:
if n.Op == OCOMPLEX && n.Left == nil && n.Right == nil {
n.Left = n.List.N
n.Right = n.List.Next.N
}
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
goto ret
case OOR,
OXOR:
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
walkrotate(&n)
goto ret
case OEQ,
ONE:
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
// Disable safemode while compiling this code: the code we
// generate internally can refer to unsafe.Pointer.
// In this case it can happen if we need to generate an ==
// for a struct containing a reflect.Value, which itself has
// an unexported field of type unsafe.Pointer.
old_safemode = safemode
safemode = 0
walkcompare(&n, init)
safemode = old_safemode
goto ret
case OANDAND,
OOROR:
walkexpr(&n.Left, init)
// cannot put side effects from n->right on init,
// because they cannot run before n->left is checked.
// save elsewhere and store on the eventual n->right.
ll = nil
walkexpr(&n.Right, &ll)
addinit(&n.Right, ll)
goto ret
case OPRINT,
OPRINTN:
walkexprlist(n.List, init)
n = walkprint(n, init)
goto ret
case OPANIC:
n = mkcall("gopanic", nil, init, n.Left)
goto ret
case ORECOVER:
n = mkcall("gorecover", n.Type, init, Nod(OADDR, nodfp, nil))
goto ret
case OLITERAL:
n.Addable = 1
goto ret
case OCLOSUREVAR,
OCFUNC:
n.Addable = 1
goto ret
case ONAME:
if n.Class&PHEAP == 0 && n.Class != PPARAMREF {
n.Addable = 1
}
goto ret
case OCALLINTER:
t = n.Left.Type
if n.List != nil && n.List.N.Op == OAS {
goto ret
}
walkexpr(&n.Left, init)
walkexprlist(n.List, init)
ll = ascompatte(int(n.Op), n, int(n.Isddd), getinarg(t), n.List, 0, init)
n.List = reorder1(ll)
goto ret
case OCALLFUNC:
if n.Left.Op == OCLOSURE {
// Transform direct call of a closure to call of a normal function.
// transformclosure already did all preparation work.
// Append captured variables to argument list.
n.List = concat(n.List, n.Left.Enter)
n.Left.Enter = nil
// Replace OCLOSURE with ONAME/PFUNC.
n.Left = n.Left.Closure.Nname
// Update type of OCALLFUNC node.
// Output arguments had not changed, but their offsets could.
if n.Left.Type.Outtuple == 1 {
t = getoutargx(n.Left.Type).Type
if t.Etype == TFIELD {
t = t.Type
}
n.Type = t
} else {
n.Type = getoutargx(n.Left.Type)
}
}
t = n.Left.Type
if n.List != nil && n.List.N.Op == OAS {
goto ret
}
walkexpr(&n.Left, init)
walkexprlist(n.List, init)
ll = ascompatte(int(n.Op), n, int(n.Isddd), getinarg(t), n.List, 0, init)
n.List = reorder1(ll)
goto ret
case OCALLMETH:
t = n.Left.Type
if n.List != nil && n.List.N.Op == OAS {
goto ret
}
walkexpr(&n.Left, init)
walkexprlist(n.List, init)
ll = ascompatte(int(n.Op), n, 0, getthis(t), list1(n.Left.Left), 0, init)
lr = ascompatte(int(n.Op), n, int(n.Isddd), getinarg(t), n.List, 0, init)
ll = concat(ll, lr)
n.Left.Left = nil
ullmancalc(n.Left)
n.List = reorder1(ll)
goto ret
case OAS:
*init = concat(*init, n.Ninit)
n.Ninit = nil
walkexpr(&n.Left, init)
n.Left = safeexpr(n.Left, init)
if oaslit(n, init) {
goto ret
}
if n.Right == nil || iszero(n.Right) && flag_race == 0 {
goto ret
}
switch n.Right.Op {
default:
walkexpr(&n.Right, init)
// x = i.(T); n->left is x, n->right->left is i.
// orderstmt made sure x is addressable.
case ODOTTYPE:
walkexpr(&n.Right.Left, init)
n1 = Nod(OADDR, n.Left, nil)
r = n.Right // i.(T)
from = "I"
to = "T"
if isnilinter(r.Left.Type) {
from = "E"
}
if isnilinter(r.Type) {
to = "E"
} else if Isinter(r.Type) {
to = "I"
}
buf = fmt.Sprintf("assert%s2%s", from, to)
fn = syslook(buf, 1)
argtype(fn, r.Left.Type)
argtype(fn, r.Type)
n = mkcall1(fn, nil, init, typename(r.Type), r.Left, n1)
walkexpr(&n, init)
goto ret
// x = <-c; n->left is x, n->right->left is c.
// orderstmt made sure x is addressable.
case ORECV:
walkexpr(&n.Right.Left, init)
n1 = Nod(OADDR, n.Left, nil)
r = n.Right.Left // the channel
n = mkcall1(chanfn("chanrecv1", 2, r.Type), nil, init, typename(r.Type), r, n1)
walkexpr(&n, init)
goto ret
}
if n.Left != nil && n.Right != nil {
r = convas(Nod(OAS, n.Left, n.Right), init)
r.Dodata = n.Dodata
n = r
n = applywritebarrier(n, init)
}
goto ret
case OAS2:
*init = concat(*init, n.Ninit)
n.Ninit = nil
walkexprlistsafe(n.List, init)
walkexprlistsafe(n.Rlist, init)
ll = ascompatee(OAS, n.List, n.Rlist, init)
ll = reorder3(ll)
for lr = ll; lr != nil; lr = lr.Next {
lr.N = applywritebarrier(lr.N, init)
}
n = liststmt(ll)
goto ret
// a,b,... = fn()
case OAS2FUNC:
*init = concat(*init, n.Ninit)
n.Ninit = nil
r = n.Rlist.N
walkexprlistsafe(n.List, init)
walkexpr(&r, init)
ll = ascompatet(int(n.Op), n.List, &r.Type, 0, init)
for lr = ll; lr != nil; lr = lr.Next {
lr.N = applywritebarrier(lr.N, init)
}
n = liststmt(concat(list1(r), ll))
goto ret
// x, y = <-c
// orderstmt made sure x is addressable.
case OAS2RECV:
*init = concat(*init, n.Ninit)
n.Ninit = nil
r = n.Rlist.N
walkexprlistsafe(n.List, init)
walkexpr(&r.Left, init)
if isblank(n.List.N) {
n1 = nodnil()
} else {
n1 = Nod(OADDR, n.List.N, nil)
}
n1.Etype = 1 // addr does not escape
fn = chanfn("chanrecv2", 2, r.Left.Type)
r = mkcall1(fn, n.List.Next.N.Type, init, typename(r.Left.Type), r.Left, n1)
n = Nod(OAS, n.List.Next.N, r)
typecheck(&n, Etop)
goto ret
// a,b = m[i];
case OAS2MAPR:
*init = concat(*init, n.Ninit)
n.Ninit = nil
r = n.Rlist.N
walkexprlistsafe(n.List, init)
walkexpr(&r.Left, init)
walkexpr(&r.Right, init)
t = r.Left.Type
p = ""
if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
switch Simsimtype(t.Down) {
case TINT32,
TUINT32:
p = "mapaccess2_fast32"
case TINT64,
TUINT64:
p = "mapaccess2_fast64"
case TSTRING:
p = "mapaccess2_faststr"
}
}
if p != "" {
// fast versions take key by value
key = r.Right
} else {
// standard version takes key by reference
// orderexpr made sure key is addressable.
key = Nod(OADDR, r.Right, nil)
p = "mapaccess2"
}
// from:
// a,b = m[i]
// to:
// var,b = mapaccess2*(t, m, i)
// a = *var
a = n.List.N
fn = mapfn(p, t)
r = mkcall1(fn, getoutargx(fn.Type), init, typename(t), r.Left, key)
// mapaccess2* returns a typed bool, but due to spec changes,
// the boolean result of i.(T) is now untyped so we make it the
// same type as the variable on the lhs.
if !isblank(n.List.Next.N) {
r.Type.Type.Down.Type = n.List.Next.N.Type
}
n.Rlist = list1(r)
n.Op = OAS2FUNC
// don't generate a = *var if a is _
if !isblank(a) {
var_ = temp(Ptrto(t.Type))
var_.Typecheck = 1
n.List.N = var_
walkexpr(&n, init)
*init = list(*init, n)
n = Nod(OAS, a, Nod(OIND, var_, nil))
}
typecheck(&n, Etop)
walkexpr(&n, init)
// mapaccess needs a zero value to be at least this big.
if zerosize < t.Type.Width {
zerosize = t.Type.Width
}
// TODO: ptr is always non-nil, so disable nil check for this OIND op.
goto ret
case ODELETE:
*init = concat(*init, n.Ninit)
n.Ninit = nil
map_ = n.List.N
key = n.List.Next.N
walkexpr(&map_, init)
walkexpr(&key, init)
// orderstmt made sure key is addressable.
key = Nod(OADDR, key, nil)
t = map_.Type
n = mkcall1(mapfndel("mapdelete", t), nil, init, typename(t), map_, key)
goto ret
// a,b = i.(T)
// orderstmt made sure a is addressable.
case OAS2DOTTYPE:
*init = concat(*init, n.Ninit)
n.Ninit = nil
r = n.Rlist.N
walkexprlistsafe(n.List, init)
walkexpr(&r.Left, init)
if isblank(n.List.N) {
n1 = nodnil()
} else {
n1 = Nod(OADDR, n.List.N, nil)
}
n1.Etype = 1 // addr does not escape
from = "I"
to = "T"
if isnilinter(r.Left.Type) {
from = "E"
}
if isnilinter(r.Type) {
to = "E"
} else if Isinter(r.Type) {
to = "I"
}
buf = fmt.Sprintf("assert%s2%s2", from, to)
fn = syslook(buf, 1)
argtype(fn, r.Left.Type)
argtype(fn, r.Type)
t = Types[TBOOL]
ok = n.List.Next.N
if !isblank(ok) {
t = ok.Type
}
r = mkcall1(fn, t, init, typename(r.Type), r.Left, n1)
n = Nod(OAS, ok, r)
typecheck(&n, Etop)
goto ret
case ODOTTYPE,
ODOTTYPE2:
Fatal("walkexpr ODOTTYPE") // should see inside OAS or OAS2 only
case OCONVIFACE:
walkexpr(&n.Left, init)
// Optimize convT2E as a two-word copy when T is pointer-shaped.
if isnilinter(n.Type) && isdirectiface(n.Left.Type) {
l = Nod(OEFACE, typename(n.Left.Type), n.Left)
l.Type = n.Type
l.Typecheck = n.Typecheck
n = l
goto ret
}
// Build name of function: convI2E etc.
// Not all names are possible
// (e.g., we'll never generate convE2E or convE2I).
from = "T"
to = "I"
if isnilinter(n.Left.Type) {
from = "E"
} else if Isinter(n.Left.Type) {
from = "I"
}
if isnilinter(n.Type) {
to = "E"
}
buf = fmt.Sprintf("conv%s2%s", from, to)
fn = syslook(buf, 1)
ll = nil
if !Isinter(n.Left.Type) {
ll = list(ll, typename(n.Left.Type))
}
if !isnilinter(n.Type) {
ll = list(ll, typename(n.Type))
}
if !Isinter(n.Left.Type) && !isnilinter(n.Type) {
sym = Pkglookup(fmt.Sprintf("%v.%v", Tconv(n.Left.Type, obj.FmtLeft), Tconv(n.Type, obj.FmtLeft)), itabpkg)
if sym.Def == nil {
l = Nod(ONAME, nil, nil)
l.Sym = sym
l.Type = Ptrto(Types[TUINT8])
l.Addable = 1
l.Class = PEXTERN
l.Xoffset = 0
sym.Def = l
ggloblsym(sym, int32(Widthptr), obj.DUPOK|obj.NOPTR)
}
l = Nod(OADDR, sym.Def, nil)
l.Addable = 1
ll = list(ll, l)
if isdirectiface(n.Left.Type) {
/* For pointer types, we can make a special form of optimization
*
* These statements are put onto the expression init list:
* Itab *tab = atomicloadtype(&cache);
* if(tab == nil)
* tab = typ2Itab(type, itype, &cache);
*
* The CONVIFACE expression is replaced with this:
* OEFACE{tab, ptr};
*/
l = temp(Ptrto(Types[TUINT8]))
n1 = Nod(OAS, l, sym.Def)
typecheck(&n1, Etop)
*init = list(*init, n1)
fn = syslook("typ2Itab", 1)
n1 = Nod(OCALL, fn, nil)
n1.List = ll
typecheck(&n1, Erv)
walkexpr(&n1, init)
n2 = Nod(OIF, nil, nil)
n2.Ntest = Nod(OEQ, l, nodnil())
n2.Nbody = list1(Nod(OAS, l, n1))
n2.Likely = -1
typecheck(&n2, Etop)
*init = list(*init, n2)
l = Nod(OEFACE, l, n.Left)
l.Typecheck = n.Typecheck
l.Type = n.Type
n = l
goto ret
}
}
if Isinter(n.Left.Type) {
ll = list(ll, n.Left)
} else {
// regular types are passed by reference to avoid C vararg calls
// orderexpr arranged for n->left to be a temporary for all
// the conversions it could see. comparison of an interface
// with a non-interface, especially in a switch on interface value
// with non-interface cases, is not visible to orderstmt, so we
// have to fall back on allocating a temp here.
if islvalue(n.Left) {
ll = list(ll, Nod(OADDR, n.Left, nil))
} else {
ll = list(ll, Nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil))
}
}
argtype(fn, n.Left.Type)
argtype(fn, n.Type)
dowidth(fn.Type)
n = Nod(OCALL, fn, nil)
n.List = ll
typecheck(&n, Erv)
walkexpr(&n, init)
goto ret
case OCONV,
OCONVNOP:
if Thearch.Thechar == '5' {
if Isfloat[n.Left.Type.Etype] != 0 {
if n.Type.Etype == TINT64 {
n = mkcall("float64toint64", n.Type, init, conv(n.Left, Types[TFLOAT64]))
goto ret
}
if n.Type.Etype == TUINT64 {
n = mkcall("float64touint64", n.Type, init, conv(n.Left, Types[TFLOAT64]))
goto ret
}
}
if Isfloat[n.Type.Etype] != 0 {
if n.Left.Type.Etype == TINT64 {
n = mkcall("int64tofloat64", n.Type, init, conv(n.Left, Types[TINT64]))
goto ret
}
if n.Left.Type.Etype == TUINT64 {
n = mkcall("uint64tofloat64", n.Type, init, conv(n.Left, Types[TUINT64]))
goto ret
}
}
}
walkexpr(&n.Left, init)
goto ret
case OANDNOT:
walkexpr(&n.Left, init)
n.Op = OAND
n.Right = Nod(OCOM, n.Right, nil)
typecheck(&n.Right, Erv)
walkexpr(&n.Right, init)
goto ret
case OMUL:
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
walkmul(&n, init)
goto ret
case ODIV,
OMOD:
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
/*
* rewrite complex div into function call.
*/
et = int(n.Left.Type.Etype)
if Iscomplex[et] != 0 && n.Op == ODIV {
t = n.Type
n = mkcall("complex128div", Types[TCOMPLEX128], init, conv(n.Left, Types[TCOMPLEX128]), conv(n.Right, Types[TCOMPLEX128]))
n = conv(n, t)
goto ret
}
// Nothing to do for float divisions.
if Isfloat[et] != 0 {
goto ret
}
// Try rewriting as shifts or magic multiplies.
walkdiv(&n, init)
/*
* rewrite 64-bit div and mod into function calls
* on 32-bit architectures.
*/
switch n.Op {
case OMOD,
ODIV:
if Widthreg >= 8 || (et != TUINT64 && et != TINT64) {
goto ret
}
if et == TINT64 {
namebuf = "int64"
} else {
namebuf = "uint64"
}
if n.Op == ODIV {
namebuf += "div"
} else {
namebuf += "mod"
}
n = mkcall(namebuf, n.Type, init, conv(n.Left, Types[et]), conv(n.Right, Types[et]))
default:
break
}
goto ret
case OINDEX:
walkexpr(&n.Left, init)
// save the original node for bounds checking elision.
// If it was a ODIV/OMOD walk might rewrite it.
r = n.Right
walkexpr(&n.Right, init)
// if range of type cannot exceed static array bound,
// disable bounds check.
if n.Bounded {
goto ret
}
t = n.Left.Type
if t != nil && Isptr[t.Etype] != 0 {
t = t.Type
}
if Isfixedarray(t) {
n.Bounded = bounded(r, t.Bound)
if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
Warn("index bounds check elided")
}
if Smallintconst(n.Right) && !n.Bounded {
Yyerror("index out of bounds")
}
} else if Isconst(n.Left, CTSTR) {
n.Bounded = bounded(r, int64(len(n.Left.Val.U.Sval.S)))
if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
Warn("index bounds check elided")
}
if Smallintconst(n.Right) {
if !n.Bounded {
Yyerror("index out of bounds")
} else {
// replace "abc"[1] with 'b'.
// delayed until now because "abc"[1] is not
// an ideal constant.
v = Mpgetfix(n.Right.Val.U.Xval)
Nodconst(n, n.Type, int64(n.Left.Val.U.Sval.S[v]))
n.Typecheck = 1
}
}
}
if Isconst(n.Right, CTINT) {
if Mpcmpfixfix(n.Right.Val.U.Xval, &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.Xval, Maxintval[TINT]) > 0 {
Yyerror("index out of bounds")
}
}
goto ret
case OINDEXMAP:
if n.Etype == 1 {
goto ret
}
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
t = n.Left.Type
p = ""
if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
switch Simsimtype(t.Down) {
case TINT32,
TUINT32:
p = "mapaccess1_fast32"
case TINT64,
TUINT64:
p = "mapaccess1_fast64"
case TSTRING:
p = "mapaccess1_faststr"
}
}
if p != "" {
// fast versions take key by value
key = n.Right
} else {
// standard version takes key by reference.
// orderexpr made sure key is addressable.
key = Nod(OADDR, n.Right, nil)
p = "mapaccess1"
}
n = mkcall1(mapfn(p, t), Ptrto(t.Type), init, typename(t), n.Left, key)
n = Nod(OIND, n, nil)
n.Type = t.Type
n.Typecheck = 1
// mapaccess needs a zero value to be at least this big.
if zerosize < t.Type.Width {
zerosize = t.Type.Width
}
goto ret
case ORECV:
Fatal("walkexpr ORECV") // should see inside OAS only
case OSLICE:
if n.Right != nil && n.Right.Left == nil && n.Right.Right == nil { // noop
walkexpr(&n.Left, init)
n = n.Left
goto ret
}
fallthrough
// fallthrough
case OSLICEARR,
OSLICESTR:
if n.Right == nil { // already processed
goto ret
}
walkexpr(&n.Left, init)
// cgen_slice can't handle string literals as source
// TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi]
if (n.Op == OSLICESTR && n.Left.Op == OLITERAL) || (n.Left.Op == OINDEX) {
n.Left = copyexpr(n.Left, n.Left.Type, init)
} else {
n.Left = safeexpr(n.Left, init)
}
walkexpr(&n.Right.Left, init)
n.Right.Left = safeexpr(n.Right.Left, init)
walkexpr(&n.Right.Right, init)
n.Right.Right = safeexpr(n.Right.Right, init)
n = sliceany(n, init) // chops n->right, sets n->list
goto ret
case OSLICE3,
OSLICE3ARR:
if n.Right == nil { // already processed
goto ret
}
walkexpr(&n.Left, init)
// TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi]
// TODO the comment on the previous line was copied from case OSLICE. it might not even be true.
if n.Left.Op == OINDEX {
n.Left = copyexpr(n.Left, n.Left.Type, init)
} else {
n.Left = safeexpr(n.Left, init)
}
walkexpr(&n.Right.Left, init)
n.Right.Left = safeexpr(n.Right.Left, init)
walkexpr(&n.Right.Right.Left, init)
n.Right.Right.Left = safeexpr(n.Right.Right.Left, init)
walkexpr(&n.Right.Right.Right, init)
n.Right.Right.Right = safeexpr(n.Right.Right.Right, init)
n = sliceany(n, init) // chops n->right, sets n->list
goto ret
case OADDR:
walkexpr(&n.Left, init)
goto ret
case ONEW:
if n.Esc == EscNone && n.Type.Type.Width < 1<<16 {
r = temp(n.Type.Type)
r = Nod(OAS, r, nil) // zero temp
typecheck(&r, Etop)
*init = list(*init, r)
r = Nod(OADDR, r.Left, nil)
typecheck(&r, Erv)
n = r
} else {
n = callnew(n.Type.Type)
}
goto ret
// If one argument to the comparison is an empty string,
// comparing the lengths instead will yield the same result
// without the function call.
case OCMPSTR:
if (Isconst(n.Left, CTSTR) && len(n.Left.Val.U.Sval.S) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val.U.Sval.S) == 0) {
r = Nod(int(n.Etype), Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil))
typecheck(&r, Erv)
walkexpr(&r, init)
r.Type = n.Type
n = r
goto ret
}
// s + "badgerbadgerbadger" == "badgerbadgerbadger"
if (n.Etype == OEQ || n.Etype == ONE) && Isconst(n.Right, CTSTR) && n.Left.Op == OADDSTR && count(n.Left.List) == 2 && Isconst(n.Left.List.Next.N, CTSTR) && cmpslit(n.Right, n.Left.List.Next.N) == 0 {
r = Nod(int(n.Etype), Nod(OLEN, n.Left.List.N, nil), Nodintconst(0))
typecheck(&r, Erv)
walkexpr(&r, init)
r.Type = n.Type
n = r
goto ret
}
if n.Etype == OEQ || n.Etype == ONE {
// prepare for rewrite below
n.Left = cheapexpr(n.Left, init)
n.Right = cheapexpr(n.Right, init)
r = mkcall("eqstring", Types[TBOOL], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING]))
// quick check of len before full compare for == or !=
// eqstring assumes that the lengths are equal
if n.Etype == OEQ {
// len(left) == len(right) && eqstring(left, right)
r = Nod(OANDAND, Nod(OEQ, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r)
} else {
// len(left) != len(right) || !eqstring(left, right)
r = Nod(ONOT, r, nil)
r = Nod(OOROR, Nod(ONE, Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil)), r)
}
typecheck(&r, Erv)
walkexpr(&r, nil)
} else {
// sys_cmpstring(s1, s2) :: 0
r = mkcall("cmpstring", Types[TINT], init, conv(n.Left, Types[TSTRING]), conv(n.Right, Types[TSTRING]))
r = Nod(int(n.Etype), r, Nodintconst(0))
}
typecheck(&r, Erv)
if n.Type.Etype != TBOOL {
Fatal("cmp %v", Tconv(n.Type, 0))
}
r.Type = n.Type
n = r
goto ret
case OADDSTR:
n = addstr(n, init)
goto ret
case OAPPEND:
if n.Isddd != 0 {
n = appendslice(n, init) // also works for append(slice, string).
} else {
n = walkappend(n, init)
}
goto ret
case OCOPY:
n = copyany(n, init, flag_race)
goto ret
// cannot use chanfn - closechan takes any, not chan any
case OCLOSE:
fn = syslook("closechan", 1)
argtype(fn, n.Left.Type)
n = mkcall1(fn, nil, init, n.Left)
goto ret
case OMAKECHAN:
n = mkcall1(chanfn("makechan", 1, n.Type), n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64]))
goto ret
case OMAKEMAP:
t = n.Type
fn = syslook("makemap", 1)
a = nodnil() // hmap buffer
r = nodnil() // bucket buffer
if n.Esc == EscNone {
// Allocate hmap buffer on stack.
var_ = temp(hmap(t))
a = Nod(OAS, var_, nil) // zero temp
typecheck(&a, Etop)
*init = list(*init, a)
a = Nod(OADDR, var_, nil)
// Allocate one bucket on stack.
// Maximum key/value size is 128 bytes, larger objects
// are stored with an indirection. So max bucket size is 2048+eps.
var_ = temp(mapbucket(t))
r = Nod(OAS, var_, nil) // zero temp
typecheck(&r, Etop)
*init = list(*init, r)
r = Nod(OADDR, var_, nil)
}
argtype(fn, hmap(t)) // hmap buffer
argtype(fn, mapbucket(t)) // bucket buffer
argtype(fn, t.Down) // key type
argtype(fn, t.Type) // value type
n = mkcall1(fn, n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64]), a, r)
goto ret
case OMAKESLICE:
l = n.Left
r = n.Right
if r == nil {
r = safeexpr(l, init)
l = r
}
t = n.Type
if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.Xval) < (1<<16)/t.Type.Width) {
// var arr [r]T
// n = arr[:l]
t = aindex(r, t.Type) // [r]T
var_ = temp(t)
a = Nod(OAS, var_, nil) // zero temp
typecheck(&a, Etop)
*init = list(*init, a)
r = Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l]
r = conv(r, n.Type) // in case n->type is named.
typecheck(&r, Erv)
walkexpr(&r, init)
n = r
} else {
// makeslice(t *Type, nel int64, max int64) (ary []any)
fn = syslook("makeslice", 1)
argtype(fn, t.Type) // any-1
n = mkcall1(fn, n.Type, init, typename(n.Type), conv(l, Types[TINT64]), conv(r, Types[TINT64]))
}
goto ret
case ORUNESTR:
a = nodnil()
if n.Esc == EscNone {
t = aindex(Nodintconst(4), Types[TUINT8])
var_ = temp(t)
a = Nod(OADDR, var_, nil)
}
// intstring(*[4]byte, rune)
n = mkcall("intstring", n.Type, init, a, conv(n.Left, Types[TINT64]))
goto ret
case OARRAYBYTESTR:
a = nodnil()
if n.Esc == EscNone {
// Create temporary buffer for string on stack.
t = aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
a = Nod(OADDR, temp(t), nil)
}
// slicebytetostring(*[32]byte, []byte) string;
n = mkcall("slicebytetostring", n.Type, init, a, n.Left)
goto ret
// slicebytetostringtmp([]byte) string;
case OARRAYBYTESTRTMP:
n = mkcall("slicebytetostringtmp", n.Type, init, n.Left)
goto ret
// slicerunetostring(*[32]byte, []rune) string;
case OARRAYRUNESTR:
a = nodnil()
if n.Esc == EscNone {
// Create temporary buffer for string on stack.
t = aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
a = Nod(OADDR, temp(t), nil)
}
n = mkcall("slicerunetostring", n.Type, init, a, n.Left)
goto ret
// stringtoslicebyte(*32[byte], string) []byte;
case OSTRARRAYBYTE:
a = nodnil()
if n.Esc == EscNone {
// Create temporary buffer for slice on stack.
t = aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
a = Nod(OADDR, temp(t), nil)
}
n = mkcall("stringtoslicebyte", n.Type, init, a, conv(n.Left, Types[TSTRING]))
goto ret
// stringtoslicebytetmp(string) []byte;
case OSTRARRAYBYTETMP:
n = mkcall("stringtoslicebytetmp", n.Type, init, conv(n.Left, Types[TSTRING]))
goto ret
// stringtoslicerune(*[32]rune, string) []rune
case OSTRARRAYRUNE:
a = nodnil()
if n.Esc == EscNone {
// Create temporary buffer for slice on stack.
t = aindex(Nodintconst(tmpstringbufsize), Types[TINT32])
a = Nod(OADDR, temp(t), nil)
}
n = mkcall("stringtoslicerune", n.Type, init, a, n.Left)
goto ret
// ifaceeq(i1 any-1, i2 any-2) (ret bool);
case OCMPIFACE:
if !Eqtype(n.Left.Type, n.Right.Type) {
Fatal("ifaceeq %v %v %v", Oconv(int(n.Op), 0), Tconv(n.Left.Type, 0), Tconv(n.Right.Type, 0))
}
if isnilinter(n.Left.Type) {
fn = syslook("efaceeq", 1)
} else {
fn = syslook("ifaceeq", 1)
}
n.Right = cheapexpr(n.Right, init)
n.Left = cheapexpr(n.Left, init)
argtype(fn, n.Right.Type)
argtype(fn, n.Left.Type)
r = mkcall1(fn, n.Type, init, n.Left, n.Right)
if n.Etype == ONE {
r = Nod(ONOT, r, nil)
}
// check itable/type before full compare.
if n.Etype == OEQ {
r = Nod(OANDAND, Nod(OEQ, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r)
} else {
r = Nod(OOROR, Nod(ONE, Nod(OITAB, n.Left, nil), Nod(OITAB, n.Right, nil)), r)
}
typecheck(&r, Erv)
walkexpr(&r, init)
r.Type = n.Type
n = r
goto ret
case OARRAYLIT,
OMAPLIT,
OSTRUCTLIT,
OPTRLIT:
var_ = temp(n.Type)
anylit(0, n, var_, init)
n = var_
goto ret
case OSEND:
n1 = n.Right
n1 = assignconv(n1, n.Left.Type.Type, "chan send")
walkexpr(&n1, init)
n1 = Nod(OADDR, n1, nil)
n = mkcall1(chanfn("chansend1", 2, n.Left.Type), nil, init, typename(n.Left.Type), n.Left, n1)
goto ret
case OCLOSURE:
n = walkclosure(n, init)
goto ret
case OCALLPART:
n = walkpartialcall(n, init)
goto ret
}
Fatal("missing switch %v", Oconv(int(n.Op), 0))
// Expressions that are constant at run time but not
// considered const by the language spec are not turned into
// constants until walk. For example, if n is y%1 == 0, the
// walk of y%1 may have replaced it by 0.
// Check whether n with its updated args is itself now a constant.
ret:
t = n.Type
evconst(n)
n.Type = t
if n.Op == OLITERAL {
typecheck(&n, Erv)
}
ullmancalc(n)
if Debug['w'] != 0 && n != nil {
Dump("walk", n)
}
lineno = lno
*np = n
}
func ascompatee1(op int, l *Node, r *Node, init **NodeList) *Node {
var n *Node
// convas will turn map assigns into function calls,
// making it impossible for reorder3 to work.
n = Nod(OAS, l, r)
if l.Op == OINDEXMAP {
return n
}
return convas(n, init)
}
func ascompatee(op int, nl *NodeList, nr *NodeList, init **NodeList) *NodeList {
var ll *NodeList
var lr *NodeList
var nn *NodeList
/*
* check assign expression list to
* a expression list. called in
* expr-list = expr-list
*/
// ensure order of evaluation for function calls
for ll = nl; ll != nil; ll = ll.Next {
ll.N = safeexpr(ll.N, init)
}
for lr = nr; lr != nil; lr = lr.Next {
lr.N = safeexpr(lr.N, init)
}
nn = nil
ll = nl
lr = nr
for ; ll != nil && lr != nil; (func() { ll = ll.Next; lr = lr.Next })() {
// Do not generate 'x = x' during return. See issue 4014.
if op == ORETURN && ll.N == lr.N {
continue
}
nn = list(nn, ascompatee1(op, ll.N, lr.N, init))
}
// cannot happen: caller checked that lists had same length
if ll != nil || lr != nil {
Yyerror("error in shape across %v %v %v / %d %d [%s]", Hconv(nl, obj.FmtSign), Oconv(int(op), 0), Hconv(nr, obj.FmtSign), count(nl), count(nr), Curfn.Nname.Sym.Name)
}
return nn
}
/*
* l is an lv and rt is the type of an rv
* return 1 if this implies a function call
* evaluating the lv or a function call
* in the conversion of the types
*/
func fncall(l *Node, rt *Type) bool {
var r Node
if l.Ullman >= UINF || l.Op == OINDEXMAP {
return true
}
r = Node{}
if needwritebarrier(l, &r) {
return true
}
if Eqtype(l.Type, rt) {
return false
}
return true
}
func ascompatet(op int, nl *NodeList, nr **Type, fp int, init **NodeList) *NodeList {
var l *Node
var tmp *Node
var a *Node
var ll *NodeList
var r *Type
var saver Iter
var ucount int
var nn *NodeList
var mm *NodeList
/*
* check assign type list to
* a expression list. called in
* expr-list = func()
*/
r = Structfirst(&saver, nr)
nn = nil
mm = nil
ucount = 0
for ll = nl; ll != nil; ll = ll.Next {
if r == nil {
break
}
l = ll.N
if isblank(l) {
r = structnext(&saver)
continue
}
// any lv that causes a fn call must be
// deferred until all the return arguments
// have been pulled from the output arguments
if fncall(l, r.Type) {
tmp = temp(r.Type)
typecheck(&tmp, Erv)
a = Nod(OAS, l, tmp)
a = convas(a, init)
mm = list(mm, a)
l = tmp
}
a = Nod(OAS, l, nodarg(r, fp))
a = convas(a, init)
ullmancalc(a)
if a.Ullman >= UINF {
Dump("ascompatet ucount", a)
ucount++
}
nn = list(nn, a)
r = structnext(&saver)
}
if ll != nil || r != nil {
Yyerror("ascompatet: assignment count mismatch: %d = %d", count(nl), structcount(*nr))
}
if ucount != 0 {
Fatal("ascompatet: too many function calls evaluating parameters")
}
return concat(nn, mm)
}
/*
* package all the arguments that match a ... T parameter into a []T.
*/
func mkdotargslice(lr0 *NodeList, nn *NodeList, l *Type, fp int, init **NodeList, ddd *Node) *NodeList {
var a *Node
var n *Node
var tslice *Type
var esc int
esc = EscUnknown
if ddd != nil {
esc = int(ddd.Esc)
}
tslice = typ(TARRAY)
tslice.Type = l.Type.Type
tslice.Bound = -1
if count(lr0) == 0 {
n = nodnil()
n.Type = tslice
} else {
n = Nod(OCOMPLIT, nil, typenod(tslice))
if ddd != nil {
n.Alloc = ddd.Alloc // temporary to use
}
n.List = lr0
n.Esc = uint(esc)
typecheck(&n, Erv)
if n.Type == nil {
Fatal("mkdotargslice: typecheck failed")
}
walkexpr(&n, init)
}
a = Nod(OAS, nodarg(l, fp), n)
nn = list(nn, convas(a, init))
return nn
}
/*
* helpers for shape errors
*/
func dumptypes(nl **Type, what string) string {
var first int
var l *Type
var savel Iter
var fmt_ string
fmt_ = ""
fmt_ += fmt.Sprintf("\t")
first = 1
for l = Structfirst(&savel, nl); l != nil; l = structnext(&savel) {
if first != 0 {
first = 0
} else {
fmt_ += fmt.Sprintf(", ")
}
fmt_ += fmt.Sprintf("%v", Tconv(l, 0))
}
if first != 0 {
fmt_ += fmt.Sprintf("[no arguments %s]", what)
}
return fmt_
}
func dumpnodetypes(l *NodeList, what string) string {
var first int
var r *Node
var fmt_ string
fmt_ = ""
fmt_ += fmt.Sprintf("\t")
first = 1
for ; l != nil; l = l.Next {
r = l.N
if first != 0 {
first = 0
} else {
fmt_ += fmt.Sprintf(", ")
}
fmt_ += fmt.Sprintf("%v", Tconv(r.Type, 0))
}
if first != 0 {
fmt_ += fmt.Sprintf("[no arguments %s]", what)
}
return fmt_
}
/*
* check assign expression list to
* a type list. called in
* return expr-list
* func(expr-list)
*/
func ascompatte(op int, call *Node, isddd int, nl **Type, lr *NodeList, fp int, init **NodeList) *NodeList {
var l *Type
var ll *Type
var r *Node
var a *Node
var nn *NodeList
var lr0 *NodeList
var alist *NodeList
var savel Iter
var l1 string
var l2 string
lr0 = lr
l = Structfirst(&savel, nl)
r = nil
if lr != nil {
r = lr.N
}
nn = nil
// f(g()) where g has multiple return values
if r != nil && lr.Next == nil && r.Type.Etype == TSTRUCT && r.Type.Funarg != 0 {
// optimization - can do block copy
if eqtypenoname(r.Type, *nl) {
a = nodarg(*nl, fp)
r = Nod(OCONVNOP, r, nil)
r.Type = a.Type
nn = list1(convas(Nod(OAS, a, r), init))
goto ret
}
// conversions involved.
// copy into temporaries.
alist = nil
for l = Structfirst(&savel, &r.Type); l != nil; l = structnext(&savel) {
a = temp(l.Type)
alist = list(alist, a)
}
a = Nod(OAS2, nil, nil)
a.List = alist
a.Rlist = lr
typecheck(&a, Etop)
walkstmt(&a)
*init = list(*init, a)
lr = alist
r = lr.N
l = Structfirst(&savel, nl)
}
loop:
if l != nil && l.Isddd != 0 {
// the ddd parameter must be last
ll = structnext(&savel)
if ll != nil {
Yyerror("... must be last argument")
}
// special case --
// only if we are assigning a single ddd
// argument to a ddd parameter then it is
// passed thru unencapsulated
if r != nil && lr.Next == nil && isddd != 0 && Eqtype(l.Type, r.Type) {
a = Nod(OAS, nodarg(l, fp), r)
a = convas(a, init)
nn = list(nn, a)
goto ret
}
// normal case -- make a slice of all
// remaining arguments and pass it to
// the ddd parameter.
nn = mkdotargslice(lr, nn, l, fp, init, call.Right)
goto ret
}
if l == nil || r == nil {
if l != nil || r != nil {
l1 = dumptypes(nl, "expected")
l2 = dumpnodetypes(lr0, "given")
if l != nil {
Yyerror("not enough arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2)
} else {
Yyerror("too many arguments to %v\n%s\n%s", Oconv(int(op), 0), l1, l2)
}
}
goto ret
}
a = Nod(OAS, nodarg(l, fp), r)
a = convas(a, init)
nn = list(nn, a)
l = structnext(&savel)
r = nil
lr = lr.Next
if lr != nil {
r = lr.N
}
goto loop
ret:
for lr = nn; lr != nil; lr = lr.Next {
lr.N.Typecheck = 1
}
return nn
}
// generate code for print
func walkprint(nn *Node, init **NodeList) *Node {
var r *Node
var n *Node
var l *NodeList
var all *NodeList
var on *Node
var t *Type
var notfirst bool
var et int
var op int
var calls *NodeList
op = int(nn.Op)
all = nn.List
calls = nil
notfirst = false
// Hoist all the argument evaluation up before the lock.
walkexprlistcheap(all, init)
calls = list(calls, mkcall("printlock", nil, init))
for l = all; l != nil; l = l.Next {
if notfirst {
calls = list(calls, mkcall("printsp", nil, init))
}
notfirst = op == OPRINTN
n = l.N
if n.Op == OLITERAL {
switch n.Val.Ctype {
case CTRUNE:
defaultlit(&n, runetype)
case CTINT:
defaultlit(&n, Types[TINT64])
case CTFLT:
defaultlit(&n, Types[TFLOAT64])
}
}
if n.Op != OLITERAL && n.Type != nil && n.Type.Etype == TIDEAL {
defaultlit(&n, Types[TINT64])
}
defaultlit(&n, nil)
l.N = n
if n.Type == nil || n.Type.Etype == TFORW {
continue
}
t = n.Type
et = int(n.Type.Etype)
if Isinter(n.Type) {
if isnilinter(n.Type) {
on = syslook("printeface", 1)
} else {
on = syslook("printiface", 1)
}
argtype(on, n.Type) // any-1
} else if Isptr[et] != 0 || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR {
on = syslook("printpointer", 1)
argtype(on, n.Type) // any-1
} else if Isslice(n.Type) {
on = syslook("printslice", 1)
argtype(on, n.Type) // any-1
} else if Isint[et] != 0 {
if et == TUINT64 {
if (t.Sym.Pkg == Runtimepkg || compiling_runtime != 0) && t.Sym.Name == "hex" {
on = syslook("printhex", 0)
} else {
on = syslook("printuint", 0)
}
} else {
on = syslook("printint", 0)
}
} else if Isfloat[et] != 0 {
on = syslook("printfloat", 0)
} else if Iscomplex[et] != 0 {
on = syslook("printcomplex", 0)
} else if et == TBOOL {
on = syslook("printbool", 0)
} else if et == TSTRING {
on = syslook("printstring", 0)
} else {
badtype(OPRINT, n.Type, nil)
continue
}
t = *getinarg(on.Type)
if t != nil {
t = t.Type
}
if t != nil {
t = t.Type
}
if !Eqtype(t, n.Type) {
n = Nod(OCONV, n, nil)
n.Type = t
}
r = Nod(OCALL, on, nil)
r.List = list1(n)
calls = list(calls, r)
}
if op == OPRINTN {
calls = list(calls, mkcall("printnl", nil, nil))
}
calls = list(calls, mkcall("printunlock", nil, init))
typechecklist(calls, Etop)
walkexprlist(calls, init)
r = Nod(OEMPTY, nil, nil)
typecheck(&r, Etop)
walkexpr(&r, init)
r.Ninit = calls
return r
}
func callnew(t *Type) *Node {
var fn *Node
dowidth(t)
fn = syslook("newobject", 1)
argtype(fn, t)
return mkcall1(fn, Ptrto(t), nil, typename(t))
}
func isstack(n *Node) bool {
var defn *Node
n = outervalue(n)
// If n is *autotmp and autotmp = &foo, replace n with foo.
// We introduce such temps when initializing struct literals.
if n.Op == OIND && n.Left.Op == ONAME && strings.HasPrefix(n.Left.Sym.Name, "autotmp_") {
defn = n.Left.Defn
if defn != nil && defn.Op == OAS && defn.Right.Op == OADDR {
n = defn.Right.Left
}
}
switch n.Op {
// OINDREG only ends up in walk if it's indirect of SP.
case OINDREG:
return true
case ONAME:
switch n.Class {
case PAUTO,
PPARAM,
PPARAMOUT:
return true
}
}
return false
}
func isglobal(n *Node) bool {
n = outervalue(n)
switch n.Op {
case ONAME:
switch n.Class {
case PEXTERN:
return true
}
}
return false
}
// Do we need a write barrier for the assignment l = r?
func needwritebarrier(l *Node, r *Node) bool {
if use_writebarrier == 0 {
return false
}
if l == nil || isblank(l) {
return false
}
// No write barrier for write of non-pointers.
dowidth(l.Type)
if !haspointers(l.Type) {
return false
}
// No write barrier for write to stack.
if isstack(l) {
return false
}
// No write barrier for implicit or explicit zeroing.
if r == nil || iszero(r) {
return false
}
// No write barrier for initialization to constant.
if r.Op == OLITERAL {
return false
}
// No write barrier for storing static (read-only) data.
if r.Op == ONAME && strings.HasPrefix(r.Sym.Name, "statictmp_") {
return false
}
// No write barrier for storing address of stack values,
// which are guaranteed only to be written to the stack.
if r.Op == OADDR && isstack(r.Left) {
return false
}
// No write barrier for storing address of global, which
// is live no matter what.
if r.Op == OADDR && isglobal(r.Left) {
return false
}
// No write barrier for reslice: x = x[0:y] or x = append(x, ...).
// Both are compiled to modify x directly.
// In the case of append, a write barrier may still be needed
// if the underlying array grows, but the append code can
// generate the write barrier directly in that case.
// (It does not yet, but the cost of the write barrier will be
// small compared to the cost of the allocation.)
if r.Reslice != 0 {
switch r.Op {
case OSLICE,
OSLICE3,
OSLICESTR,
OAPPEND:
break
default:
Dump("bad reslice-l", l)
Dump("bad reslice-r", r)
}
return false
}
// Otherwise, be conservative and use write barrier.
return true
}
// TODO(rsc): Perhaps componentgen should run before this.
var applywritebarrier_bv *Bvec
func applywritebarrier(n *Node, init **NodeList) *Node {
var l *Node
var r *Node
var t *Type
var x int64
var name string
if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
if Curfn != nil && Curfn.Nowritebarrier {
Yyerror("write barrier prohibited")
}
t = n.Left.Type
l = Nod(OADDR, n.Left, nil)
l.Etype = 1 // addr does not escape
if t.Width == int64(Widthptr) {
n = mkcall1(writebarrierfn("writebarrierptr", t, n.Right.Type), nil, init, l, n.Right)
} else if t.Etype == TSTRING {
n = mkcall1(writebarrierfn("writebarrierstring", t, n.Right.Type), nil, init, l, n.Right)
} else if Isslice(t) {
n = mkcall1(writebarrierfn("writebarrierslice", t, n.Right.Type), nil, init, l, n.Right)
} else if Isinter(t) {
n = mkcall1(writebarrierfn("writebarrieriface", t, n.Right.Type), nil, init, l, n.Right)
} else if t.Width <= int64(4*Widthptr) {
x = 0
if applywritebarrier_bv == nil {
applywritebarrier_bv = bvalloc(obj.BitsPerPointer * 4)
}
bvresetall(applywritebarrier_bv)
twobitwalktype1(t, &x, applywritebarrier_bv)
const (
PtrBit = 1
)
// The bvgets are looking for BitsPointer in successive slots.
if obj.BitsPointer != 1<<PtrBit {
Fatal("wrong PtrBit")
}
switch t.Width / int64(Widthptr) {
default:
Fatal("found writebarrierfat for %d-byte object of type %v", int(t.Width), Tconv(t, 0))
case 2:
name = fmt.Sprintf("writebarrierfat%d%d", bvget(applywritebarrier_bv, PtrBit), bvget(applywritebarrier_bv, obj.BitsPerPointer+PtrBit))
case 3:
name = fmt.Sprintf("writebarrierfat%d%d%d", bvget(applywritebarrier_bv, PtrBit), bvget(applywritebarrier_bv, obj.BitsPerPointer+PtrBit), bvget(applywritebarrier_bv, 2*obj.BitsPerPointer+PtrBit))
case 4:
name = fmt.Sprintf("writebarrierfat%d%d%d%d", bvget(applywritebarrier_bv, PtrBit), bvget(applywritebarrier_bv, obj.BitsPerPointer+PtrBit), bvget(applywritebarrier_bv, 2*obj.BitsPerPointer+PtrBit), bvget(applywritebarrier_bv, 3*obj.BitsPerPointer+PtrBit))
}
n = mkcall1(writebarrierfn(name, t, n.Right.Type), nil, init, l, nodnil(), n.Right)
} else {
r = n.Right
for r.Op == OCONVNOP {
r = r.Left
}
r = Nod(OADDR, r, nil)
r.Etype = 1 // addr does not escape
//warnl(n->lineno, "typedmemmove %T %N", t, r);
n = mkcall1(writebarrierfn("typedmemmove", t, r.Left.Type), nil, init, typename(t), l, r)
}
}
return n
}
func convas(n *Node, init **NodeList) *Node {
var lt *Type
var rt *Type
var map_ *Node
var key *Node
var val *Node
if n.Op != OAS {
Fatal("convas: not OAS %v", Oconv(int(n.Op), 0))
}
n.Typecheck = 1
if n.Left == nil || n.Right == nil {
goto out
}
lt = n.Left.Type
rt = n.Right.Type
if lt == nil || rt == nil {
goto out
}
if isblank(n.Left) {
defaultlit(&n.Right, nil)
goto out
}
if n.Left.Op == OINDEXMAP {
map_ = n.Left.Left
key = n.Left.Right
val = n.Right
walkexpr(&map_, init)
walkexpr(&key, init)
walkexpr(&val, init)
// orderexpr made sure key and val are addressable.
key = Nod(OADDR, key, nil)
val = Nod(OADDR, val, nil)
n = mkcall1(mapfn("mapassign1", map_.Type), nil, init, typename(map_.Type), map_, key, val)
goto out
}
if !Eqtype(lt, rt) {
n.Right = assignconv(n.Right, lt, "assignment")
walkexpr(&n.Right, init)
}
out:
ullmancalc(n)
return n
}
/*
* from ascompat[te]
* evaluating actual function arguments.
* f(a,b)
* if there is exactly one function expr,
* then it is done first. otherwise must
* make temp variables
*/
func reorder1(all *NodeList) *NodeList {
var f *Node
var a *Node
var n *Node
var l *NodeList
var r *NodeList
var g *NodeList
var c int
var d int
var t int
c = 0 // function calls
t = 0 // total parameters
for l = all; l != nil; l = l.Next {
n = l.N
t++
ullmancalc(n)
if n.Ullman >= UINF {
c++
}
}
if c == 0 || t == 1 {
return all
}
g = nil // fncalls assigned to tempnames
f = nil // last fncall assigned to stack
r = nil // non fncalls and tempnames assigned to stack
d = 0
for l = all; l != nil; l = l.Next {
n = l.N
if n.Ullman < UINF {
r = list(r, n)
continue
}
d++
if d == c {
f = n
continue
}
// make assignment of fncall to tempname
a = temp(n.Right.Type)
a = Nod(OAS, a, n.Right)
g = list(g, a)
// put normal arg assignment on list
// with fncall replaced by tempname
n.Right = a.Left
r = list(r, n)
}
if f != nil {
g = list(g, f)
}
return concat(g, r)
}
/*
* from ascompat[ee]
* a,b = c,d
* simultaneous assignment. there cannot
* be later use of an earlier lvalue.
*
* function calls have been removed.
*/
func reorder3(all *NodeList) *NodeList {
var list *NodeList
var early *NodeList
var mapinit *NodeList
var l *Node
// If a needed expression may be affected by an
// earlier assignment, make an early copy of that
// expression and use the copy instead.
early = nil
mapinit = nil
for list = all; list != nil; list = list.Next {
l = list.N.Left
// Save subexpressions needed on left side.
// Drill through non-dereferences.
for {
if l.Op == ODOT || l.Op == OPAREN {
l = l.Left
continue
}
if l.Op == OINDEX && Isfixedarray(l.Left.Type) {
reorder3save(&l.Right, all, list, &early)
l = l.Left
continue
}
break
}
switch l.Op {
default:
Fatal("reorder3 unexpected lvalue %v", Oconv(int(l.Op), obj.FmtSharp))
case ONAME:
break
case OINDEX,
OINDEXMAP:
reorder3save(&l.Left, all, list, &early)
reorder3save(&l.Right, all, list, &early)
if l.Op == OINDEXMAP {
list.N = convas(list.N, &mapinit)
}
case OIND,
ODOTPTR:
reorder3save(&l.Left, all, list, &early)
}
// Save expression on right side.
reorder3save(&list.N.Right, all, list, &early)
}
early = concat(mapinit, early)
return concat(early, all)
}
/*
* if the evaluation of *np would be affected by the
* assignments in all up to but not including stop,
* copy into a temporary during *early and
* replace *np with that temp.
*/
func reorder3save(np **Node, all *NodeList, stop *NodeList, early **NodeList) {
var n *Node
var q *Node
n = *np
if !aliased(n, all, stop) {
return
}
q = temp(n.Type)
q = Nod(OAS, q, n)
typecheck(&q, Etop)
*early = list(*early, q)
*np = q.Left
}
/*
* what's the outer value that a write to n affects?
* outer value means containing struct or array.
*/
func outervalue(n *Node) *Node {
for {
if n.Op == OXDOT {
Fatal("OXDOT in walk")
}
if n.Op == ODOT || n.Op == OPAREN || n.Op == OCONVNOP {
n = n.Left
continue
}
if n.Op == OINDEX && Isfixedarray(n.Left.Type) {
n = n.Left
continue
}
break
}
return n
}
/*
* Is it possible that the computation of n might be
* affected by writes in as up to but not including stop?
*/
func aliased(n *Node, all *NodeList, stop *NodeList) bool {
var memwrite int
var varwrite int
var a *Node
var l *NodeList
if n == nil {
return false
}
// Look for obvious aliasing: a variable being assigned
// during the all list and appearing in n.
// Also record whether there are any writes to main memory.
// Also record whether there are any writes to variables
// whose addresses have been taken.
memwrite = 0
varwrite = 0
for l = all; l != stop; l = l.Next {
a = outervalue(l.N.Left)
if a.Op != ONAME {
memwrite = 1
continue
}
switch n.Class {
default:
varwrite = 1
continue
case PAUTO,
PPARAM,
PPARAMOUT:
if n.Addrtaken != 0 {
varwrite = 1
continue
}
if vmatch2(a, n) {
// Direct hit.
return true
}
}
}
// The variables being written do not appear in n.
// However, n might refer to computed addresses
// that are being written.
// If no computed addresses are affected by the writes, no aliasing.
if memwrite == 0 && varwrite == 0 {
return false
}
// If n does not refer to computed addresses
// (that is, if n only refers to variables whose addresses
// have not been taken), no aliasing.
if varexpr(n) {
return false
}
// Otherwise, both the writes and n refer to computed memory addresses.
// Assume that they might conflict.
return true
}
/*
* does the evaluation of n only refer to variables
* whose addresses have not been taken?
* (and no other memory)
*/
func varexpr(n *Node) bool {
if n == nil {
return true
}
switch n.Op {
case OLITERAL:
return true
case ONAME:
switch n.Class {
case PAUTO,
PPARAM,
PPARAMOUT:
if n.Addrtaken == 0 {
return true
}
}
return false
case OADD,
OSUB,
OOR,
OXOR,
OMUL,
ODIV,
OMOD,
OLSH,
ORSH,
OAND,
OANDNOT,
OPLUS,
OMINUS,
OCOM,
OPAREN,
OANDAND,
OOROR,
ODOT, // but not ODOTPTR
OCONV,
OCONVNOP,
OCONVIFACE,
ODOTTYPE:
return varexpr(n.Left) && varexpr(n.Right)
}
// Be conservative.
return false
}
/*
* is the name l mentioned in r?
*/
func vmatch2(l *Node, r *Node) bool {
var ll *NodeList
if r == nil {
return false
}
switch r.Op {
// match each right given left
case ONAME:
return l == r
case OLITERAL:
return false
}
if vmatch2(l, r.Left) {
return true
}
if vmatch2(l, r.Right) {
return true
}
for ll = r.List; ll != nil; ll = ll.Next {
if vmatch2(l, ll.N) {
return true
}
}
return false
}
/*
* is any name mentioned in l also mentioned in r?
* called by sinit.c
*/
func vmatch1(l *Node, r *Node) bool {
var ll *NodeList
/*
* isolate all left sides
*/
if l == nil || r == nil {
return false
}
switch l.Op {
case ONAME:
switch l.Class {
case PPARAM,
PPARAMREF,
PAUTO:
break
// assignment to non-stack variable
// must be delayed if right has function calls.
default:
if r.Ullman >= UINF {
return true
}
}
return vmatch2(l, r)
case OLITERAL:
return false
}
if vmatch1(l.Left, r) {
return true
}
if vmatch1(l.Right, r) {
return true
}
for ll = l.List; ll != nil; ll = ll.Next {
if vmatch1(ll.N, r) {
return true
}
}
return false
}
/*
* walk through argin parameters.
* generate and return code to allocate
* copies of escaped parameters to the heap.
*/
func paramstoheap(argin **Type, out int) *NodeList {
var t *Type
var savet Iter
var v *Node
var as *Node
var nn *NodeList
nn = nil
for t = Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
v = t.Nname
if v != nil && v.Sym != nil && v.Sym.Name[0] == '~' && v.Sym.Name[1] == 'r' { // unnamed result
v = nil
}
// For precise stacks, the garbage collector assumes results
// are always live, so zero them always.
if out != 0 {
// Defer might stop a panic and show the
// return values as they exist at the time of panic.
// Make sure to zero them on entry to the function.
nn = list(nn, Nod(OAS, nodarg(t, 1), nil))
}
if v == nil || v.Class&PHEAP == 0 {
continue
}
// generate allocation & copying code
if compiling_runtime != 0 {
Yyerror("%v escapes to heap, not allowed in runtime.", Nconv(v, 0))
}
if v.Alloc == nil {
v.Alloc = callnew(v.Type)
}
nn = list(nn, Nod(OAS, v.Heapaddr, v.Alloc))
if v.Class&^PHEAP != PPARAMOUT {
as = Nod(OAS, v, v.Stackparam)
v.Stackparam.Typecheck = 1
typecheck(&as, Etop)
as = applywritebarrier(as, &nn)
nn = list(nn, as)
}
}
return nn
}
/*
* walk through argout parameters copying back to stack
*/
func returnsfromheap(argin **Type) *NodeList {
var t *Type
var savet Iter
var v *Node
var nn *NodeList
nn = nil
for t = Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
v = t.Nname
if v == nil || v.Class != PHEAP|PPARAMOUT {
continue
}
nn = list(nn, Nod(OAS, v.Stackparam, v))
}
return nn
}
/*
* take care of migrating any function in/out args
* between the stack and the heap. adds code to
* curfn's before and after lists.
*/
func heapmoves() {
var nn *NodeList
var lno int32
lno = lineno
lineno = Curfn.Lineno
nn = paramstoheap(getthis(Curfn.Type), 0)
nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0))
nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1))
Curfn.Enter = concat(Curfn.Enter, nn)
lineno = Curfn.Endlineno
Curfn.Exit = returnsfromheap(Getoutarg(Curfn.Type))
lineno = lno
}
func vmkcall(fn *Node, t *Type, init **NodeList, va []*Node) *Node {
var i int
var n int
var r *Node
var args *NodeList
if fn.Type == nil || fn.Type.Etype != TFUNC {
Fatal("mkcall %v %v", Nconv(fn, 0), Tconv(fn.Type, 0))
}
args = nil
n = fn.Type.Intuple
for i = 0; i < n; i++ {
args = list(args, va[i])
}
r = Nod(OCALL, fn, nil)
r.List = args
if fn.Type.Outtuple > 0 {
typecheck(&r, Erv|Efnstruct)
} else {
typecheck(&r, Etop)
}
walkexpr(&r, init)
r.Type = t
return r
}
func mkcall(name string, t *Type, init **NodeList, args ...*Node) *Node {
return vmkcall(syslook(name, 0), t, init, args)
}
func mkcall1(fn *Node, t *Type, init **NodeList, args ...*Node) *Node {
return vmkcall(fn, t, init, args)
}
func conv(n *Node, t *Type) *Node {
if Eqtype(n.Type, t) {
return n
}
n = Nod(OCONV, n, nil)
n.Type = t
typecheck(&n, Erv)
return n
}
func chanfn(name string, n int, t *Type) *Node {
var fn *Node
var i int
if t.Etype != TCHAN {
Fatal("chanfn %v", Tconv(t, 0))
}
fn = syslook(name, 1)
for i = 0; i < n; i++ {
argtype(fn, t.Type)
}
return fn
}
func mapfn(name string, t *Type) *Node {
var fn *Node
if t.Etype != TMAP {
Fatal("mapfn %v", Tconv(t, 0))
}
fn = syslook(name, 1)
argtype(fn, t.Down)
argtype(fn, t.Type)
argtype(fn, t.Down)
argtype(fn, t.Type)
return fn
}
func mapfndel(name string, t *Type) *Node {
var fn *Node
if t.Etype != TMAP {
Fatal("mapfn %v", Tconv(t, 0))
}
fn = syslook(name, 1)
argtype(fn, t.Down)
argtype(fn, t.Type)
argtype(fn, t.Down)
return fn
}
func writebarrierfn(name string, l *Type, r *Type) *Node {
var fn *Node
fn = syslook(name, 1)
argtype(fn, l)
argtype(fn, r)
return fn
}
func addstr(n *Node, init **NodeList) *Node {
var r *Node
var cat *Node
var slice *Node
var buf *Node
var args *NodeList
var l *NodeList
var c int
var sz int64
var t *Type
// orderexpr rewrote OADDSTR to have a list of strings.
c = count(n.List)
if c < 2 {
Yyerror("addstr count %d too small", c)
}
buf = nodnil()
if n.Esc == EscNone {
sz = 0
for l = n.List; l != nil; l = l.Next {
if n.Op == OLITERAL {
sz += int64(len(n.Val.U.Sval.S))
}
}
// Don't allocate the buffer if the result won't fit.
if sz < tmpstringbufsize {
// Create temporary buffer for result string on stack.
t = aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
buf = Nod(OADDR, temp(t), nil)
}
}
// build list of string arguments
args = list1(buf)
for l = n.List; l != nil; l = l.Next {
args = list(args, conv(l.N, Types[TSTRING]))
}
if c <= 5 {
// small numbers of strings use direct runtime helpers.
// note: orderexpr knows this cutoff too.
namebuf = fmt.Sprintf("concatstring%d", c)
} else {
// large numbers of strings are passed to the runtime as a slice.
namebuf = "concatstrings"
t = typ(TARRAY)
t.Type = Types[TSTRING]
t.Bound = -1
slice = Nod(OCOMPLIT, nil, typenod(t))
slice.Alloc = n.Alloc
slice.List = args.Next // skip buf arg
args = list1(buf)
args = list(args, slice)
slice.Esc = EscNone
}
cat = syslook(namebuf, 1)
r = Nod(OCALL, cat, nil)
r.List = args
typecheck(&r, Erv)
walkexpr(&r, init)
r.Type = n.Type
return r
}
// expand append(l1, l2...) to
// init {
// s := l1
// if n := len(l1) + len(l2) - cap(s); n > 0 {
// s = growslice(s, n)
// }
// s = s[:len(l1)+len(l2)]
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
// }
// s
//
// l2 is allowed to be a string.
func appendslice(n *Node, init **NodeList) *Node {
var l *NodeList
var l1 *Node
var l2 *Node
var nt *Node
var nif *Node
var fn *Node
var nptr1 *Node
var nptr2 *Node
var nwid *Node
var s *Node
walkexprlistsafe(n.List, init)
// walkexprlistsafe will leave OINDEX (s[n]) alone if both s
// and n are name or literal, but those may index the slice we're
// modifying here. Fix explicitly.
for l = n.List; l != nil; l = l.Next {
l.N = cheapexpr(l.N, init)
}
l1 = n.List.N
l2 = n.List.Next.N
s = temp(l1.Type) // var s []T
l = nil
l = list(l, Nod(OAS, s, l1)) // s = l1
nt = temp(Types[TINT])
nif = Nod(OIF, nil, nil)
// n := len(s) + len(l2) - cap(s)
nif.Ninit = list1(Nod(OAS, nt, Nod(OSUB, Nod(OADD, Nod(OLEN, s, nil), Nod(OLEN, l2, nil)), Nod(OCAP, s, nil))))
nif.Ntest = Nod(OGT, nt, Nodintconst(0))
// instantiate growslice(Type*, []any, int64) []any
fn = syslook("growslice", 1)
argtype(fn, s.Type.Type)
argtype(fn, s.Type.Type)
// s = growslice(T, s, n)
nif.Nbody = list1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, conv(nt, Types[TINT64]))))
l = list(l, nif)
if haspointers(l1.Type.Type) {
// copy(s[len(l1):len(l1)+len(l2)], l2)
nptr1 = Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil))))
nptr1.Etype = 1
nptr2 = l2
fn = syslook("typedslicecopy", 1)
argtype(fn, l1.Type)
argtype(fn, l2.Type)
nt = mkcall1(fn, Types[TINT], &l, typename(l1.Type.Type), nptr1, nptr2)
l = list(l, nt)
} else if flag_race != 0 {
// rely on runtime to instrument copy.
// copy(s[len(l1):len(l1)+len(l2)], l2)
nptr1 = Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil))))
nptr1.Etype = 1
nptr2 = l2
if l2.Type.Etype == TSTRING {
fn = syslook("slicestringcopy", 1)
} else {
fn = syslook("slicecopy", 1)
}
argtype(fn, l1.Type)
argtype(fn, l2.Type)
nt = mkcall1(fn, Types[TINT], &l, nptr1, nptr2, Nodintconst(s.Type.Type.Width))
l = list(l, nt)
} else {
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
nptr1 = Nod(OINDEX, s, Nod(OLEN, l1, nil))
nptr1.Bounded = true
nptr1 = Nod(OADDR, nptr1, nil)
nptr2 = Nod(OSPTR, l2, nil)
fn = syslook("memmove", 1)
argtype(fn, s.Type.Type) // 1 old []any
argtype(fn, s.Type.Type) // 2 ret []any
nwid = cheapexpr(conv(Nod(OLEN, l2, nil), Types[TUINTPTR]), &l)
nwid = Nod(OMUL, nwid, Nodintconst(s.Type.Type.Width))
nt = mkcall1(fn, nil, &l, nptr1, nptr2, nwid)
l = list(l, nt)
}
// s = s[:len(l1)+len(l2)]
nt = Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil))
nt = Nod(OSLICE, s, Nod(OKEY, nil, nt))
nt.Etype = 1
l = list(l, Nod(OAS, s, nt))
typechecklist(l, Etop)
walkstmtlist(l)
*init = concat(*init, l)
return s
}
// expand append(src, a [, b]* ) to
//
// init {
// s := src
// const argc = len(args) - 1
// if cap(s) - len(s) < argc {
// s = growslice(s, argc)
// }
// n := len(s)
// s = s[:n+argc]
// s[n] = a
// s[n+1] = b
// ...
// }
// s
func walkappend(n *Node, init **NodeList) *Node {
var l *NodeList
var a *NodeList
var nsrc *Node
var ns *Node
var nn *Node
var na *Node
var nx *Node
var fn *Node
var argc int
walkexprlistsafe(n.List, init)
// walkexprlistsafe will leave OINDEX (s[n]) alone if both s
// and n are name or literal, but those may index the slice we're
// modifying here. Fix explicitly.
for l = n.List; l != nil; l = l.Next {
l.N = cheapexpr(l.N, init)
}
nsrc = n.List.N
// Resolve slice type of multi-valued return.
if Istype(nsrc.Type, TSTRUCT) {
nsrc.Type = nsrc.Type.Type.Type
}
argc = count(n.List) - 1
if argc < 1 {
return nsrc
}
l = nil
ns = temp(nsrc.Type)
l = list(l, Nod(OAS, ns, nsrc)) // s = src
na = Nodintconst(int64(argc)) // const argc
nx = Nod(OIF, nil, nil) // if cap(s) - len(s) < argc
nx.Ntest = Nod(OLT, Nod(OSUB, Nod(OCAP, ns, nil), Nod(OLEN, ns, nil)), na)
fn = syslook("growslice", 1) // growslice(<type>, old []T, n int64) (ret []T)
argtype(fn, ns.Type.Type) // 1 old []any
argtype(fn, ns.Type.Type) // 2 ret []any
nx.Nbody = list1(Nod(OAS, ns, mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns, conv(na, Types[TINT64]))))
l = list(l, nx)
nn = temp(Types[TINT])
l = list(l, Nod(OAS, nn, Nod(OLEN, ns, nil))) // n = len(s)
nx = Nod(OSLICE, ns, Nod(OKEY, nil, Nod(OADD, nn, na))) // ...s[:n+argc]
nx.Etype = 1
l = list(l, Nod(OAS, ns, nx)) // s = s[:n+argc]
for a = n.List.Next; a != nil; a = a.Next {
nx = Nod(OINDEX, ns, nn) // s[n] ...
nx.Bounded = true
l = list(l, Nod(OAS, nx, a.N)) // s[n] = arg
if a.Next != nil {
l = list(l, Nod(OAS, nn, Nod(OADD, nn, Nodintconst(1)))) // n = n + 1
}
}
typechecklist(l, Etop)
walkstmtlist(l)
*init = concat(*init, l)
return ns
}
// Lower copy(a, b) to a memmove call or a runtime call.
//
// init {
// n := len(a)
// if n > len(b) { n = len(b) }
// memmove(a.ptr, b.ptr, n*sizeof(elem(a)))
// }
// n;
//
// Also works if b is a string.
//
func copyany(n *Node, init **NodeList, runtimecall int) *Node {
var nl *Node
var nr *Node
var nfrm *Node
var nto *Node
var nif *Node
var nlen *Node
var nwid *Node
var fn *Node
var l *NodeList
if haspointers(n.Left.Type.Type) {
fn = writebarrierfn("typedslicecopy", n.Left.Type, n.Right.Type)
return mkcall1(fn, n.Type, init, typename(n.Left.Type.Type), n.Left, n.Right)
}
if runtimecall != 0 {
if n.Right.Type.Etype == TSTRING {
fn = syslook("slicestringcopy", 1)
} else {
fn = syslook("slicecopy", 1)
}
argtype(fn, n.Left.Type)
argtype(fn, n.Right.Type)
return mkcall1(fn, n.Type, init, n.Left, n.Right, Nodintconst(n.Left.Type.Type.Width))
}
walkexpr(&n.Left, init)
walkexpr(&n.Right, init)
nl = temp(n.Left.Type)
nr = temp(n.Right.Type)
l = nil
l = list(l, Nod(OAS, nl, n.Left))
l = list(l, Nod(OAS, nr, n.Right))
nfrm = Nod(OSPTR, nr, nil)
nto = Nod(OSPTR, nl, nil)
nlen = temp(Types[TINT])
// n = len(to)
l = list(l, Nod(OAS, nlen, Nod(OLEN, nl, nil)))
// if n > len(frm) { n = len(frm) }
nif = Nod(OIF, nil, nil)
nif.Ntest = Nod(OGT, nlen, Nod(OLEN, nr, nil))
nif.Nbody = list(nif.Nbody, Nod(OAS, nlen, Nod(OLEN, nr, nil)))
l = list(l, nif)
// Call memmove.
fn = syslook("memmove", 1)
argtype(fn, nl.Type.Type)
argtype(fn, nl.Type.Type)
nwid = temp(Types[TUINTPTR])
l = list(l, Nod(OAS, nwid, conv(nlen, Types[TUINTPTR])))
nwid = Nod(OMUL, nwid, Nodintconst(nl.Type.Type.Width))
l = list(l, mkcall1(fn, nil, init, nto, nfrm, nwid))
typechecklist(l, Etop)
walkstmtlist(l)
*init = concat(*init, l)
return nlen
}
// Generate frontend part for OSLICE[3][ARR|STR]
//
func sliceany(n *Node, init **NodeList) *Node {
var bounded int
var slice3 bool
var src *Node
var lb *Node
var hb *Node
var cb *Node
var bound *Node
var chk *Node
var chk0 *Node
var chk1 *Node
var chk2 *Node
var lbv int64
var hbv int64
var cbv int64
var bv int64
var w int64
var bt *Type
// print("before sliceany: %+N\n", n);
src = n.Left
lb = n.Right.Left
slice3 = n.Op == OSLICE3 || n.Op == OSLICE3ARR
if slice3 {
hb = n.Right.Right.Left
cb = n.Right.Right.Right
} else {
hb = n.Right.Right
cb = nil
}
bounded = int(n.Etype)
if n.Op == OSLICESTR {
bound = Nod(OLEN, src, nil)
} else {
bound = Nod(OCAP, src, nil)
}
typecheck(&bound, Erv)
walkexpr(&bound, init) // if src is an array, bound will be a const now.
// static checks if possible
bv = 1 << 50
if Isconst(bound, CTINT) {
if !Smallintconst(bound) {
Yyerror("array len too large")
} else {
bv = Mpgetfix(bound.Val.U.Xval)
}
}
if Isconst(cb, CTINT) {
cbv = Mpgetfix(cb.Val.U.Xval)
if cbv < 0 || cbv > bv {
Yyerror("slice index out of bounds")
}
}
if Isconst(hb, CTINT) {
hbv = Mpgetfix(hb.Val.U.Xval)
if hbv < 0 || hbv > bv {
Yyerror("slice index out of bounds")
}
}
if Isconst(lb, CTINT) {
lbv = Mpgetfix(lb.Val.U.Xval)
if lbv < 0 || lbv > bv {
Yyerror("slice index out of bounds")
lbv = -1
}
if lbv == 0 {
lb = nil
}
}
// Checking src[lb:hb:cb] or src[lb:hb].
// if chk0 || chk1 || chk2 { panicslice() }
chk = nil
chk0 = nil // cap(src) < cb
chk1 = nil // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
chk2 = nil // hb < lb
// All comparisons are unsigned to avoid testing < 0.
bt = Types[Simtype[TUINT]]
if cb != nil && cb.Type.Width > 4 {
bt = Types[TUINT64]
}
if hb != nil && hb.Type.Width > 4 {
bt = Types[TUINT64]
}
if lb != nil && lb.Type.Width > 4 {
bt = Types[TUINT64]
}
bound = cheapexpr(conv(bound, bt), init)
if cb != nil {
cb = cheapexpr(conv(cb, bt), init)
if bounded == 0 {
chk0 = Nod(OLT, bound, cb)
}
} else if slice3 {
// When we figure out what this means, implement it.
Fatal("slice3 with cb == N") // rejected by parser
}
if hb != nil {
hb = cheapexpr(conv(hb, bt), init)
if bounded == 0 {
if cb != nil {
chk1 = Nod(OLT, cb, hb)
} else {
chk1 = Nod(OLT, bound, hb)
}
}
} else if slice3 {
// When we figure out what this means, implement it.
Fatal("slice3 with hb == N") // rejected by parser
} else if n.Op == OSLICEARR {
hb = bound
} else {
hb = Nod(OLEN, src, nil)
typecheck(&hb, Erv)
walkexpr(&hb, init)
hb = cheapexpr(conv(hb, bt), init)
}
if lb != nil {
lb = cheapexpr(conv(lb, bt), init)
if bounded == 0 {
chk2 = Nod(OLT, hb, lb)
}
}
if chk0 != nil || chk1 != nil || chk2 != nil {
chk = Nod(OIF, nil, nil)
chk.Nbody = list1(mkcall("panicslice", nil, init))
chk.Likely = -1
if chk0 != nil {
chk.Ntest = chk0
}
if chk1 != nil {
if chk.Ntest == nil {
chk.Ntest = chk1
} else {
chk.Ntest = Nod(OOROR, chk.Ntest, chk1)
}
}
if chk2 != nil {
if chk.Ntest == nil {
chk.Ntest = chk2
} else {
chk.Ntest = Nod(OOROR, chk.Ntest, chk2)
}
}
typecheck(&chk, Etop)
walkstmt(&chk)
*init = concat(*init, chk.Ninit)
chk.Ninit = nil
*init = list(*init, chk)
}
// prepare new cap, len and offs for backend cgen_slice
// cap = bound [ - lo ]
n.Right = nil
n.List = nil
if !slice3 {
cb = bound
}
if lb == nil {
bound = conv(cb, Types[Simtype[TUINT]])
} else {
bound = Nod(OSUB, conv(cb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]]))
}
typecheck(&bound, Erv)
walkexpr(&bound, init)
n.List = list(n.List, bound)
// len = hi [ - lo]
if lb == nil {
hb = conv(hb, Types[Simtype[TUINT]])
} else {
hb = Nod(OSUB, conv(hb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]]))
}
typecheck(&hb, Erv)
walkexpr(&hb, init)
n.List = list(n.List, hb)
// offs = [width *] lo, but omit if zero
if lb != nil {
if n.Op == OSLICESTR {
w = 1
} else {
w = n.Type.Type.Width
}
lb = conv(lb, Types[TUINTPTR])
if w > 1 {
lb = Nod(OMUL, Nodintconst(w), lb)
}
typecheck(&lb, Erv)
walkexpr(&lb, init)
n.List = list(n.List, lb)
}
// print("after sliceany: %+N\n", n);
return n
}
func eqfor(t *Type, needsize *int) *Node {
var a int
var n *Node
var ntype *Node
var sym *Sym
// Should only arrive here with large memory or
// a struct/array containing a non-memory field/element.
// Small memory is handled inline, and single non-memory
// is handled during type check (OCMPSTR etc).
a = algtype1(t, nil)
if a != AMEM && a != -1 {
Fatal("eqfor %v", Tconv(t, 0))
}
if a == AMEM {
n = syslook("memequal", 1)
argtype(n, t)
argtype(n, t)
*needsize = 1
return n
}
sym = typesymprefix(".eq", t)
n = newname(sym)
n.Class = PFUNC
ntype = Nod(OTFUNC, nil, nil)
ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
ntype.List = list(ntype.List, Nod(ODCLFIELD, nil, typenod(Ptrto(t))))
ntype.Rlist = list(ntype.Rlist, Nod(ODCLFIELD, nil, typenod(Types[TBOOL])))
typecheck(&ntype, Etype)
n.Type = ntype.Type
*needsize = 0
return n
}
func countfield(t *Type) int {
var t1 *Type
var n int
n = 0
for t1 = t.Type; t1 != nil; t1 = t1.Down {
n++
}
return n
}
func walkcompare(np **Node, init **NodeList) {
var n *Node
var l *Node
var r *Node
var call *Node
var a *Node
var li *Node
var ri *Node
var expr *Node
var cmpl *Node
var cmpr *Node
var x *Node
var ok *Node
var andor int
var i int
var needsize int
var t *Type
var t1 *Type
n = *np
// Given interface value l and concrete value r, rewrite
// l == r
// to
// x, ok := l.(type(r)); ok && x == r
// Handle != similarly.
// This avoids the allocation that would be required
// to convert r to l for comparison.
l = nil
r = nil
if Isinter(n.Left.Type) && !Isinter(n.Right.Type) {
l = n.Left
r = n.Right
} else if !Isinter(n.Left.Type) && Isinter(n.Right.Type) {
l = n.Right
r = n.Left
}
if l != nil {
x = temp(r.Type)
ok = temp(Types[TBOOL])
// l.(type(r))
a = Nod(ODOTTYPE, l, nil)
a.Type = r.Type
// x, ok := l.(type(r))
expr = Nod(OAS2, nil, nil)
expr.List = list1(x)
expr.List = list(expr.List, ok)
expr.Rlist = list1(a)
typecheck(&expr, Etop)
walkexpr(&expr, init)
if n.Op == OEQ {
r = Nod(OANDAND, ok, Nod(OEQ, x, r))
} else {
r = Nod(OOROR, Nod(ONOT, ok, nil), Nod(ONE, x, r))
}
*init = list(*init, expr)
goto ret
}
// Must be comparison of array or struct.
// Otherwise back end handles it.
t = n.Left.Type
switch t.Etype {
default:
return
case TARRAY:
if Isslice(t) {
return
}
case TSTRUCT:
break
}
cmpl = n.Left
for cmpl != nil && cmpl.Op == OCONVNOP {
cmpl = cmpl.Left
}
cmpr = n.Right
for cmpr != nil && cmpr.Op == OCONVNOP {
cmpr = cmpr.Left
}
if !islvalue(cmpl) || !islvalue(cmpr) {
Fatal("arguments of comparison must be lvalues - %v %v", Nconv(cmpl, 0), Nconv(cmpr, 0))
}
l = temp(Ptrto(t))
a = Nod(OAS, l, Nod(OADDR, cmpl, nil))
a.Right.Etype = 1 // addr does not escape
typecheck(&a, Etop)
*init = list(*init, a)
r = temp(Ptrto(t))
a = Nod(OAS, r, Nod(OADDR, cmpr, nil))
a.Right.Etype = 1 // addr does not escape
typecheck(&a, Etop)
*init = list(*init, a)
expr = nil
andor = OANDAND
if n.Op == ONE {
andor = OOROR
}
if t.Etype == TARRAY && t.Bound <= 4 && issimple[t.Type.Etype] != 0 {
// Four or fewer elements of a basic type.
// Unroll comparisons.
for i = 0; int64(i) < t.Bound; i++ {
li = Nod(OINDEX, l, Nodintconst(int64(i)))
ri = Nod(OINDEX, r, Nodintconst(int64(i)))
a = Nod(int(n.Op), li, ri)
if expr == nil {
expr = a
} else {
expr = Nod(andor, expr, a)
}
}
if expr == nil {
expr = Nodbool(n.Op == OEQ)
}
r = expr
goto ret
}
if t.Etype == TSTRUCT && countfield(t) <= 4 {
// Struct of four or fewer fields.
// Inline comparisons.
for t1 = t.Type; t1 != nil; t1 = t1.Down {
if isblanksym(t1.Sym) {
continue
}
li = Nod(OXDOT, l, newname(t1.Sym))
ri = Nod(OXDOT, r, newname(t1.Sym))
a = Nod(int(n.Op), li, ri)
if expr == nil {
expr = a
} else {
expr = Nod(andor, expr, a)
}
}
if expr == nil {
expr = Nodbool(n.Op == OEQ)
}
r = expr
goto ret
}
// Chose not to inline. Call equality function directly.
call = Nod(OCALL, eqfor(t, &needsize), nil)
call.List = list(call.List, l)
call.List = list(call.List, r)
if needsize != 0 {
call.List = list(call.List, Nodintconst(t.Width))
}
r = call
if n.Op != OEQ {
r = Nod(ONOT, r, nil)
}
goto ret
ret:
typecheck(&r, Erv)
walkexpr(&r, init)
if r.Type != n.Type {
r = Nod(OCONVNOP, r, nil)
r.Type = n.Type
r.Typecheck = 1
}
*np = r
return
}
func samecheap(a *Node, b *Node) bool {
var ar *Node
var br *Node
for a != nil && b != nil && a.Op == b.Op {
switch a.Op {
default:
return false
case ONAME:
return a == b
case ODOT,
ODOTPTR:
ar = a.Right
br = b.Right
if ar.Op != ONAME || br.Op != ONAME || ar.Sym != br.Sym {
return false
}
case OINDEX:
ar = a.Right
br = b.Right
if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.Xval, br.Val.U.Xval) != 0 {
return false
}
}
a = a.Left
b = b.Left
}
return false
}
func walkrotate(np **Node) {
var w int
var sl int
var sr int
var s int
var l *Node
var r *Node
var n *Node
if Thearch.Thechar == '9' {
return
}
n = *np
// Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value.
l = n.Left
r = n.Right
if (n.Op != OOR && n.Op != OXOR) || (l.Op != OLSH && l.Op != ORSH) || (r.Op != OLSH && r.Op != ORSH) || n.Type == nil || Issigned[n.Type.Etype] != 0 || l.Op == r.Op {
return
}
// Want same, side effect-free expression on lhs of both shifts.
if !samecheap(l.Left, r.Left) {
return
}
// Constants adding to width?
w = int(l.Type.Width * 8)
if Smallintconst(l.Right) && Smallintconst(r.Right) {
sl = int(Mpgetfix(l.Right.Val.U.Xval))
if sl >= 0 {
sr = int(Mpgetfix(r.Right.Val.U.Xval))
if sr >= 0 && sl+sr == w {
goto yes
}
}
return
}
// TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31).
return
// Rewrite left shift half to left rotate.
yes:
if l.Op == OLSH {
n = l
} else {
n = r
}
n.Op = OLROT
// Remove rotate 0 and rotate w.
s = int(Mpgetfix(n.Right.Val.U.Xval))
if s == 0 || s == w {
n = n.Left
}
*np = n
return
}
/*
* walkmul rewrites integer multiplication by powers of two as shifts.
*/
func walkmul(np **Node, init **NodeList) {
var n *Node
var nl *Node
var nr *Node
var pow int
var neg int
var w int
n = *np
if Isint[n.Type.Etype] == 0 {
return
}
if n.Right.Op == OLITERAL {
nl = n.Left
nr = n.Right
} else if n.Left.Op == OLITERAL {
nl = n.Right
nr = n.Left
} else {
return
}
neg = 0
// x*0 is 0 (and side effects of x).
if Mpgetfix(nr.Val.U.Xval) == 0 {
cheapexpr(nl, init)
Nodconst(n, n.Type, 0)
goto ret
}
// nr is a constant.
pow = powtwo(nr)
if pow < 0 {
return
}
if pow >= 1000 {
// negative power of 2, like -16
neg = 1
pow -= 1000
}
w = int(nl.Type.Width * 8)
if pow+1 >= w { // too big, shouldn't happen
return
}
nl = cheapexpr(nl, init)
if pow == 0 {
// x*1 is x
n = nl
goto ret
}
n = Nod(OLSH, nl, Nodintconst(int64(pow)))
ret:
if neg != 0 {
n = Nod(OMINUS, n, nil)
}
typecheck(&n, Erv)
walkexpr(&n, init)
*np = n
}
/*
* walkdiv rewrites division by a constant as less expensive
* operations.
*/
func walkdiv(np **Node, init **NodeList) {
var n *Node
var nl *Node
var nr *Node
// if >= 0, nr is 1<<pow // 1 if nr is negative.
var nc *Node
var n1 *Node
var n2 *Node
var n3 *Node
var n4 *Node
var pow int
var s int
var w int
var twide *Type
var m Magic
// TODO(minux)
if Thearch.Thechar == '9' {
return
}
n = *np
if n.Right.Op != OLITERAL {
return
}
// nr is a constant.
nl = cheapexpr(n.Left, init)
nr = n.Right
// special cases of mod/div
// by a constant
w = int(nl.Type.Width * 8)
s = 0
pow = powtwo(nr)
if pow >= 1000 {
// negative power of 2
s = 1
pow -= 1000
}
if pow+1 >= w {
// divisor too large.
return
}
if pow < 0 {
goto divbymul
}
switch pow {
case 0:
if n.Op == OMOD {
// nl % 1 is zero.
Nodconst(n, n.Type, 0)
} else if s != 0 {
// divide by -1
n.Op = OMINUS
n.Right = nil
} else {
// divide by 1
n = nl
}
default:
if Issigned[n.Type.Etype] != 0 {
if n.Op == OMOD {
// signed modulo 2^pow is like ANDing
// with the last pow bits, but if nl < 0,
// nl & (2^pow-1) is (nl+1)%2^pow - 1.
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1)
n1 = Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0.
if pow == 1 {
typecheck(&n1, Erv)
n1 = cheapexpr(n1, init)
// n = (nl+ε)&1 -ε where ε=1 iff nl<0.
n2 = Nod(OSUB, nl, n1)
nc = Nod(OXXX, nil, nil)
Nodconst(nc, nl.Type, 1)
n3 = Nod(OAND, n2, nc)
n = Nod(OADD, n3, n1)
} else {
// n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0.
nc = Nod(OXXX, nil, nil)
Nodconst(nc, nl.Type, (1<<uint(pow))-1)
n2 = Nod(OAND, n1, nc) // n2 = 2^pow-1 iff nl<0.
typecheck(&n2, Erv)
n2 = cheapexpr(n2, init)
n3 = Nod(OADD, nl, n2)
n4 = Nod(OAND, n3, nc)
n = Nod(OSUB, n4, n2)
}
break
} else {
// arithmetic right shift does not give the correct rounding.
// if nl >= 0, nl >> n == nl / nr
// if nl < 0, we want to add 2^n-1 first.
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1)
n1 = Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0.
if pow == 1 {
// nl+1 is nl-(-1)
n.Left = Nod(OSUB, nl, n1)
} else {
// Do a logical right right on -1 to keep pow bits.
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[Simtype[TUINT]], int64(w)-int64(pow))
n2 = Nod(ORSH, conv(n1, tounsigned(nl.Type)), nc)
n.Left = Nod(OADD, nl, conv(n2, nl.Type))
}
// n = (nl + 2^pow-1) >> pow
n.Op = ORSH
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[Simtype[TUINT]], int64(pow))
n.Right = nc
n.Typecheck = 0
}
if s != 0 {
n = Nod(OMINUS, n, nil)
}
break
}
nc = Nod(OXXX, nil, nil)
if n.Op == OMOD {
// n = nl & (nr-1)
n.Op = OAND
Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.Xval)-1)
} else {
// n = nl >> pow
n.Op = ORSH
Nodconst(nc, Types[Simtype[TUINT]], int64(pow))
}
n.Typecheck = 0
n.Right = nc
}
goto ret
// try to do division by multiply by (2^w)/d
// see hacker's delight chapter 10
// TODO: support 64-bit magic multiply here.
divbymul:
m.W = w
if Issigned[nl.Type.Etype] != 0 {
m.Sd = Mpgetfix(nr.Val.U.Xval)
Smagic(&m)
} else {
m.Ud = uint64(Mpgetfix(nr.Val.U.Xval))
Umagic(&m)
}
if m.Bad != 0 {
return
}
// We have a quick division method so use it
// for modulo too.
if n.Op == OMOD {
goto longmod
}
switch Simtype[nl.Type.Etype] {
default:
return
// n1 = nl * magic >> w (HMUL)
case TUINT8,
TUINT16,
TUINT32:
nc = Nod(OXXX, nil, nil)
Nodconst(nc, nl.Type, int64(m.Um))
n1 = Nod(OMUL, nl, nc)
typecheck(&n1, Erv)
n1.Op = OHMUL
if m.Ua != 0 {
// Select a Go type with (at least) twice the width.
switch Simtype[nl.Type.Etype] {
default:
return
case TUINT8,
TUINT16:
twide = Types[TUINT32]
case TUINT32:
twide = Types[TUINT64]
case TINT8,
TINT16:
twide = Types[TINT32]
case TINT32:
twide = Types[TINT64]
}
// add numerator (might overflow).
// n2 = (n1 + nl)
n2 = Nod(OADD, conv(n1, twide), conv(nl, twide))
// shift by m.s
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[TUINT], int64(m.S))
n = conv(Nod(ORSH, n2, nc), nl.Type)
} else {
// n = n1 >> m.s
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[TUINT], int64(m.S))
n = Nod(ORSH, n1, nc)
}
// n1 = nl * magic >> w
case TINT8,
TINT16,
TINT32:
nc = Nod(OXXX, nil, nil)
Nodconst(nc, nl.Type, m.Sm)
n1 = Nod(OMUL, nl, nc)
typecheck(&n1, Erv)
n1.Op = OHMUL
if m.Sm < 0 {
// add the numerator.
n1 = Nod(OADD, n1, nl)
}
// shift by m.s
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[TUINT], int64(m.S))
n2 = conv(Nod(ORSH, n1, nc), nl.Type)
// add 1 iff n1 is negative.
nc = Nod(OXXX, nil, nil)
Nodconst(nc, Types[TUINT], int64(w)-1)
n3 = Nod(ORSH, nl, nc) // n4 = -1 iff n1 is negative.
n = Nod(OSUB, n2, n3)
// apply sign.
if m.Sd < 0 {
n = Nod(OMINUS, n, nil)
}
}
goto ret
// rewrite as A%B = A - (A/B*B).
longmod:
n1 = Nod(ODIV, nl, nr)
n2 = Nod(OMUL, n1, nr)
n = Nod(OSUB, nl, n2)
goto ret
ret:
typecheck(&n, Erv)
walkexpr(&n, init)
*np = n
}
// return 1 if integer n must be in range [0, max), 0 otherwise
func bounded(n *Node, max int64) bool {
var v int64
var bits int32
var sign int
if n.Type == nil || Isint[n.Type.Etype] == 0 {
return false
}
sign = int(Issigned[n.Type.Etype])
bits = int32(8 * n.Type.Width)
if Smallintconst(n) {
v = Mpgetfix(n.Val.U.Xval)
return 0 <= v && v < max
}
switch n.Op {
case OAND:
v = -1
if Smallintconst(n.Left) {
v = Mpgetfix(n.Left.Val.U.Xval)
} else if Smallintconst(n.Right) {
v = Mpgetfix(n.Right.Val.U.Xval)
}
if 0 <= v && v < max {
return true
}
case OMOD:
if sign == 0 && Smallintconst(n.Right) {
v = Mpgetfix(n.Right.Val.U.Xval)
if 0 <= v && v <= max {
return true
}
}
case ODIV:
if sign == 0 && Smallintconst(n.Right) {
v = Mpgetfix(n.Right.Val.U.Xval)
for bits > 0 && v >= 2 {
bits--
v >>= 1
}
}
case ORSH:
if sign == 0 && Smallintconst(n.Right) {
v = Mpgetfix(n.Right.Val.U.Xval)
if v > int64(bits) {
return true
}
bits -= int32(v)
}
}
if sign == 0 && bits <= 62 && 1<<uint(bits) <= max {
return true
}
return false
}
func usefield(n *Node) {
var field *Type
var l *Type
if obj.Fieldtrack_enabled == 0 {
return
}
switch n.Op {
default:
Fatal("usefield %v", Oconv(int(n.Op), 0))
case ODOT,
ODOTPTR:
break
}
field = n.Paramfld
if field == nil {
Fatal("usefield %v %v without paramfld", Tconv(n.Left.Type, 0), Sconv(n.Right.Sym, 0))
}
if field.Note == nil || !strings.Contains(field.Note.S, "go:\"track\"") {
return
}
// dedup on list
if field.Lastfn == Curfn {
return
}
field.Lastfn = Curfn
field.Outer = n.Left.Type
if Isptr[field.Outer.Etype] != 0 {
field.Outer = field.Outer.Type
}
if field.Outer.Sym == nil {
Yyerror("tracked field must be in named struct type")
}
if !exportname(field.Sym.Name) {
Yyerror("tracked field must be exported (upper case)")
}
l = typ(0)
l.Type = field
l.Down = Curfn.Paramfld
Curfn.Paramfld = l
}
func candiscardlist(l *NodeList) bool {
for ; l != nil; l = l.Next {
if !candiscard(l.N) {
return false
}
}
return true
}
func candiscard(n *Node) bool {
if n == nil {
return true
}
switch n.Op {
default:
return false
// Discardable as long as the subpieces are.
case ONAME,
ONONAME,
OTYPE,
OPACK,
OLITERAL,
OADD,
OSUB,
OOR,
OXOR,
OADDSTR,
OADDR,
OANDAND,
OARRAYBYTESTR,
OARRAYRUNESTR,
OSTRARRAYBYTE,
OSTRARRAYRUNE,
OCAP,
OCMPIFACE,
OCMPSTR,
OCOMPLIT,
OMAPLIT,
OSTRUCTLIT,
OARRAYLIT,
OPTRLIT,
OCONV,
OCONVIFACE,
OCONVNOP,
ODOT,
OEQ,
ONE,
OLT,
OLE,
OGT,
OGE,
OKEY,
OLEN,
OMUL,
OLSH,
ORSH,
OAND,
OANDNOT,
ONEW,
ONOT,
OCOM,
OPLUS,
OMINUS,
OOROR,
OPAREN,
ORUNESTR,
OREAL,
OIMAG,
OCOMPLEX:
break
// Discardable as long as we know it's not division by zero.
case ODIV,
OMOD:
if Isconst(n.Right, CTINT) && mpcmpfixc(n.Right.Val.U.Xval, 0) != 0 {
break
}
if Isconst(n.Right, CTFLT) && mpcmpfltc(n.Right.Val.U.Fval, 0) != 0 {
break
}
return false
// Discardable as long as we know it won't fail because of a bad size.
case OMAKECHAN,
OMAKEMAP:
if Isconst(n.Left, CTINT) && mpcmpfixc(n.Left.Val.U.Xval, 0) == 0 {
break
}
return false
// Difficult to tell what sizes are okay.
case OMAKESLICE:
return false
}
if !candiscard(n.Left) || !candiscard(n.Right) || !candiscard(n.Ntest) || !candiscard(n.Nincr) || !candiscardlist(n.Ninit) || !candiscardlist(n.Nbody) || !candiscardlist(n.Nelse) || !candiscardlist(n.List) || !candiscardlist(n.Rlist) {
return false
}
return true
}
// rewrite
// print(x, y, z)
// into
// func(a1, a2, a3) {
// print(a1, a2, a3)
// }(x, y, z)
// and same for println.
var walkprintfunc_prgen int
func walkprintfunc(np **Node, init **NodeList) {
var n *Node
var a *Node
var fn *Node
var t *Node
var oldfn *Node
var l *NodeList
var printargs *NodeList
var num int
var buf string
n = *np
if n.Ninit != nil {
walkstmtlist(n.Ninit)
*init = concat(*init, n.Ninit)
n.Ninit = nil
}
t = Nod(OTFUNC, nil, nil)
num = 0
printargs = nil
for l = n.List; l != nil; l = l.Next {
buf = fmt.Sprintf("a%d", num)
num++
a = Nod(ODCLFIELD, newname(Lookup(buf)), typenod(l.N.Type))
t.List = list(t.List, a)
printargs = list(printargs, a.Left)
}
fn = Nod(ODCLFUNC, nil, nil)
walkprintfunc_prgen++
buf = fmt.Sprintf("print·%d", walkprintfunc_prgen)
fn.Nname = newname(Lookup(buf))
fn.Nname.Defn = fn
fn.Nname.Ntype = t
declare(fn.Nname, PFUNC)
oldfn = Curfn
Curfn = nil
funchdr(fn)
a = Nod(int(n.Op), nil, nil)
a.List = printargs
typecheck(&a, Etop)
walkstmt(&a)
fn.Nbody = list1(a)
funcbody(fn)
typecheck(&fn, Etop)
typechecklist(fn.Nbody, Etop)
xtop = list(xtop, fn)
Curfn = oldfn
a = Nod(OCALL, nil, nil)
a.Left = fn.Nname
a.List = n.List
typecheck(&a, Etop)
walkexpr(&a, init)
*np = a
}