2015-02-13 14:40:36 -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.
|
|
|
|
|
|
|
|
|
|
package gc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"cmd/internal/obj"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var mpzero Mpint
|
|
|
|
|
|
|
|
|
|
// The constant is known to runtime.
|
|
|
|
|
const (
|
|
|
|
|
tmpstringbufsize = 32
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func walk(fn *Node) {
|
|
|
|
|
Curfn = fn
|
|
|
|
|
|
|
|
|
|
if Debug['W'] != 0 {
|
2015-04-17 12:03:22 -04:00
|
|
|
s := fmt.Sprintf("\nbefore %v", Curfn.Nname.Sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
dumplist(s, Curfn.Nbody)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
lno := int(lineno)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// 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.
|
2015-03-25 19:33:01 -07:00
|
|
|
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-03-25 19:33:01 -07:00
|
|
|
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
2015-03-06 21:18:41 +11:00
|
|
|
if l.N.Op == ONAME && l.N.Class&^PHEAP == PAUTO && l.N.Defn != nil && l.N.Defn.Op == OTYPESW && l.N.Used {
|
|
|
|
|
l.N.Defn.Left.Used = true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-25 19:33:01 -07:00
|
|
|
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
2015-03-06 21:18:41 +11:00
|
|
|
if l.N.Op != ONAME || l.N.Class&^PHEAP != PAUTO || l.N.Sym.Name[0] == '&' || l.N.Used {
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if l.N.Defn != nil && l.N.Defn.Op == OTYPESW {
|
2015-03-06 21:18:41 +11:00
|
|
|
if l.N.Defn.Left.Used {
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
lineno = l.N.Defn.Left.Lineno
|
2015-04-17 12:03:22 -04:00
|
|
|
Yyerror("%v declared and not used", l.N.Sym)
|
2015-03-06 21:18:41 +11:00
|
|
|
l.N.Defn.Left.Used = true // suppress repeats
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
|
|
|
|
lineno = l.N.Lineno
|
2015-04-17 12:03:22 -04:00
|
|
|
Yyerror("%v declared and not used", l.N.Sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lineno = int32(lno)
|
|
|
|
|
if nerrors != 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
walkstmtlist(Curfn.Nbody)
|
|
|
|
|
if Debug['W'] != 0 {
|
2015-04-17 12:03:22 -04:00
|
|
|
s := fmt.Sprintf("after walk %v", Curfn.Nname.Sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
dumplist(s, Curfn.Nbody)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
heapmoves()
|
2015-03-25 19:33:01 -07:00
|
|
|
if Debug['W'] != 0 && Curfn.Func.Enter != nil {
|
2015-04-17 12:03:22 -04:00
|
|
|
s := fmt.Sprintf("enter %v", Curfn.Nname.Sym)
|
2015-03-25 19:33:01 -07:00
|
|
|
dumplist(s, Curfn.Func.Enter)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func walkstmtlist(l *NodeList) {
|
|
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
|
walkstmt(&l.N)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func samelist(a *NodeList, b *NodeList) bool {
|
2015-03-09 00:31:13 -04:00
|
|
|
for ; a != nil && b != nil; a, b = a.Next, b.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if a.N != b.N {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return a == b
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-03-06 18:42:58 +11:00
|
|
|
func paramoutheap(fn *Node) bool {
|
2015-03-25 19:33:01 -07:00
|
|
|
for l := fn.Func.Dcl; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
switch l.N.Class {
|
|
|
|
|
case PPARAMOUT,
|
|
|
|
|
PPARAMOUT | PHEAP:
|
2015-03-06 18:42:58 +11:00
|
|
|
return l.N.Addrtaken
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// stop early - parameters are over
|
|
|
|
|
case PAUTO,
|
|
|
|
|
PAUTO | PHEAP:
|
2015-03-06 18:42:58 +11:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-06 18:42:58 +11:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 arg *Node
|
|
|
|
|
var lhs *Node
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
callfunc := n.Left
|
|
|
|
|
for args := callfunc.List; args != nil; args = args.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
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) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-04-17 12:03:22 -04:00
|
|
|
Yyerror("%v is not a top level statement", n.Sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
} 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,
|
2015-04-03 12:23:28 -04:00
|
|
|
ORECOVER,
|
|
|
|
|
OGETG:
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Typecheck == 0 {
|
|
|
|
|
Fatal("missing typecheck: %v", Nconv(n, obj.FmtSign))
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
init := n.Ninit
|
2015-02-13 14:40:36 -05:00
|
|
|
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))
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
init := n.Ninit
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OPRINT, OPRINTN:
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
2015-02-23 16:07:24 -05:00
|
|
|
init := n.Ntest.Ninit
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OPRINT, OPRINTN:
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|
2015-03-06 18:42:58 +11:00
|
|
|
if (Curfn.Type.Outnamed != 0 && count(n.List) > 1) || paramoutheap(Curfn) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// assign to the function out parameters,
|
|
|
|
|
// so that reorder3 can fix up conflicts
|
2015-03-02 14:22:05 -05:00
|
|
|
var rl *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-27 12:00:07 -07:00
|
|
|
var cl uint8
|
2015-03-25 19:33:01 -07:00
|
|
|
for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
|
2015-03-27 12:00:07 -07:00
|
|
|
cl = ll.N.Class &^ PHEAP
|
2015-02-13 14:40:36 -05:00
|
|
|
if cl == PAUTO {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if cl == PPARAMOUT {
|
|
|
|
|
rl = list(rl, ll.N)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if samelist(rl, n.List) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// special return in disguise
|
|
|
|
|
n.List = nil
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if count(n.List) == 1 && count(rl) > 1 {
|
|
|
|
|
// OAS2FUNC in disguise
|
2015-02-23 16:07:24 -05:00
|
|
|
f := n.List.N
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if f.Op != OCALLFUNC && f.Op != OCALLMETH && f.Op != OCALLINTER {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("expected return of call, have %v", f)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
ll := ascompatee(int(n.Op), rl, n.List, &n.Ninit)
|
2015-02-13 14:40:36 -05:00
|
|
|
n.List = reorder3(ll)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-09 16:24:07 +11:00
|
|
|
ll := ascompatte(int(n.Op), nil, false, Getoutarg(Curfn.Type), n.List, 1, &n.Ninit)
|
2015-02-13 14:40:36 -05:00
|
|
|
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) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
lno := setlineno(n)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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,
|
2015-04-03 12:23:28 -04:00
|
|
|
OPARAM,
|
|
|
|
|
OGETG:
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OSPTR, OITAB:
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OLEN, OCAP:
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
|
|
|
|
|
// replace len(*[10]int) with 10.
|
|
|
|
|
// delayed until now to preserve side effects.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Left.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-01 07:54:01 +00:00
|
|
|
if Isptr[t.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
t = t.Type
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isfixedarray(t) {
|
2015-02-13 14:40:36 -05:00
|
|
|
safeexpr(n.Left, init)
|
|
|
|
|
Nodconst(n, n.Type, t.Bound)
|
|
|
|
|
n.Typecheck = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OLSH, ORSH:
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
walkexpr(&n.Right, init)
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Left.Type
|
2015-02-17 22:13:49 -05:00
|
|
|
n.Bounded = bounded(n.Right, 8*t.Width)
|
|
|
|
|
if Debug['m'] != 0 && n.Etype != 0 && !Isconst(n.Right, CTINT) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OOR, OXOR:
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
walkexpr(&n.Right, init)
|
|
|
|
|
walkrotate(&n)
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OEQ, ONE:
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
old_safemode := safemode
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
safemode = 0
|
|
|
|
|
walkcompare(&n, init)
|
|
|
|
|
safemode = old_safemode
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OANDAND, OOROR:
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
|
2015-03-27 12:34:45 -04:00
|
|
|
// 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.
|
2015-03-02 14:22:05 -05:00
|
|
|
var ll *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
walkexpr(&n.Right, &ll)
|
|
|
|
|
addinit(&n.Right, ll)
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OPRINT, OPRINTN:
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-04-02 19:58:37 -07:00
|
|
|
n.Addable = true
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCLOSUREVAR, OCFUNC:
|
2015-04-02 19:58:37 -07:00
|
|
|
n.Addable = true
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ONAME:
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Class&PHEAP == 0 && n.Class != PPARAMREF {
|
2015-04-02 19:58:37 -07:00
|
|
|
n.Addable = true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OCALLINTER:
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Left.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.List != nil && n.List.N.Op == OAS {
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
walkexprlist(n.List, init)
|
2015-03-09 16:24:07 +11:00
|
|
|
ll := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init)
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-03-25 19:33:01 -07:00
|
|
|
n.List = concat(n.List, n.Left.Func.Enter)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-25 19:33:01 -07:00
|
|
|
n.Left.Func.Enter = nil
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// 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 {
|
2015-02-23 16:07:24 -05:00
|
|
|
t := getoutargx(n.Left.Type).Type
|
2015-02-13 14:40:36 -05:00
|
|
|
if t.Etype == TFIELD {
|
|
|
|
|
t = t.Type
|
|
|
|
|
}
|
|
|
|
|
n.Type = t
|
|
|
|
|
} else {
|
|
|
|
|
n.Type = getoutargx(n.Left.Type)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Left.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.List != nil && n.List.N.Op == OAS {
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
walkexprlist(n.List, init)
|
|
|
|
|
|
2015-04-01 16:02:34 -04:00
|
|
|
if n.Left.Op == ONAME && n.Left.Sym.Name == "Sqrt" && n.Left.Sym.Pkg.Path == "math" {
|
|
|
|
|
switch Thearch.Thechar {
|
2015-04-03 18:15:26 -04:00
|
|
|
case '5', '6', '7':
|
2015-04-01 16:02:34 -04:00
|
|
|
n.Op = OSQRT
|
|
|
|
|
n.Left = n.List.N
|
|
|
|
|
n.List = nil
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-09 16:24:07 +11:00
|
|
|
ll := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init)
|
2015-02-13 14:40:36 -05:00
|
|
|
n.List = reorder1(ll)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OCALLMETH:
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Left.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.List != nil && n.List.N.Op == OAS {
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
walkexprlist(n.List, init)
|
2015-03-09 16:24:07 +11:00
|
|
|
ll := ascompatte(int(n.Op), n, false, getthis(t), list1(n.Left.Left), 0, init)
|
|
|
|
|
lr := ascompatte(int(n.Op), n, n.Isddd, getinarg(t), n.List, 0, init)
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if oaslit(n, init) {
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Right == nil || iszero(n.Right) && flag_race == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch n.Right.Op {
|
|
|
|
|
default:
|
|
|
|
|
walkexpr(&n.Right, init)
|
|
|
|
|
|
|
|
|
|
case ODOTTYPE:
|
2015-03-20 00:06:10 -04:00
|
|
|
// TODO(rsc): The Isfat is for consistency with componentgen and orderexpr.
|
|
|
|
|
// It needs to be removed in all three places.
|
|
|
|
|
// That would allow inlining x.(struct{*int}) the same as x.(*int).
|
|
|
|
|
if isdirectiface(n.Right.Type) && !Isfat(n.Right.Type) && flag_race == 0 {
|
|
|
|
|
// handled directly during cgen
|
|
|
|
|
walkexpr(&n.Right, init)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-27 12:34:45 -04:00
|
|
|
// x = i.(T); n.Left is x, n.Right.Left is i.
|
2015-03-20 00:06:10 -04:00
|
|
|
// orderstmt made sure x is addressable.
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Right.Left, init)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n1 := Nod(OADDR, n.Left, nil)
|
|
|
|
|
r := n.Right // i.(T)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-20 00:06:10 -04:00
|
|
|
if Debug_typeassert > 0 {
|
|
|
|
|
Warn("type assertion not inlined")
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-19 23:38:24 +00:00
|
|
|
buf := "assert" + type2IET(r.Left.Type) + "2" + type2IET(r.Type)
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook(buf, 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, r.Left.Type, r.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
n = mkcall1(fn, nil, init, typename(r.Type), r.Left, n1)
|
|
|
|
|
walkexpr(&n, init)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ORECV:
|
2015-03-27 12:34:45 -04:00
|
|
|
// x = <-c; n.Left is x, n.Right.Left is c.
|
2015-03-20 00:06:10 -04:00
|
|
|
// orderstmt made sure x is addressable.
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Right.Left, init)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n1 := Nod(OADDR, n.Left, nil)
|
|
|
|
|
r := n.Right.Left // the channel
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-02-23 16:07:24 -05:00
|
|
|
r := convas(Nod(OAS, n.Left, n.Right), init)
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
2015-02-23 16:07:24 -05:00
|
|
|
ll := ascompatee(OAS, n.List, n.Rlist, init)
|
2015-02-13 14:40:36 -05:00
|
|
|
ll = reorder3(ll)
|
2015-02-23 16:07:24 -05:00
|
|
|
for lr := ll; lr != nil; lr = lr.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
lr.N = applywritebarrier(lr.N, init)
|
|
|
|
|
}
|
|
|
|
|
n = liststmt(ll)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// a,b,... = fn()
|
|
|
|
|
case OAS2FUNC:
|
|
|
|
|
*init = concat(*init, n.Ninit)
|
|
|
|
|
|
|
|
|
|
n.Ninit = nil
|
2015-02-23 16:07:24 -05:00
|
|
|
r := n.Rlist.N
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexprlistsafe(n.List, init)
|
|
|
|
|
walkexpr(&r, init)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
ll := ascompatet(int(n.Op), n.List, &r.Type, 0, init)
|
|
|
|
|
for lr := ll; lr != nil; lr = lr.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-02-23 16:07:24 -05:00
|
|
|
r := n.Rlist.N
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexprlistsafe(n.List, init)
|
|
|
|
|
walkexpr(&r.Left, init)
|
2015-02-23 16:07:24 -05:00
|
|
|
var n1 *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
if isblank(n.List.N) {
|
|
|
|
|
n1 = nodnil()
|
|
|
|
|
} else {
|
|
|
|
|
n1 = Nod(OADDR, n.List.N, nil)
|
|
|
|
|
}
|
|
|
|
|
n1.Etype = 1 // addr does not escape
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := chanfn("chanrecv2", 2, r.Left.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-02-23 16:07:24 -05:00
|
|
|
r := n.Rlist.N
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexprlistsafe(n.List, init)
|
|
|
|
|
walkexpr(&r.Left, init)
|
|
|
|
|
walkexpr(&r.Right, init)
|
2015-02-23 16:07:24 -05:00
|
|
|
t := r.Left.Type
|
|
|
|
|
p := ""
|
2015-02-13 14:40:36 -05:00
|
|
|
if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
|
|
|
|
|
switch Simsimtype(t.Down) {
|
2015-04-01 09:38:44 -07:00
|
|
|
case TINT32, TUINT32:
|
2015-02-13 14:40:36 -05:00
|
|
|
p = "mapaccess2_fast32"
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case TINT64, TUINT64:
|
2015-02-13 14:40:36 -05:00
|
|
|
p = "mapaccess2_fast64"
|
|
|
|
|
|
|
|
|
|
case TSTRING:
|
|
|
|
|
p = "mapaccess2_faststr"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var key *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-02-23 16:07:24 -05:00
|
|
|
a := n.List.N
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := mapfn(p, t)
|
2015-02-13 14:40:36 -05:00
|
|
|
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) {
|
2015-02-23 16:07:24 -05:00
|
|
|
var_ := temp(Ptrto(t.Type))
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-02-23 16:07:24 -05:00
|
|
|
map_ := n.List.N
|
|
|
|
|
key := n.List.Next.N
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&map_, init)
|
|
|
|
|
walkexpr(&key, init)
|
|
|
|
|
|
|
|
|
|
// orderstmt made sure key is addressable.
|
|
|
|
|
key = Nod(OADDR, key, nil)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
t := map_.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
n = mkcall1(mapfndel("mapdelete", t), nil, init, typename(t), map_, key)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OAS2DOTTYPE:
|
2015-03-20 00:06:10 -04:00
|
|
|
e := n.Rlist.N // i.(T)
|
|
|
|
|
// TODO(rsc): The Isfat is for consistency with componentgen and orderexpr.
|
|
|
|
|
// It needs to be removed in all three places.
|
|
|
|
|
// That would allow inlining x.(struct{*int}) the same as x.(*int).
|
|
|
|
|
if isdirectiface(e.Type) && !Isfat(e.Type) && flag_race == 0 {
|
|
|
|
|
// handled directly during gen.
|
|
|
|
|
walkexprlistsafe(n.List, init)
|
|
|
|
|
walkexpr(&e.Left, init)
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// res, ok = i.(T)
|
|
|
|
|
// orderstmt made sure a is addressable.
|
2015-02-13 14:40:36 -05:00
|
|
|
*init = concat(*init, n.Ninit)
|
|
|
|
|
n.Ninit = nil
|
|
|
|
|
|
2015-03-17 13:56:29 -07:00
|
|
|
walkexprlistsafe(n.List, init)
|
|
|
|
|
walkexpr(&e.Left, init)
|
|
|
|
|
t := e.Type // T
|
|
|
|
|
from := e.Left // i
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-17 13:56:29 -07:00
|
|
|
oktype := Types[TBOOL]
|
|
|
|
|
ok := n.List.Next.N
|
|
|
|
|
if !isblank(ok) {
|
|
|
|
|
oktype = ok.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-17 13:56:29 -07:00
|
|
|
|
2015-03-19 23:38:24 +00:00
|
|
|
fromKind := type2IET(from.Type)
|
|
|
|
|
toKind := type2IET(t)
|
2015-03-17 15:14:31 -07:00
|
|
|
|
|
|
|
|
// Avoid runtime calls in a few cases of the form _, ok := i.(T).
|
|
|
|
|
// This is faster and shorter and allows the corresponding assertX2X2
|
|
|
|
|
// routines to skip nil checks on their last argument.
|
|
|
|
|
if isblank(n.List.N) {
|
|
|
|
|
var fast *Node
|
|
|
|
|
switch {
|
|
|
|
|
case fromKind == "E" && toKind == "T":
|
|
|
|
|
tab := Nod(OITAB, from, nil) // type:eface::tab:iface
|
|
|
|
|
typ := Nod(OCONVNOP, typename(t), nil)
|
|
|
|
|
typ.Type = Ptrto(Types[TUINTPTR])
|
|
|
|
|
fast = Nod(OEQ, tab, typ)
|
|
|
|
|
case fromKind == "I" && toKind == "E",
|
|
|
|
|
fromKind == "E" && toKind == "E":
|
|
|
|
|
tab := Nod(OITAB, from, nil)
|
2015-03-19 09:49:25 -07:00
|
|
|
fast = Nod(ONE, nodnil(), tab)
|
2015-03-17 15:14:31 -07:00
|
|
|
}
|
|
|
|
|
if fast != nil {
|
2015-03-20 00:06:10 -04:00
|
|
|
if Debug_typeassert > 0 {
|
|
|
|
|
Warn("type assertion (ok only) inlined")
|
|
|
|
|
}
|
2015-03-17 15:14:31 -07:00
|
|
|
n = Nod(OAS, ok, fast)
|
|
|
|
|
typecheck(&n, Etop)
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-17 13:56:29 -07:00
|
|
|
var resptr *Node // &res
|
|
|
|
|
if isblank(n.List.N) {
|
|
|
|
|
resptr = nodnil()
|
|
|
|
|
} else {
|
|
|
|
|
resptr = Nod(OADDR, n.List.N, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-17 13:56:29 -07:00
|
|
|
resptr.Etype = 1 // addr does not escape
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-20 00:06:10 -04:00
|
|
|
if Debug_typeassert > 0 {
|
|
|
|
|
Warn("type assertion not inlined")
|
|
|
|
|
}
|
2015-03-19 23:38:24 +00:00
|
|
|
buf := "assert" + fromKind + "2" + toKind + "2"
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook(buf, 1)
|
2015-03-17 13:56:29 -07:00
|
|
|
substArgTypes(fn, from.Type, t)
|
|
|
|
|
call := mkcall1(fn, oktype, init, typename(t), from, resptr)
|
|
|
|
|
n = Nod(OAS, ok, call)
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&n, Etop)
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-03-20 00:06:10 -04:00
|
|
|
case ODOTTYPE, ODOTTYPE2:
|
|
|
|
|
if !isdirectiface(n.Type) || Isfat(n.Type) {
|
|
|
|
|
Fatal("walkexpr ODOTTYPE") // should see inside OAS only
|
|
|
|
|
}
|
|
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
goto ret
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case OCONVIFACE:
|
|
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
|
|
|
|
|
// Optimize convT2E as a two-word copy when T is pointer-shaped.
|
2015-02-17 22:13:49 -05:00
|
|
|
if isnilinter(n.Type) && isdirectiface(n.Left.Type) {
|
2015-02-23 16:07:24 -05:00
|
|
|
l := Nod(OEFACE, typename(n.Left.Type), n.Left)
|
2015-02-13 14:40:36 -05:00
|
|
|
l.Type = n.Type
|
|
|
|
|
l.Typecheck = n.Typecheck
|
|
|
|
|
n = l
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-19 23:38:24 +00:00
|
|
|
// Build name of function: convI2E etc.
|
|
|
|
|
// Not all names are possible
|
|
|
|
|
// (e.g., we'll never generate convE2E or convE2I).
|
|
|
|
|
buf := "conv" + type2IET(n.Left.Type) + "2" + type2IET(n.Type)
|
|
|
|
|
fn := syslook(buf, 1)
|
2015-03-02 14:22:05 -05:00
|
|
|
var ll *NodeList
|
2015-02-17 22:13:49 -05:00
|
|
|
if !Isinter(n.Left.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
ll = list(ll, typename(n.Left.Type))
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if !isnilinter(n.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
ll = list(ll, typename(n.Type))
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if !Isinter(n.Left.Type) && !isnilinter(n.Type) {
|
2015-03-12 18:45:30 -04:00
|
|
|
sym := Pkglookup(Tconv(n.Left.Type, obj.FmtLeft)+"."+Tconv(n.Type, obj.FmtLeft), itabpkg)
|
2015-02-13 14:40:36 -05:00
|
|
|
if sym.Def == nil {
|
2015-02-23 16:07:24 -05:00
|
|
|
l := Nod(ONAME, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
l.Sym = sym
|
|
|
|
|
l.Type = Ptrto(Types[TUINT8])
|
2015-04-02 19:58:37 -07:00
|
|
|
l.Addable = true
|
2015-02-13 14:40:36 -05:00
|
|
|
l.Class = PEXTERN
|
|
|
|
|
l.Xoffset = 0
|
|
|
|
|
sym.Def = l
|
|
|
|
|
ggloblsym(sym, int32(Widthptr), obj.DUPOK|obj.NOPTR)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
l := Nod(OADDR, sym.Def, nil)
|
2015-04-02 19:58:37 -07:00
|
|
|
l.Addable = true
|
2015-02-13 14:40:36 -05:00
|
|
|
ll = list(ll, l)
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if isdirectiface(n.Left.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
/* 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};
|
|
|
|
|
*/
|
2015-02-23 16:07:24 -05:00
|
|
|
l := temp(Ptrto(Types[TUINT8]))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n1 := Nod(OAS, l, sym.Def)
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&n1, Etop)
|
|
|
|
|
*init = list(*init, n1)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook("typ2Itab", 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
n1 = Nod(OCALL, fn, nil)
|
|
|
|
|
n1.List = ll
|
|
|
|
|
typecheck(&n1, Erv)
|
|
|
|
|
walkexpr(&n1, init)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n2 := Nod(OIF, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isinter(n.Left.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
ll = list(ll, n.Left)
|
|
|
|
|
} else {
|
|
|
|
|
// regular types are passed by reference to avoid C vararg calls
|
2015-03-27 12:34:45 -04:00
|
|
|
// orderexpr arranged for n.Left to be a temporary for all
|
2015-02-13 14:40:36 -05:00
|
|
|
// 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.
|
2015-02-17 22:13:49 -05:00
|
|
|
if islvalue(n.Left) {
|
2015-02-13 14:40:36 -05:00
|
|
|
ll = list(ll, Nod(OADDR, n.Left, nil))
|
|
|
|
|
} else {
|
|
|
|
|
ll = list(ll, Nod(OADDR, copyexpr(n.Left, n.Left.Type, init), nil))
|
|
|
|
|
}
|
2015-03-27 11:21:14 -04:00
|
|
|
dowidth(n.Left.Type)
|
|
|
|
|
r := nodnil()
|
|
|
|
|
if n.Esc == EscNone && n.Left.Type.Width <= 1024 {
|
|
|
|
|
// Allocate stack buffer for value stored in interface.
|
|
|
|
|
r = temp(n.Left.Type)
|
|
|
|
|
r = Nod(OAS, r, nil) // zero temp
|
|
|
|
|
typecheck(&r, Etop)
|
|
|
|
|
*init = list(*init, r)
|
|
|
|
|
r = Nod(OADDR, r.Left, nil)
|
|
|
|
|
typecheck(&r, Erv)
|
|
|
|
|
}
|
|
|
|
|
ll = list(ll, r)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-03-27 11:21:14 -04:00
|
|
|
if !Isinter(n.Left.Type) {
|
|
|
|
|
substArgTypes(fn, n.Left.Type, n.Left.Type, n.Type)
|
|
|
|
|
} else {
|
|
|
|
|
substArgTypes(fn, n.Left.Type, n.Type)
|
|
|
|
|
}
|
2015-02-13 14:40:36 -05:00
|
|
|
dowidth(fn.Type)
|
|
|
|
|
n = Nod(OCALL, fn, nil)
|
|
|
|
|
n.List = ll
|
|
|
|
|
typecheck(&n, Erv)
|
|
|
|
|
walkexpr(&n, init)
|
|
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OCONV, OCONVNOP:
|
2015-02-13 14:40:36 -05:00
|
|
|
if Thearch.Thechar == '5' {
|
2015-03-01 07:54:01 +00:00
|
|
|
if Isfloat[n.Left.Type.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-01 07:54:01 +00:00
|
|
|
if Isfloat[n.Type.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case ODIV, OMOD:
|
2015-02-13 14:40:36 -05:00
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
walkexpr(&n.Right, init)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* rewrite complex div into function call.
|
|
|
|
|
*/
|
2015-02-23 16:07:24 -05:00
|
|
|
et := int(n.Left.Type.Etype)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-01 07:54:01 +00:00
|
|
|
if Iscomplex[et] && n.Op == ODIV {
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-03-01 07:54:01 +00:00
|
|
|
if Isfloat[et] {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OMOD, ODIV:
|
2015-02-13 14:40:36 -05:00
|
|
|
if Widthreg >= 8 || (et != TUINT64 && et != TINT64) {
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
2015-03-06 12:02:24 -08:00
|
|
|
var fn string
|
2015-02-13 14:40:36 -05:00
|
|
|
if et == TINT64 {
|
2015-03-06 12:02:24 -08:00
|
|
|
fn = "int64"
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
2015-03-06 12:02:24 -08:00
|
|
|
fn = "uint64"
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
if n.Op == ODIV {
|
2015-03-06 12:02:24 -08:00
|
|
|
fn += "div"
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
2015-03-06 12:02:24 -08:00
|
|
|
fn += "mod"
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-06 12:02:24 -08:00
|
|
|
n = mkcall(fn, n.Type, init, conv(n.Left, Types[et]), conv(n.Right, Types[et]))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
r := n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
walkexpr(&n.Right, init)
|
|
|
|
|
|
|
|
|
|
// if range of type cannot exceed static array bound,
|
|
|
|
|
// disable bounds check.
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Bounded {
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Left.Type
|
2015-03-01 07:54:01 +00:00
|
|
|
if t != nil && Isptr[t.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
t = t.Type
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isfixedarray(t) {
|
|
|
|
|
n.Bounded = bounded(r, t.Bound)
|
|
|
|
|
if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
|
2015-02-13 14:40:36 -05:00
|
|
|
Warn("index bounds check elided")
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if Smallintconst(n.Right) && !n.Bounded {
|
2015-02-13 14:40:36 -05:00
|
|
|
Yyerror("index out of bounds")
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if Isconst(n.Left, CTSTR) {
|
2015-03-02 16:03:26 -05:00
|
|
|
n.Bounded = bounded(r, int64(len(n.Left.Val.U.Sval)))
|
2015-02-17 22:13:49 -05:00
|
|
|
if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
|
2015-02-13 14:40:36 -05:00
|
|
|
Warn("index bounds check elided")
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if Smallintconst(n.Right) {
|
|
|
|
|
if !n.Bounded {
|
2015-02-13 14:40:36 -05:00
|
|
|
Yyerror("index out of bounds")
|
|
|
|
|
} else {
|
|
|
|
|
// replace "abc"[1] with 'b'.
|
|
|
|
|
// delayed until now because "abc"[1] is not
|
|
|
|
|
// an ideal constant.
|
2015-02-23 16:07:24 -05:00
|
|
|
v := Mpgetfix(n.Right.Val.U.Xval)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-02 16:03:26 -05:00
|
|
|
Nodconst(n, n.Type, int64(n.Left.Val.U.Sval[v]))
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Typecheck = 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(n.Right, CTINT) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Left.Type
|
|
|
|
|
p := ""
|
2015-02-13 14:40:36 -05:00
|
|
|
if t.Type.Width <= 128 { // Check ../../runtime/hashmap.go:maxValueSize before changing.
|
|
|
|
|
switch Simsimtype(t.Down) {
|
2015-04-01 09:38:44 -07:00
|
|
|
case TINT32, TUINT32:
|
2015-02-13 14:40:36 -05:00
|
|
|
p = "mapaccess1_fast32"
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case TINT64, TUINT64:
|
2015-02-13 14:40:36 -05:00
|
|
|
p = "mapaccess1_fast64"
|
|
|
|
|
|
|
|
|
|
case TSTRING:
|
|
|
|
|
p = "mapaccess1_faststr"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var key *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OSLICEARR, OSLICESTR:
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
2015-03-27 12:34:45 -04:00
|
|
|
n = sliceany(n, init) // chops n.Right, sets n.List
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OSLICE3, OSLICE3ARR:
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
2015-03-27 12:34:45 -04:00
|
|
|
n = sliceany(n, init) // chops n.Right, sets n.List
|
2015-02-13 14:40:36 -05:00
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OADDR:
|
|
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ONEW:
|
|
|
|
|
if n.Esc == EscNone && n.Type.Type.Width < 1<<16 {
|
2015-02-23 16:07:24 -05:00
|
|
|
r := temp(n.Type.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-03-02 16:03:26 -05:00
|
|
|
if (Isconst(n.Left, CTSTR) && len(n.Left.Val.U.Sval) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val.U.Sval) == 0) {
|
2015-02-23 16:07:24 -05:00
|
|
|
r := Nod(int(n.Etype), Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil))
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&r, Erv)
|
|
|
|
|
walkexpr(&r, init)
|
|
|
|
|
r.Type = n.Type
|
|
|
|
|
n = r
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// s + "badgerbadgerbadger" == "badgerbadgerbadger"
|
2015-02-17 22:13:49 -05:00
|
|
|
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 {
|
2015-02-23 16:07:24 -05:00
|
|
|
r := Nod(int(n.Etype), Nod(OLEN, n.Left.List.N, nil), Nodintconst(0))
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&r, Erv)
|
|
|
|
|
walkexpr(&r, init)
|
|
|
|
|
r.Type = n.Type
|
|
|
|
|
n = r
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var r *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("cmp %v", n.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
r.Type = n.Type
|
|
|
|
|
n = r
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OADDSTR:
|
|
|
|
|
n = addstr(n, init)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OAPPEND:
|
2015-03-09 16:24:07 +11:00
|
|
|
if n.Isddd {
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook("closechan", 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, n.Left.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook("makemap", 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
a := nodnil() // hmap buffer
|
|
|
|
|
r := nodnil() // bucket buffer
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Esc == EscNone {
|
|
|
|
|
// Allocate hmap buffer on stack.
|
2015-02-23 16:07:24 -05:00
|
|
|
var_ := temp(hmap(t))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, hmap(t), mapbucket(t), t.Down, t.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
n = mkcall1(fn, n.Type, init, typename(n.Type), conv(n.Left, Types[TINT64]), a, r)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OMAKESLICE:
|
2015-02-23 16:07:24 -05:00
|
|
|
l := n.Left
|
|
|
|
|
r := n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
if r == nil {
|
|
|
|
|
r = safeexpr(l, init)
|
|
|
|
|
l = r
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Type
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.Xval) < (1<<16)/t.Type.Width) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// var arr [r]T
|
|
|
|
|
// n = arr[:l]
|
|
|
|
|
t = aindex(r, t.Type) // [r]T
|
2015-02-23 16:07:24 -05:00
|
|
|
var_ := temp(t)
|
|
|
|
|
a := Nod(OAS, var_, nil) // zero temp
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&a, Etop)
|
|
|
|
|
*init = list(*init, a)
|
2015-02-23 16:07:24 -05:00
|
|
|
r := Nod(OSLICE, var_, Nod(OKEY, nil, l)) // arr[:l]
|
2015-03-27 12:34:45 -04:00
|
|
|
r = conv(r, n.Type) // in case n.Type is named.
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&r, Erv)
|
|
|
|
|
walkexpr(&r, init)
|
|
|
|
|
n = r
|
|
|
|
|
} else {
|
|
|
|
|
// makeslice(t *Type, nel int64, max int64) (ary []any)
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook("makeslice", 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, t.Type) // any-1
|
2015-02-13 14:40:36 -05:00
|
|
|
n = mkcall1(fn, n.Type, init, typename(n.Type), conv(l, Types[TINT64]), conv(r, Types[TINT64]))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case ORUNESTR:
|
2015-02-23 16:07:24 -05:00
|
|
|
a := nodnil()
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Esc == EscNone {
|
2015-02-23 16:07:24 -05:00
|
|
|
t := aindex(Nodintconst(4), Types[TUINT8])
|
|
|
|
|
var_ := temp(t)
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-02-23 16:07:24 -05:00
|
|
|
a := nodnil()
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Esc == EscNone {
|
|
|
|
|
// Create temporary buffer for string on stack.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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:
|
2015-02-23 16:07:24 -05:00
|
|
|
a := nodnil()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if n.Esc == EscNone {
|
|
|
|
|
// Create temporary buffer for string on stack.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
a = Nod(OADDR, temp(t), nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = mkcall("slicerunetostring", n.Type, init, a, n.Left)
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
// stringtoslicebyte(*32[byte], string) []byte;
|
|
|
|
|
case OSTRARRAYBYTE:
|
2015-02-23 16:07:24 -05:00
|
|
|
a := nodnil()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if n.Esc == EscNone {
|
|
|
|
|
// Create temporary buffer for slice on stack.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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:
|
2015-02-23 16:07:24 -05:00
|
|
|
a := nodnil()
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if n.Esc == EscNone {
|
|
|
|
|
// Create temporary buffer for slice on stack.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := aindex(Nodintconst(tmpstringbufsize), Types[TINT32])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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) {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("ifaceeq %v %v %v", Oconv(int(n.Op), 0), n.Left.Type, n.Right.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
var fn *Node
|
2015-02-17 22:13:49 -05:00
|
|
|
if isnilinter(n.Left.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
fn = syslook("efaceeq", 1)
|
|
|
|
|
} else {
|
|
|
|
|
fn = syslook("ifaceeq", 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n.Right = cheapexpr(n.Right, init)
|
|
|
|
|
n.Left = cheapexpr(n.Left, init)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, n.Right.Type, n.Left.Type)
|
2015-02-23 16:07:24 -05:00
|
|
|
r := mkcall1(fn, n.Type, init, n.Left, n.Right)
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OARRAYLIT, OMAPLIT, OSTRUCTLIT, OPTRLIT:
|
2015-02-23 16:07:24 -05:00
|
|
|
var_ := temp(n.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
anylit(0, n, var_, init)
|
|
|
|
|
n = var_
|
|
|
|
|
goto ret
|
|
|
|
|
|
|
|
|
|
case OSEND:
|
2015-02-23 16:07:24 -05:00
|
|
|
n1 := n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-02-23 16:07:24 -05:00
|
|
|
t := n.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
// convas will turn map assigns into function calls,
|
|
|
|
|
// making it impossible for reorder3 to work.
|
2015-02-23 16:07:24 -05:00
|
|
|
n := Nod(OAS, l, r)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if l.Op == OINDEXMAP {
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return convas(n, init)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ascompatee(op int, nl *NodeList, nr *NodeList, init **NodeList) *NodeList {
|
|
|
|
|
/*
|
|
|
|
|
* check assign expression list to
|
|
|
|
|
* a expression list. called in
|
|
|
|
|
* expr-list = expr-list
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// ensure order of evaluation for function calls
|
2015-03-12 18:45:30 -04:00
|
|
|
for ll := nl; ll != nil; ll = ll.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
ll.N = safeexpr(ll.N, init)
|
|
|
|
|
}
|
2015-03-12 18:45:30 -04:00
|
|
|
for lr := nr; lr != nil; lr = lr.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
lr.N = safeexpr(lr.N, init)
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var nn *NodeList
|
2015-03-12 18:45:30 -04:00
|
|
|
ll := nl
|
|
|
|
|
lr := nr
|
2015-03-09 00:31:13 -04:00
|
|
|
for ; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
// 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
|
|
|
|
|
*/
|
2015-02-17 22:13:49 -05:00
|
|
|
func fncall(l *Node, rt *Type) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if l.Ullman >= UINF || l.Op == OINDEXMAP {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-02 14:22:05 -05:00
|
|
|
var r Node
|
2015-02-17 22:13:49 -05:00
|
|
|
if needwritebarrier(l, &r) {
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
if Eqtype(l.Type, rt) {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 saver Iter
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* check assign type list to
|
|
|
|
|
* a expression list. called in
|
|
|
|
|
* expr-list = func()
|
|
|
|
|
*/
|
2015-02-23 16:07:24 -05:00
|
|
|
r := Structfirst(&saver, nr)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var nn *NodeList
|
|
|
|
|
var mm *NodeList
|
2015-02-23 16:07:24 -05:00
|
|
|
ucount := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-02-17 22:13:49 -05:00
|
|
|
if fncall(l, r.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-03-10 09:58:01 +11:00
|
|
|
esc := uint8(EscUnknown)
|
2015-02-13 14:40:36 -05:00
|
|
|
if ddd != nil {
|
2015-03-10 09:58:01 +11:00
|
|
|
esc = ddd.Esc
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
tslice := typ(TARRAY)
|
2015-02-13 14:40:36 -05:00
|
|
|
tslice.Type = l.Type.Type
|
|
|
|
|
tslice.Bound = -1
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var n *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-03-10 09:58:01 +11:00
|
|
|
n.Esc = esc
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&n, Erv)
|
|
|
|
|
if n.Type == nil {
|
|
|
|
|
Fatal("mkdotargslice: typecheck failed")
|
|
|
|
|
}
|
|
|
|
|
walkexpr(&n, init)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
a := Nod(OAS, nodarg(l, fp), n)
|
2015-02-13 14:40:36 -05:00
|
|
|
nn = list(nn, convas(a, init))
|
|
|
|
|
return nn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* helpers for shape errors
|
|
|
|
|
*/
|
|
|
|
|
func dumptypes(nl **Type, what string) string {
|
|
|
|
|
var savel Iter
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
fmt_ := ""
|
2015-02-28 20:31:32 +00:00
|
|
|
fmt_ += "\t"
|
2015-02-23 16:07:24 -05:00
|
|
|
first := 1
|
|
|
|
|
for l := Structfirst(&savel, nl); l != nil; l = structnext(&savel) {
|
2015-02-13 14:40:36 -05:00
|
|
|
if first != 0 {
|
|
|
|
|
first = 0
|
|
|
|
|
} else {
|
2015-02-28 20:31:32 +00:00
|
|
|
fmt_ += ", "
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-12 18:45:30 -04:00
|
|
|
fmt_ += Tconv(l, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if first != 0 {
|
|
|
|
|
fmt_ += fmt.Sprintf("[no arguments %s]", what)
|
|
|
|
|
}
|
|
|
|
|
return fmt_
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dumpnodetypes(l *NodeList, what string) string {
|
|
|
|
|
var r *Node
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
fmt_ := ""
|
2015-02-28 20:31:32 +00:00
|
|
|
fmt_ += "\t"
|
2015-02-23 16:07:24 -05:00
|
|
|
first := 1
|
2015-02-13 14:40:36 -05:00
|
|
|
for ; l != nil; l = l.Next {
|
|
|
|
|
r = l.N
|
|
|
|
|
if first != 0 {
|
|
|
|
|
first = 0
|
|
|
|
|
} else {
|
2015-02-28 20:31:32 +00:00
|
|
|
fmt_ += ", "
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-12 18:45:30 -04:00
|
|
|
fmt_ += Tconv(r.Type, 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
*/
|
2015-03-09 16:24:07 +11:00
|
|
|
func ascompatte(op int, call *Node, isddd bool, nl **Type, lr *NodeList, fp int, init **NodeList) *NodeList {
|
2015-02-13 14:40:36 -05:00
|
|
|
var savel Iter
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
lr0 := lr
|
|
|
|
|
l := Structfirst(&savel, nl)
|
2015-03-02 14:22:05 -05:00
|
|
|
var r *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
if lr != nil {
|
|
|
|
|
r = lr.N
|
|
|
|
|
}
|
2015-03-02 14:22:05 -05:00
|
|
|
var nn *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// f(g()) where g has multiple return values
|
2015-02-23 16:07:24 -05:00
|
|
|
var a *Node
|
|
|
|
|
var l2 string
|
|
|
|
|
var ll *Type
|
|
|
|
|
var l1 string
|
2015-02-13 14:40:36 -05:00
|
|
|
if r != nil && lr.Next == nil && r.Type.Etype == TSTRUCT && r.Type.Funarg != 0 {
|
|
|
|
|
// optimization - can do block copy
|
2015-02-17 22:13:49 -05:00
|
|
|
if eqtypenoname(r.Type, *nl) {
|
2015-02-23 16:07:24 -05:00
|
|
|
a := nodarg(*nl, fp)
|
2015-02-13 14:40:36 -05:00
|
|
|
r = Nod(OCONVNOP, r, nil)
|
|
|
|
|
r.Type = a.Type
|
|
|
|
|
nn = list1(convas(Nod(OAS, a, r), init))
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// conversions involved.
|
|
|
|
|
// copy into temporaries.
|
2015-03-02 14:22:05 -05:00
|
|
|
var alist *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := Structfirst(&savel, &r.Type); l != nil; l = structnext(&savel) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-03-09 16:24:07 +11:00
|
|
|
if l != nil && l.Isddd {
|
2015-02-13 14:40:36 -05:00
|
|
|
// 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
|
2015-03-09 16:24:07 +11:00
|
|
|
if r != nil && lr.Next == nil && isddd && Eqtype(l.Type, r.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 on *Node
|
|
|
|
|
var t *Type
|
|
|
|
|
var et int
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
op := int(nn.Op)
|
|
|
|
|
all := nn.List
|
2015-03-02 14:22:05 -05:00
|
|
|
var calls *NodeList
|
2015-02-23 16:07:24 -05:00
|
|
|
notfirst := false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Hoist all the argument evaluation up before the lock.
|
|
|
|
|
walkexprlistcheap(all, init)
|
|
|
|
|
|
|
|
|
|
calls = list(calls, mkcall("printlock", nil, init))
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := all; l != nil; l = l.Next {
|
2015-02-17 22:13:49 -05:00
|
|
|
if notfirst {
|
2015-02-13 14:40:36 -05:00
|
|
|
calls = list(calls, mkcall("printsp", nil, init))
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
notfirst = op == OPRINTN
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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)
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isinter(n.Type) {
|
|
|
|
|
if isnilinter(n.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
on = syslook("printeface", 1)
|
|
|
|
|
} else {
|
|
|
|
|
on = syslook("printiface", 1)
|
|
|
|
|
}
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(on, n.Type) // any-1
|
2015-03-01 07:54:01 +00:00
|
|
|
} else if Isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR {
|
2015-02-13 14:40:36 -05:00
|
|
|
on = syslook("printpointer", 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(on, n.Type) // any-1
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if Isslice(n.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
on = syslook("printslice", 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(on, n.Type) // any-1
|
2015-03-01 07:54:01 +00:00
|
|
|
} else if Isint[et] {
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
|
|
|
|
}
|
2015-03-01 07:54:01 +00:00
|
|
|
} else if Isfloat[et] {
|
2015-02-13 14:40:36 -05:00
|
|
|
on = syslook("printfloat", 0)
|
2015-03-01 07:54:01 +00:00
|
|
|
} else if Iscomplex[et] {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
|
|
|
|
dowidth(t)
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook("newobject", 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, t)
|
2015-02-13 14:40:36 -05:00
|
|
|
return mkcall1(fn, Ptrto(t), nil, typename(t))
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func isstack(n *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
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_") {
|
2015-02-23 16:07:24 -05:00
|
|
|
defn := n.Left.Defn
|
2015-02-13 14:40:36 -05:00
|
|
|
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:
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case ONAME:
|
|
|
|
|
switch n.Class {
|
2015-04-01 09:38:44 -07:00
|
|
|
case PAUTO, PPARAM, PPARAMOUT:
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func isglobal(n *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
n = outervalue(n)
|
|
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
|
case ONAME:
|
|
|
|
|
switch n.Class {
|
|
|
|
|
case PEXTERN:
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Do we need a write barrier for the assignment l = r?
|
2015-02-17 22:13:49 -05:00
|
|
|
func needwritebarrier(l *Node, r *Node) bool {
|
|
|
|
|
if use_writebarrier == 0 {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l == nil || isblank(l) {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No write barrier for write of non-pointers.
|
|
|
|
|
dowidth(l.Type)
|
|
|
|
|
|
|
|
|
|
if !haspointers(l.Type) {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No write barrier for write to stack.
|
2015-02-17 22:13:49 -05:00
|
|
|
if isstack(l) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No write barrier for implicit or explicit zeroing.
|
2015-02-17 22:13:49 -05:00
|
|
|
if r == nil || iszero(r) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No write barrier for initialization to constant.
|
|
|
|
|
if r.Op == OLITERAL {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No write barrier for storing static (read-only) data.
|
|
|
|
|
if r.Op == ONAME && strings.HasPrefix(r.Sym.Name, "statictmp_") {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No write barrier for storing address of stack values,
|
|
|
|
|
// which are guaranteed only to be written to the stack.
|
2015-02-17 22:13:49 -05:00
|
|
|
if r.Op == OADDR && isstack(r.Left) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No write barrier for storing address of global, which
|
|
|
|
|
// is live no matter what.
|
2015-02-17 22:13:49 -05:00
|
|
|
if r.Op == OADDR && isglobal(r.Left) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.)
|
2015-03-06 18:42:58 +11:00
|
|
|
if r.Reslice {
|
2015-02-13 14:40:36 -05:00
|
|
|
switch r.Op {
|
2015-04-01 09:38:44 -07:00
|
|
|
case OSLICE, OSLICE3, OSLICESTR, OAPPEND:
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
Dump("bad reslice-l", l)
|
|
|
|
|
Dump("bad reslice-r", r)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, be conservative and use write barrier.
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(rsc): Perhaps componentgen should run before this.
|
|
|
|
|
|
2015-03-02 21:25:33 -05:00
|
|
|
var applywritebarrier_bv Bvec
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
func applywritebarrier(n *Node, init **NodeList) *Node {
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
|
2015-03-25 19:33:01 -07:00
|
|
|
if Curfn != nil && Curfn.Func.Nowritebarrier {
|
2015-02-13 14:40:36 -05:00
|
|
|
Yyerror("write barrier prohibited")
|
|
|
|
|
}
|
cmd/internal/gc: emit typedmemmove write barrier from sgen
Emitting it here instead of rewriting the tree earlier sets us up
to generate an inline check, like we do for single pointers.
But even without the inline check, generating at this level lets
us generate significantly more efficient code, probably due to
having fewer temporaries and less complex high-level code
for the compiler to churn through.
Revcomp is worse, almost certainly due to register pressure.
name old new delta
BenchmarkBinaryTree17 18.0s Ă— (0.99,1.01) 18.0s Ă— (0.99,1.01) ~
BenchmarkFannkuch11 4.43s Ă— (1.00,1.00) 4.36s Ă— (1.00,1.00) -1.44%
BenchmarkFmtFprintfEmpty 114ns Ă— (0.95,1.05) 86ns Ă— (0.97,1.06) -24.12%
BenchmarkFmtFprintfString 468ns Ă— (0.99,1.01) 420ns Ă— (0.99,1.02) -10.16%
BenchmarkFmtFprintfInt 433ns Ă— (1.00,1.01) 386ns Ă— (0.99,1.02) -10.74%
BenchmarkFmtFprintfIntInt 748ns Ă— (0.99,1.01) 647ns Ă— (0.99,1.01) -13.56%
BenchmarkFmtFprintfPrefixedInt 547ns Ă— (0.99,1.01) 499ns Ă— (0.99,1.02) -8.78%
BenchmarkFmtFprintfFloat 756ns Ă— (1.00,1.01) 689ns Ă— (1.00,1.00) -8.86%
BenchmarkFmtManyArgs 2.79µs × (1.00,1.01) 2.53µs × (1.00,1.00) -9.30%
BenchmarkGobDecode 39.6ms Ă— (0.99,1.00) 39.2ms Ă— (0.98,1.01) -1.07%
BenchmarkGobEncode 37.6ms Ă— (1.00,1.01) 37.5ms Ă— (0.99,1.01) ~
BenchmarkGzip 663ms Ă— (0.99,1.02) 660ms Ă— (0.98,1.01) ~
BenchmarkGunzip 142ms Ă— (1.00,1.00) 143ms Ă— (1.00,1.00) ~
BenchmarkHTTPClientServer 132µs × (0.99,1.01) 133µs × (0.99,1.02) ~
BenchmarkJSONEncode 56.2ms Ă— (0.99,1.01) 54.0ms Ă— (0.98,1.01) -3.97%
BenchmarkJSONDecode 138ms Ă— (1.00,1.00) 134ms Ă— (0.99,1.02) -2.70%
BenchmarkMandelbrot200 6.03ms Ă— (1.00,1.01) 6.00ms Ă— (1.00,1.01) ~
BenchmarkGoParse 9.82ms Ă— (0.93,1.10) 10.35ms Ă— (0.88,1.11) ~
BenchmarkRegexpMatchEasy0_32 207ns Ă— (1.00,1.00) 163ns Ă— (0.99,1.01) -21.26%
BenchmarkRegexpMatchEasy0_1K 581ns Ă— (1.00,1.01) 566ns Ă— (0.99,1.00) -2.50%
BenchmarkRegexpMatchEasy1_32 185ns Ă— (0.99,1.01) 138ns Ă— (1.00,1.01) -25.41%
BenchmarkRegexpMatchEasy1_1K 975ns Ă— (1.00,1.01) 892ns Ă— (1.00,1.00) -8.51%
BenchmarkRegexpMatchMedium_32 328ns Ă— (0.99,1.00) 252ns Ă— (1.00,1.00) -23.17%
BenchmarkRegexpMatchMedium_1K 88.6µs × (1.00,1.01) 73.0µs × (1.00,1.01) -17.66%
BenchmarkRegexpMatchHard_32 4.69µs × (0.95,1.03) 3.85µs × (1.00,1.01) -17.91%
BenchmarkRegexpMatchHard_1K 133µs × (1.00,1.01) 117µs × (1.00,1.00) -12.34%
BenchmarkRevcomp 902ms Ă— (0.99,1.05) 1001ms Ă— (0.94,1.01) +11.04%
BenchmarkTemplate 174ms Ă— (0.99,1.01) 160ms Ă— (0.99,1.01) -7.70%
BenchmarkTimeParse 639ns Ă— (1.00,1.00) 622ns Ă— (1.00,1.00) -2.66%
BenchmarkTimeFormat 736ns Ă— (1.00,1.01) 736ns Ă— (1.00,1.02) ~
Change-Id: Ib3bbeb379f5f4819e6f5dcf69bc88a2b7ed41460
Reviewed-on: https://go-review.googlesource.com/9225
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Russ Cox <rsc@golang.org>
2015-04-17 11:07:38 -04:00
|
|
|
if flag_race == 0 {
|
|
|
|
|
if Debug_wb > 1 {
|
|
|
|
|
Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
|
|
|
|
|
}
|
cmd/internal/gc: emit write barriers at lower level
This is primarily preparation for inlining, not an optimization by itself,
but it still helps some.
name old new delta
BenchmarkBinaryTree17 18.2s Ă— (0.99,1.01) 17.9s Ă— (0.99,1.01) -1.57%
BenchmarkFannkuch11 4.44s Ă— (1.00,1.00) 4.42s Ă— (1.00,1.00) -0.40%
BenchmarkFmtFprintfEmpty 119ns Ă— (0.95,1.02) 118ns Ă— (0.96,1.02) ~
BenchmarkFmtFprintfString 501ns Ă— (0.99,1.02) 486ns Ă— (0.99,1.01) -2.89%
BenchmarkFmtFprintfInt 474ns Ă— (0.99,1.00) 457ns Ă— (0.99,1.01) -3.59%
BenchmarkFmtFprintfIntInt 792ns Ă— (1.00,1.00) 768ns Ă— (1.00,1.01) -3.03%
BenchmarkFmtFprintfPrefixedInt 574ns Ă— (1.00,1.01) 584ns Ă— (0.99,1.03) +1.83%
BenchmarkFmtFprintfFloat 749ns Ă— (1.00,1.00) 739ns Ă— (0.99,1.00) -1.34%
BenchmarkFmtManyArgs 2.94µs × (1.00,1.01) 2.77µs × (1.00,1.00) -5.76%
BenchmarkGobDecode 39.5ms Ă— (0.99,1.01) 39.3ms Ă— (0.99,1.01) ~
BenchmarkGobEncode 39.4ms Ă— (1.00,1.01) 39.4ms Ă— (0.99,1.00) ~
BenchmarkGzip 658ms Ă— (1.00,1.01) 661ms Ă— (0.99,1.01) ~
BenchmarkGunzip 142ms Ă— (1.00,1.00) 142ms Ă— (1.00,1.00) +0.22%
BenchmarkHTTPClientServer 134µs × (0.99,1.01) 133µs × (0.98,1.01) ~
BenchmarkJSONEncode 57.1ms Ă— (0.99,1.01) 56.5ms Ă— (0.99,1.01) ~
BenchmarkJSONDecode 141ms Ă— (1.00,1.00) 143ms Ă— (1.00,1.00) +1.09%
BenchmarkMandelbrot200 6.01ms Ă— (1.00,1.00) 6.01ms Ă— (1.00,1.00) ~
BenchmarkGoParse 10.1ms Ă— (0.91,1.09) 9.6ms Ă— (0.94,1.07) ~
BenchmarkRegexpMatchEasy0_32 207ns Ă— (1.00,1.01) 210ns Ă— (1.00,1.00) +1.45%
BenchmarkRegexpMatchEasy0_1K 592ns Ă— (0.99,1.00) 596ns Ă— (0.99,1.01) +0.68%
BenchmarkRegexpMatchEasy1_32 184ns Ă— (0.99,1.01) 184ns Ă— (0.99,1.01) ~
BenchmarkRegexpMatchEasy1_1K 1.01µs × (1.00,1.00) 1.01µs × (0.99,1.01) ~
BenchmarkRegexpMatchMedium_32 327ns Ă— (0.99,1.00) 327ns Ă— (1.00,1.01) ~
BenchmarkRegexpMatchMedium_1K 92.5µs × (1.00,1.00) 93.0µs × (1.00,1.02) +0.48%
BenchmarkRegexpMatchHard_32 4.79µs × (0.95,1.00) 4.76µs × (0.95,1.01) ~
BenchmarkRegexpMatchHard_1K 136µs × (1.00,1.00) 136µs × (1.00,1.01) ~
BenchmarkRevcomp 900ms Ă— (0.99,1.01) 892ms Ă— (1.00,1.01) ~
BenchmarkTemplate 170ms Ă— (0.99,1.01) 175ms Ă— (0.99,1.00) +2.95%
BenchmarkTimeParse 645ns Ă— (1.00,1.00) 638ns Ă— (1.00,1.00) -1.16%
BenchmarkTimeFormat 740ns Ă— (1.00,1.00) 772ns Ă— (1.00,1.00) +4.39%
Change-Id: I0be905e32791e0cb70ff01f169c4b309a971d981
Reviewed-on: https://go-review.googlesource.com/9159
Reviewed-by: Rick Hudson <rlh@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-04-17 00:25:10 -04:00
|
|
|
n.Op = OASWB
|
|
|
|
|
return n
|
|
|
|
|
}
|
cmd/internal/gc: emit typedmemmove write barrier from sgen
Emitting it here instead of rewriting the tree earlier sets us up
to generate an inline check, like we do for single pointers.
But even without the inline check, generating at this level lets
us generate significantly more efficient code, probably due to
having fewer temporaries and less complex high-level code
for the compiler to churn through.
Revcomp is worse, almost certainly due to register pressure.
name old new delta
BenchmarkBinaryTree17 18.0s Ă— (0.99,1.01) 18.0s Ă— (0.99,1.01) ~
BenchmarkFannkuch11 4.43s Ă— (1.00,1.00) 4.36s Ă— (1.00,1.00) -1.44%
BenchmarkFmtFprintfEmpty 114ns Ă— (0.95,1.05) 86ns Ă— (0.97,1.06) -24.12%
BenchmarkFmtFprintfString 468ns Ă— (0.99,1.01) 420ns Ă— (0.99,1.02) -10.16%
BenchmarkFmtFprintfInt 433ns Ă— (1.00,1.01) 386ns Ă— (0.99,1.02) -10.74%
BenchmarkFmtFprintfIntInt 748ns Ă— (0.99,1.01) 647ns Ă— (0.99,1.01) -13.56%
BenchmarkFmtFprintfPrefixedInt 547ns Ă— (0.99,1.01) 499ns Ă— (0.99,1.02) -8.78%
BenchmarkFmtFprintfFloat 756ns Ă— (1.00,1.01) 689ns Ă— (1.00,1.00) -8.86%
BenchmarkFmtManyArgs 2.79µs × (1.00,1.01) 2.53µs × (1.00,1.00) -9.30%
BenchmarkGobDecode 39.6ms Ă— (0.99,1.00) 39.2ms Ă— (0.98,1.01) -1.07%
BenchmarkGobEncode 37.6ms Ă— (1.00,1.01) 37.5ms Ă— (0.99,1.01) ~
BenchmarkGzip 663ms Ă— (0.99,1.02) 660ms Ă— (0.98,1.01) ~
BenchmarkGunzip 142ms Ă— (1.00,1.00) 143ms Ă— (1.00,1.00) ~
BenchmarkHTTPClientServer 132µs × (0.99,1.01) 133µs × (0.99,1.02) ~
BenchmarkJSONEncode 56.2ms Ă— (0.99,1.01) 54.0ms Ă— (0.98,1.01) -3.97%
BenchmarkJSONDecode 138ms Ă— (1.00,1.00) 134ms Ă— (0.99,1.02) -2.70%
BenchmarkMandelbrot200 6.03ms Ă— (1.00,1.01) 6.00ms Ă— (1.00,1.01) ~
BenchmarkGoParse 9.82ms Ă— (0.93,1.10) 10.35ms Ă— (0.88,1.11) ~
BenchmarkRegexpMatchEasy0_32 207ns Ă— (1.00,1.00) 163ns Ă— (0.99,1.01) -21.26%
BenchmarkRegexpMatchEasy0_1K 581ns Ă— (1.00,1.01) 566ns Ă— (0.99,1.00) -2.50%
BenchmarkRegexpMatchEasy1_32 185ns Ă— (0.99,1.01) 138ns Ă— (1.00,1.01) -25.41%
BenchmarkRegexpMatchEasy1_1K 975ns Ă— (1.00,1.01) 892ns Ă— (1.00,1.00) -8.51%
BenchmarkRegexpMatchMedium_32 328ns Ă— (0.99,1.00) 252ns Ă— (1.00,1.00) -23.17%
BenchmarkRegexpMatchMedium_1K 88.6µs × (1.00,1.01) 73.0µs × (1.00,1.01) -17.66%
BenchmarkRegexpMatchHard_32 4.69µs × (0.95,1.03) 3.85µs × (1.00,1.01) -17.91%
BenchmarkRegexpMatchHard_1K 133µs × (1.00,1.01) 117µs × (1.00,1.00) -12.34%
BenchmarkRevcomp 902ms Ă— (0.99,1.05) 1001ms Ă— (0.94,1.01) +11.04%
BenchmarkTemplate 174ms Ă— (0.99,1.01) 160ms Ă— (0.99,1.01) -7.70%
BenchmarkTimeParse 639ns Ă— (1.00,1.00) 622ns Ă— (1.00,1.00) -2.66%
BenchmarkTimeFormat 736ns Ă— (1.00,1.01) 736ns Ă— (1.00,1.02) ~
Change-Id: Ib3bbeb379f5f4819e6f5dcf69bc88a2b7ed41460
Reviewed-on: https://go-review.googlesource.com/9225
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Russ Cox <rsc@golang.org>
2015-04-17 11:07:38 -04:00
|
|
|
// Use slow path always for race detector.
|
2015-04-17 00:34:18 -04:00
|
|
|
if Debug_wb > 0 {
|
|
|
|
|
Warnl(int(n.Lineno), "write barrier")
|
|
|
|
|
}
|
cmd/internal/gc: emit typedmemmove write barrier from sgen
Emitting it here instead of rewriting the tree earlier sets us up
to generate an inline check, like we do for single pointers.
But even without the inline check, generating at this level lets
us generate significantly more efficient code, probably due to
having fewer temporaries and less complex high-level code
for the compiler to churn through.
Revcomp is worse, almost certainly due to register pressure.
name old new delta
BenchmarkBinaryTree17 18.0s Ă— (0.99,1.01) 18.0s Ă— (0.99,1.01) ~
BenchmarkFannkuch11 4.43s Ă— (1.00,1.00) 4.36s Ă— (1.00,1.00) -1.44%
BenchmarkFmtFprintfEmpty 114ns Ă— (0.95,1.05) 86ns Ă— (0.97,1.06) -24.12%
BenchmarkFmtFprintfString 468ns Ă— (0.99,1.01) 420ns Ă— (0.99,1.02) -10.16%
BenchmarkFmtFprintfInt 433ns Ă— (1.00,1.01) 386ns Ă— (0.99,1.02) -10.74%
BenchmarkFmtFprintfIntInt 748ns Ă— (0.99,1.01) 647ns Ă— (0.99,1.01) -13.56%
BenchmarkFmtFprintfPrefixedInt 547ns Ă— (0.99,1.01) 499ns Ă— (0.99,1.02) -8.78%
BenchmarkFmtFprintfFloat 756ns Ă— (1.00,1.01) 689ns Ă— (1.00,1.00) -8.86%
BenchmarkFmtManyArgs 2.79µs × (1.00,1.01) 2.53µs × (1.00,1.00) -9.30%
BenchmarkGobDecode 39.6ms Ă— (0.99,1.00) 39.2ms Ă— (0.98,1.01) -1.07%
BenchmarkGobEncode 37.6ms Ă— (1.00,1.01) 37.5ms Ă— (0.99,1.01) ~
BenchmarkGzip 663ms Ă— (0.99,1.02) 660ms Ă— (0.98,1.01) ~
BenchmarkGunzip 142ms Ă— (1.00,1.00) 143ms Ă— (1.00,1.00) ~
BenchmarkHTTPClientServer 132µs × (0.99,1.01) 133µs × (0.99,1.02) ~
BenchmarkJSONEncode 56.2ms Ă— (0.99,1.01) 54.0ms Ă— (0.98,1.01) -3.97%
BenchmarkJSONDecode 138ms Ă— (1.00,1.00) 134ms Ă— (0.99,1.02) -2.70%
BenchmarkMandelbrot200 6.03ms Ă— (1.00,1.01) 6.00ms Ă— (1.00,1.01) ~
BenchmarkGoParse 9.82ms Ă— (0.93,1.10) 10.35ms Ă— (0.88,1.11) ~
BenchmarkRegexpMatchEasy0_32 207ns Ă— (1.00,1.00) 163ns Ă— (0.99,1.01) -21.26%
BenchmarkRegexpMatchEasy0_1K 581ns Ă— (1.00,1.01) 566ns Ă— (0.99,1.00) -2.50%
BenchmarkRegexpMatchEasy1_32 185ns Ă— (0.99,1.01) 138ns Ă— (1.00,1.01) -25.41%
BenchmarkRegexpMatchEasy1_1K 975ns Ă— (1.00,1.01) 892ns Ă— (1.00,1.00) -8.51%
BenchmarkRegexpMatchMedium_32 328ns Ă— (0.99,1.00) 252ns Ă— (1.00,1.00) -23.17%
BenchmarkRegexpMatchMedium_1K 88.6µs × (1.00,1.01) 73.0µs × (1.00,1.01) -17.66%
BenchmarkRegexpMatchHard_32 4.69µs × (0.95,1.03) 3.85µs × (1.00,1.01) -17.91%
BenchmarkRegexpMatchHard_1K 133µs × (1.00,1.01) 117µs × (1.00,1.00) -12.34%
BenchmarkRevcomp 902ms Ă— (0.99,1.05) 1001ms Ă— (0.94,1.01) +11.04%
BenchmarkTemplate 174ms Ă— (0.99,1.01) 160ms Ă— (0.99,1.01) -7.70%
BenchmarkTimeParse 639ns Ă— (1.00,1.00) 622ns Ă— (1.00,1.00) -2.66%
BenchmarkTimeFormat 736ns Ă— (1.00,1.01) 736ns Ă— (1.00,1.02) ~
Change-Id: Ib3bbeb379f5f4819e6f5dcf69bc88a2b7ed41460
Reviewed-on: https://go-review.googlesource.com/9225
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Russ Cox <rsc@golang.org>
2015-04-17 11:07:38 -04:00
|
|
|
t := n.Left.Type
|
2015-02-23 16:07:24 -05:00
|
|
|
l := Nod(OADDR, n.Left, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if Isslice(t) {
|
2015-02-13 14:40:36 -05:00
|
|
|
n = mkcall1(writebarrierfn("writebarrierslice", t, n.Right.Type), nil, init, l, n.Right)
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if Isinter(t) {
|
2015-02-13 14:40:36 -05:00
|
|
|
n = mkcall1(writebarrierfn("writebarrieriface", t, n.Right.Type), nil, init, l, n.Right)
|
|
|
|
|
} else if t.Width <= int64(4*Widthptr) {
|
2015-02-23 16:07:24 -05:00
|
|
|
x := int64(0)
|
2015-03-02 21:25:33 -05:00
|
|
|
if applywritebarrier_bv.b == nil {
|
2015-02-13 14:40:36 -05:00
|
|
|
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")
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
var name string
|
2015-02-13 14:40:36 -05:00
|
|
|
switch t.Width / int64(Widthptr) {
|
|
|
|
|
default:
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("found writebarrierfat for %d-byte object of type %v", int(t.Width), t)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
|
runtime: change unused argument in fat write barriers from pointer to scalar
The argument is unused, only present for alignment of the
following argument. The compiler today always passes a zero
but I'd rather not write anything there during the call sequence,
so mark it as a scalar so the garbage collector won't look at it.
As expected, no significant performance change.
name old new delta
BenchmarkBinaryTree17 17.9s Ă— (0.99,1.00) 17.9s Ă— (0.99,1.01) ~
BenchmarkFannkuch11 4.35s Ă— (1.00,1.00) 4.35s Ă— (1.00,1.00) ~
BenchmarkFmtFprintfEmpty 120ns Ă— (0.94,1.05) 120ns Ă— (0.93,1.06) ~
BenchmarkFmtFprintfString 477ns Ă— (1.00,1.00) 479ns Ă— (0.99,1.00) ~
BenchmarkFmtFprintfInt 450ns Ă— (0.99,1.01) 452ns Ă— (0.99,1.02) ~
BenchmarkFmtFprintfIntInt 765ns Ă— (0.99,1.01) 766ns Ă— (0.99,1.01) ~
BenchmarkFmtFprintfPrefixedInt 569ns Ă— (0.99,1.01) 576ns Ă— (0.98,1.01) ~
BenchmarkFmtFprintfFloat 728ns Ă— (1.00,1.00) 730ns Ă— (1.00,1.01) ~
BenchmarkFmtManyArgs 2.82µs × (0.99,1.01) 2.84µs × (0.99,1.00) ~
BenchmarkGobDecode 39.1ms Ă— (0.99,1.01) 39.3ms Ă— (0.98,1.01) ~
BenchmarkGobEncode 39.4ms Ă— (0.99,1.01) 39.5ms Ă— (0.99,1.01) ~
BenchmarkGzip 661ms Ă— (0.99,1.01) 663ms Ă— (1.00,1.01) ~
BenchmarkGunzip 143ms Ă— (1.00,1.00) 143ms Ă— (1.00,1.00) ~
BenchmarkHTTPClientServer 133µs × (0.99,1.01) 132µs × (0.99,1.01) ~
BenchmarkJSONEncode 57.3ms Ă— (0.99,1.04) 57.4ms Ă— (0.99,1.01) ~
BenchmarkJSONDecode 139ms Ă— (0.99,1.00) 139ms Ă— (0.99,1.00) ~
BenchmarkMandelbrot200 6.02ms Ă— (1.00,1.00) 6.03ms Ă— (1.00,1.00) ~
BenchmarkGoParse 9.72ms Ă— (0.92,1.11) 10.31ms Ă— (0.89,1.14) ~
BenchmarkRegexpMatchEasy0_32 209ns Ă— (1.00,1.01) 209ns Ă— (1.00,1.00) ~
BenchmarkRegexpMatchEasy0_1K 592ns Ă— (0.99,1.00) 591ns Ă— (0.99,1.00) ~
BenchmarkRegexpMatchEasy1_32 183ns Ă— (0.98,1.01) 184ns Ă— (0.99,1.02) ~
BenchmarkRegexpMatchEasy1_1K 1.01µs × (1.00,1.01) 1.01µs × (1.00,1.00) ~
BenchmarkRegexpMatchMedium_32 330ns Ă— (1.00,1.00) 330ns Ă— (1.00,1.00) ~
BenchmarkRegexpMatchMedium_1K 92.4µs × (1.00,1.00) 92.6µs × (1.00,1.00) ~
BenchmarkRegexpMatchHard_32 4.77µs × (0.95,1.01) 4.80µs × (0.95,1.00) ~
BenchmarkRegexpMatchHard_1K 136µs × (1.00,1.00) 136µs × (1.00,1.00) ~
BenchmarkRevcomp 906ms Ă— (0.99,1.05) 900ms Ă— (0.99,1.04) ~
BenchmarkTemplate 171ms Ă— (0.99,1.01) 172ms Ă— (1.00,1.00) ~
BenchmarkTimeParse 638ns Ă— (1.00,1.00) 637ns Ă— (1.00,1.00) ~
BenchmarkTimeFormat 745ns Ă— (0.99,1.02) 744ns Ă— (1.00,1.01) ~
Change-Id: I0aeac5dc7adfd75e2223e3aabfedc7818d339f9b
Reviewed-on: https://go-review.googlesource.com/9320
Reviewed-by: Austin Clements <austin@google.com>
2015-04-24 13:41:57 -04:00
|
|
|
n = mkcall1(writebarrierfn(name, t, n.Right.Type), nil, init, l, Nodintconst(0), n.Right)
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
2015-02-23 16:07:24 -05:00
|
|
|
r := n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
|
|
|
|
if n.Op != OAS {
|
|
|
|
|
Fatal("convas: not OAS %v", Oconv(int(n.Op), 0))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n.Typecheck = 1
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var lt *Type
|
|
|
|
|
var rt *Type
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-02-23 16:07:24 -05:00
|
|
|
map_ := n.Left.Left
|
|
|
|
|
key := n.Left.Right
|
|
|
|
|
val := n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
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 n *Node
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
c := 0 // function calls
|
|
|
|
|
t := 0 // total parameters
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := all; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
n = l.N
|
|
|
|
|
t++
|
|
|
|
|
ullmancalc(n)
|
|
|
|
|
if n.Ullman >= UINF {
|
|
|
|
|
c++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if c == 0 || t == 1 {
|
|
|
|
|
return all
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var g *NodeList // fncalls assigned to tempnames
|
|
|
|
|
var f *Node // last fncall assigned to stack
|
|
|
|
|
var r *NodeList // non fncalls and tempnames assigned to stack
|
2015-02-23 16:07:24 -05:00
|
|
|
d := 0
|
|
|
|
|
var a *Node
|
|
|
|
|
for l := all; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 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.
|
2015-03-02 14:22:05 -05:00
|
|
|
var early *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var mapinit *NodeList
|
2015-02-23 16:07:24 -05:00
|
|
|
for list := all; list != nil; list = list.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if l.Op == OINDEX && Isfixedarray(l.Left.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OINDEX, OINDEXMAP:
|
2015-02-13 14:40:36 -05:00
|
|
|
reorder3save(&l.Left, all, list, &early)
|
|
|
|
|
reorder3save(&l.Right, all, list, &early)
|
|
|
|
|
if l.Op == OINDEXMAP {
|
|
|
|
|
list.N = convas(list.N, &mapinit)
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case OIND, ODOTPTR:
|
2015-02-13 14:40:36 -05:00
|
|
|
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) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-17 22:13:49 -05:00
|
|
|
if !aliased(n, all, stop) {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
q := temp(n.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if n.Op == OINDEX && Isfixedarray(n.Left.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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?
|
|
|
|
|
*/
|
2015-02-17 22:13:49 -05:00
|
|
|
func aliased(n *Node, all *NodeList, stop *NodeList) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if n == nil {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2015-02-23 16:07:24 -05:00
|
|
|
memwrite := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
varwrite := 0
|
|
|
|
|
var a *Node
|
|
|
|
|
for l := all; l != stop; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
a = outervalue(l.N.Left)
|
|
|
|
|
if a.Op != ONAME {
|
|
|
|
|
memwrite = 1
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch n.Class {
|
|
|
|
|
default:
|
|
|
|
|
varwrite = 1
|
|
|
|
|
continue
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case PAUTO, PPARAM, PPARAMOUT:
|
2015-03-05 18:20:54 +11:00
|
|
|
if n.Addrtaken {
|
2015-02-13 14:40:36 -05:00
|
|
|
varwrite = 1
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if vmatch2(a, n) {
|
2015-02-13 14:40:36 -05:00
|
|
|
// Direct hit.
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2015-02-17 22:13:49 -05:00
|
|
|
if memwrite == 0 && varwrite == 0 {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If n does not refer to computed addresses
|
|
|
|
|
// (that is, if n only refers to variables whose addresses
|
|
|
|
|
// have not been taken), no aliasing.
|
2015-02-17 22:13:49 -05:00
|
|
|
if varexpr(n) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, both the writes and n refer to computed memory addresses.
|
|
|
|
|
// Assume that they might conflict.
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* does the evaluation of n only refer to variables
|
|
|
|
|
* whose addresses have not been taken?
|
|
|
|
|
* (and no other memory)
|
|
|
|
|
*/
|
2015-02-17 22:13:49 -05:00
|
|
|
func varexpr(n *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if n == nil {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
|
case OLITERAL:
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case ONAME:
|
|
|
|
|
switch n.Class {
|
2015-04-01 09:38:44 -07:00
|
|
|
case PAUTO, PPARAM, PPARAMOUT:
|
2015-03-05 18:20:54 +11:00
|
|
|
if !n.Addrtaken {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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:
|
2015-02-17 22:13:49 -05:00
|
|
|
return varexpr(n.Left) && varexpr(n.Right)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Be conservative.
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* is the name l mentioned in r?
|
|
|
|
|
*/
|
2015-02-17 22:13:49 -05:00
|
|
|
func vmatch2(l *Node, r *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if r == nil {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
switch r.Op {
|
|
|
|
|
// match each right given left
|
|
|
|
|
case ONAME:
|
2015-02-17 22:13:49 -05:00
|
|
|
return l == r
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case OLITERAL:
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if vmatch2(l, r.Left) {
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if vmatch2(l, r.Right) {
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
for ll := r.List; ll != nil; ll = ll.Next {
|
2015-02-17 22:13:49 -05:00
|
|
|
if vmatch2(l, ll.N) {
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* is any name mentioned in l also mentioned in r?
|
2015-03-11 12:58:47 -07:00
|
|
|
* called by sinit.go
|
2015-02-13 14:40:36 -05:00
|
|
|
*/
|
2015-02-17 22:13:49 -05:00
|
|
|
func vmatch1(l *Node, r *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
/*
|
|
|
|
|
* isolate all left sides
|
|
|
|
|
*/
|
|
|
|
|
if l == nil || r == nil {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
switch l.Op {
|
|
|
|
|
case ONAME:
|
|
|
|
|
switch l.Class {
|
2015-04-01 09:38:44 -07:00
|
|
|
case PPARAM, PPARAMREF, PAUTO:
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
|
|
|
|
|
// assignment to non-stack variable
|
|
|
|
|
// must be delayed if right has function calls.
|
|
|
|
|
default:
|
|
|
|
|
if r.Ullman >= UINF {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return vmatch2(l, r)
|
|
|
|
|
|
|
|
|
|
case OLITERAL:
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if vmatch1(l.Left, r) {
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if vmatch1(l.Right, r) {
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
for ll := l.List; ll != nil; ll = ll.Next {
|
2015-02-17 22:13:49 -05:00
|
|
|
if vmatch1(ll.N, r) {
|
|
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 savet Iter
|
|
|
|
|
var v *Node
|
|
|
|
|
var as *Node
|
|
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var nn *NodeList
|
2015-02-23 16:07:24 -05:00
|
|
|
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if v == nil || v.Class&PHEAP == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// generate allocation & copying code
|
|
|
|
|
if compiling_runtime != 0 {
|
2015-04-17 12:03:22 -04:00
|
|
|
Yyerror("%v escapes to heap, not allowed in runtime.", v)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
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 savet Iter
|
|
|
|
|
var v *Node
|
|
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var nn *NodeList
|
2015-02-23 16:07:24 -05:00
|
|
|
for t := Structfirst(&savet, argin); t != nil; t = structnext(&savet) {
|
2015-02-13 14:40:36 -05:00
|
|
|
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() {
|
2015-02-23 16:07:24 -05:00
|
|
|
lno := lineno
|
2015-02-13 14:40:36 -05:00
|
|
|
lineno = Curfn.Lineno
|
2015-02-23 16:07:24 -05:00
|
|
|
nn := paramstoheap(getthis(Curfn.Type), 0)
|
2015-02-13 14:40:36 -05:00
|
|
|
nn = concat(nn, paramstoheap(getinarg(Curfn.Type), 0))
|
|
|
|
|
nn = concat(nn, paramstoheap(Getoutarg(Curfn.Type), 1))
|
2015-03-25 19:33:01 -07:00
|
|
|
Curfn.Func.Enter = concat(Curfn.Func.Enter, nn)
|
|
|
|
|
lineno = Curfn.Func.Endlineno
|
|
|
|
|
Curfn.Func.Exit = returnsfromheap(Getoutarg(Curfn.Type))
|
2015-02-13 14:40:36 -05:00
|
|
|
lineno = lno
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func vmkcall(fn *Node, t *Type, init **NodeList, va []*Node) *Node {
|
|
|
|
|
if fn.Type == nil || fn.Type.Etype != TFUNC {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("mkcall %v %v", fn, fn.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var args *NodeList
|
2015-02-23 16:07:24 -05:00
|
|
|
n := fn.Type.Intuple
|
|
|
|
|
for i := 0; i < n; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
args = list(args, va[i])
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
r := Nod(OCALL, fn, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
|
|
|
|
if t.Etype != TCHAN {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("chanfn %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook(name, 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
switch n {
|
|
|
|
|
default:
|
|
|
|
|
Fatal("chanfn %d", n)
|
|
|
|
|
case 1:
|
|
|
|
|
substArgTypes(fn, t.Type)
|
|
|
|
|
case 2:
|
|
|
|
|
substArgTypes(fn, t.Type, t.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
return fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mapfn(name string, t *Type) *Node {
|
|
|
|
|
if t.Etype != TMAP {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("mapfn %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook(name, 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, t.Down, t.Type, t.Down, t.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
return fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mapfndel(name string, t *Type) *Node {
|
|
|
|
|
if t.Etype != TMAP {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("mapfn %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook(name, 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, t.Down, t.Type, t.Down)
|
2015-02-13 14:40:36 -05:00
|
|
|
return fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writebarrierfn(name string, l *Type, r *Type) *Node {
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook(name, 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, l, r)
|
2015-02-13 14:40:36 -05:00
|
|
|
return fn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func addstr(n *Node, init **NodeList) *Node {
|
|
|
|
|
// orderexpr rewrote OADDSTR to have a list of strings.
|
2015-02-23 16:07:24 -05:00
|
|
|
c := count(n.List)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if c < 2 {
|
|
|
|
|
Yyerror("addstr count %d too small", c)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
buf := nodnil()
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Esc == EscNone {
|
2015-02-23 16:07:24 -05:00
|
|
|
sz := int64(0)
|
|
|
|
|
for l := n.List; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Op == OLITERAL {
|
2015-03-02 16:03:26 -05:00
|
|
|
sz += int64(len(n.Val.U.Sval))
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Don't allocate the buffer if the result won't fit.
|
|
|
|
|
if sz < tmpstringbufsize {
|
|
|
|
|
// Create temporary buffer for result string on stack.
|
2015-02-23 16:07:24 -05:00
|
|
|
t := aindex(Nodintconst(tmpstringbufsize), Types[TUINT8])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
buf = Nod(OADDR, temp(t), nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// build list of string arguments
|
2015-02-23 16:07:24 -05:00
|
|
|
args := list1(buf)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.List; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
args = list(args, conv(l.N, Types[TSTRING]))
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-06 12:02:24 -08:00
|
|
|
var fn string
|
2015-02-13 14:40:36 -05:00
|
|
|
if c <= 5 {
|
|
|
|
|
// small numbers of strings use direct runtime helpers.
|
|
|
|
|
// note: orderexpr knows this cutoff too.
|
2015-03-06 12:02:24 -08:00
|
|
|
fn = fmt.Sprintf("concatstring%d", c)
|
2015-02-13 14:40:36 -05:00
|
|
|
} else {
|
|
|
|
|
// large numbers of strings are passed to the runtime as a slice.
|
2015-03-06 12:02:24 -08:00
|
|
|
fn = "concatstrings"
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
t := typ(TARRAY)
|
2015-02-13 14:40:36 -05:00
|
|
|
t.Type = Types[TSTRING]
|
|
|
|
|
t.Bound = -1
|
2015-02-23 16:07:24 -05:00
|
|
|
slice := Nod(OCOMPLIT, nil, typenod(t))
|
2015-02-13 14:40:36 -05:00
|
|
|
slice.Alloc = n.Alloc
|
|
|
|
|
slice.List = args.Next // skip buf arg
|
|
|
|
|
args = list1(buf)
|
|
|
|
|
args = list(args, slice)
|
|
|
|
|
slice.Esc = EscNone
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-06 12:02:24 -08:00
|
|
|
cat := syslook(fn, 1)
|
2015-02-23 16:07:24 -05:00
|
|
|
r := Nod(OCALL, cat, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
|
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.List; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
l.N = cheapexpr(l.N, init)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
l1 := n.List.N
|
|
|
|
|
l2 := n.List.Next.N
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
s := temp(l1.Type) // var s []T
|
2015-03-02 14:22:05 -05:00
|
|
|
var l *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
l = list(l, Nod(OAS, s, l1)) // s = l1
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nt := temp(Types[TINT])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nif := Nod(OIF, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// 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))
|
|
|
|
|
|
2015-02-27 15:13:05 +09:00
|
|
|
// instantiate growslice(Type*, []any, int) []any
|
2015-03-05 13:57:36 -05:00
|
|
|
fn := syslook("growslice", 1) // growslice(<type>, old []T, n int64) (ret []T)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, s.Type.Type, s.Type.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// s = growslice(T, s, n)
|
2015-02-27 15:13:05 +09:00
|
|
|
nif.Nbody = list1(Nod(OAS, s, mkcall1(fn, s.Type, &nif.Ninit, typename(s.Type), s, nt)))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
l = list(l, nif)
|
|
|
|
|
|
|
|
|
|
if haspointers(l1.Type.Type) {
|
|
|
|
|
// copy(s[len(l1):len(l1)+len(l2)], l2)
|
2015-02-23 16:07:24 -05:00
|
|
|
nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil))))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
nptr1.Etype = 1
|
2015-02-23 16:07:24 -05:00
|
|
|
nptr2 := l2
|
|
|
|
|
fn := syslook("typedslicecopy", 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, l1.Type, l2.Type)
|
2015-02-23 16:07:24 -05:00
|
|
|
nt := mkcall1(fn, Types[TINT], &l, typename(l1.Type.Type), nptr1, nptr2)
|
2015-02-13 14:40:36 -05:00
|
|
|
l = list(l, nt)
|
|
|
|
|
} else if flag_race != 0 {
|
|
|
|
|
// rely on runtime to instrument copy.
|
|
|
|
|
// copy(s[len(l1):len(l1)+len(l2)], l2)
|
2015-02-23 16:07:24 -05:00
|
|
|
nptr1 := Nod(OSLICE, s, Nod(OKEY, Nod(OLEN, l1, nil), Nod(OADD, Nod(OLEN, l1, nil), Nod(OLEN, l2, nil))))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
nptr1.Etype = 1
|
2015-02-23 16:07:24 -05:00
|
|
|
nptr2 := l2
|
|
|
|
|
var fn *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
if l2.Type.Etype == TSTRING {
|
|
|
|
|
fn = syslook("slicestringcopy", 1)
|
|
|
|
|
} else {
|
|
|
|
|
fn = syslook("slicecopy", 1)
|
|
|
|
|
}
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, l1.Type, l2.Type)
|
2015-02-23 16:07:24 -05:00
|
|
|
nt := mkcall1(fn, Types[TINT], &l, nptr1, nptr2, Nodintconst(s.Type.Type.Width))
|
2015-02-13 14:40:36 -05:00
|
|
|
l = list(l, nt)
|
|
|
|
|
} else {
|
|
|
|
|
// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
|
2015-02-23 16:07:24 -05:00
|
|
|
nptr1 := Nod(OINDEX, s, Nod(OLEN, l1, nil))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
nptr1.Bounded = true
|
2015-02-13 14:40:36 -05:00
|
|
|
nptr1 = Nod(OADDR, nptr1, nil)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nptr2 := Nod(OSPTR, l2, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook("memmove", 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, s.Type.Type, s.Type.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nwid := cheapexpr(conv(Nod(OLEN, l2, nil), Types[TUINTPTR]), &l)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
nwid = Nod(OMUL, nwid, Nodintconst(s.Type.Type.Width))
|
2015-02-23 16:07:24 -05:00
|
|
|
nt := mkcall1(fn, nil, &l, nptr1, nptr2, nwid)
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
|
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
for l := n.List; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
l.N = cheapexpr(l.N, init)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nsrc := n.List.N
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Resolve slice type of multi-valued return.
|
2015-02-17 22:13:49 -05:00
|
|
|
if Istype(nsrc.Type, TSTRUCT) {
|
2015-02-13 14:40:36 -05:00
|
|
|
nsrc.Type = nsrc.Type.Type.Type
|
|
|
|
|
}
|
2015-02-23 16:07:24 -05:00
|
|
|
argc := count(n.List) - 1
|
2015-02-13 14:40:36 -05:00
|
|
|
if argc < 1 {
|
|
|
|
|
return nsrc
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var l *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
ns := temp(nsrc.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
l = list(l, Nod(OAS, ns, nsrc)) // s = src
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
na := Nodintconst(int64(argc)) // const argc
|
|
|
|
|
nx := Nod(OIF, nil, nil) // if cap(s) - len(s) < argc
|
2015-02-13 14:40:36 -05:00
|
|
|
nx.Ntest = Nod(OLT, Nod(OSUB, Nod(OCAP, ns, nil), Nod(OLEN, ns, nil)), na)
|
|
|
|
|
|
2015-02-27 15:13:05 +09:00
|
|
|
fn := syslook("growslice", 1) // growslice(<type>, old []T, n int) (ret []T)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, ns.Type.Type, ns.Type.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-27 15:13:05 +09:00
|
|
|
nx.Nbody = list1(Nod(OAS, ns, mkcall1(fn, ns.Type, &nx.Ninit, typename(ns.Type), ns, na)))
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
l = list(l, nx)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nn := temp(Types[TINT])
|
2015-02-13 14:40:36 -05:00
|
|
|
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]
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
for a := n.List.Next; a != nil; a = a.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
nx = Nod(OINDEX, ns, nn) // s[n] ...
|
2015-02-17 22:13:49 -05:00
|
|
|
nx.Bounded = true
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
|
|
|
|
if haspointers(n.Left.Type.Type) {
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := writebarrierfn("typedslicecopy", n.Left.Type, n.Right.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
return mkcall1(fn, n.Type, init, typename(n.Left.Type.Type), n.Left, n.Right)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if runtimecall != 0 {
|
2015-02-23 16:07:24 -05:00
|
|
|
var fn *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Right.Type.Etype == TSTRING {
|
|
|
|
|
fn = syslook("slicestringcopy", 1)
|
|
|
|
|
} else {
|
|
|
|
|
fn = syslook("slicecopy", 1)
|
|
|
|
|
}
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, n.Left.Type, n.Right.Type)
|
2015-02-13 14:40:36 -05:00
|
|
|
return mkcall1(fn, n.Type, init, n.Left, n.Right, Nodintconst(n.Left.Type.Type.Width))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
walkexpr(&n.Left, init)
|
|
|
|
|
walkexpr(&n.Right, init)
|
2015-02-23 16:07:24 -05:00
|
|
|
nl := temp(n.Left.Type)
|
|
|
|
|
nr := temp(n.Right.Type)
|
2015-03-02 14:22:05 -05:00
|
|
|
var l *NodeList
|
2015-02-13 14:40:36 -05:00
|
|
|
l = list(l, Nod(OAS, nl, n.Left))
|
|
|
|
|
l = list(l, Nod(OAS, nr, n.Right))
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nfrm := Nod(OSPTR, nr, nil)
|
|
|
|
|
nto := Nod(OSPTR, nl, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nlen := temp(Types[TINT])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// n = len(to)
|
|
|
|
|
l = list(l, Nod(OAS, nlen, Nod(OLEN, nl, nil)))
|
|
|
|
|
|
|
|
|
|
// if n > len(frm) { n = len(frm) }
|
2015-02-23 16:07:24 -05:00
|
|
|
nif := Nod(OIF, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := syslook("memmove", 1)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(fn, nl.Type.Type, nl.Type.Type)
|
2015-02-23 16:07:24 -05:00
|
|
|
nwid := temp(Types[TUINTPTR])
|
2015-02-13 14:40:36 -05:00
|
|
|
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 hb *Node
|
|
|
|
|
var cb *Node
|
|
|
|
|
|
|
|
|
|
// print("before sliceany: %+N\n", n);
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
src := n.Left
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
lb := n.Right.Left
|
|
|
|
|
slice3 := n.Op == OSLICE3 || n.Op == OSLICE3ARR
|
2015-02-17 22:13:49 -05:00
|
|
|
if slice3 {
|
2015-02-13 14:40:36 -05:00
|
|
|
hb = n.Right.Right.Left
|
|
|
|
|
cb = n.Right.Right.Right
|
|
|
|
|
} else {
|
|
|
|
|
hb = n.Right.Right
|
|
|
|
|
cb = nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
bounded := int(n.Etype)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var bound *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-02-23 16:07:24 -05:00
|
|
|
bv := int64(1 << 50)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(bound, CTINT) {
|
|
|
|
|
if !Smallintconst(bound) {
|
2015-02-13 14:40:36 -05:00
|
|
|
Yyerror("array len too large")
|
|
|
|
|
} else {
|
|
|
|
|
bv = Mpgetfix(bound.Val.U.Xval)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(cb, CTINT) {
|
2015-02-23 16:07:24 -05:00
|
|
|
cbv := Mpgetfix(cb.Val.U.Xval)
|
2015-02-13 14:40:36 -05:00
|
|
|
if cbv < 0 || cbv > bv {
|
|
|
|
|
Yyerror("slice index out of bounds")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(hb, CTINT) {
|
2015-02-23 16:07:24 -05:00
|
|
|
hbv := Mpgetfix(hb.Val.U.Xval)
|
2015-02-13 14:40:36 -05:00
|
|
|
if hbv < 0 || hbv > bv {
|
|
|
|
|
Yyerror("slice index out of bounds")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(lb, CTINT) {
|
2015-02-23 16:07:24 -05:00
|
|
|
lbv := Mpgetfix(lb.Val.U.Xval)
|
2015-02-13 14:40:36 -05:00
|
|
|
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() }
|
|
|
|
|
|
|
|
|
|
// All comparisons are unsigned to avoid testing < 0.
|
2015-02-23 16:07:24 -05:00
|
|
|
bt := Types[Simtype[TUINT]]
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
2015-03-02 20:34:22 -05:00
|
|
|
var chk0 *Node // cap(src) < cb
|
2015-02-13 14:40:36 -05:00
|
|
|
if cb != nil {
|
|
|
|
|
cb = cheapexpr(conv(cb, bt), init)
|
2015-02-17 22:13:49 -05:00
|
|
|
if bounded == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
chk0 = Nod(OLT, bound, cb)
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if slice3 {
|
2015-02-13 14:40:36 -05:00
|
|
|
// When we figure out what this means, implement it.
|
|
|
|
|
Fatal("slice3 with cb == N") // rejected by parser
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 20:34:22 -05:00
|
|
|
var chk1 *Node // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
|
2015-02-13 14:40:36 -05:00
|
|
|
if hb != nil {
|
|
|
|
|
hb = cheapexpr(conv(hb, bt), init)
|
2015-02-17 22:13:49 -05:00
|
|
|
if bounded == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
if cb != nil {
|
|
|
|
|
chk1 = Nod(OLT, cb, hb)
|
|
|
|
|
} else {
|
|
|
|
|
chk1 = Nod(OLT, bound, hb)
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if slice3 {
|
2015-02-13 14:40:36 -05:00
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 20:34:22 -05:00
|
|
|
var chk2 *Node // hb < lb
|
2015-02-13 14:40:36 -05:00
|
|
|
if lb != nil {
|
|
|
|
|
lb = cheapexpr(conv(lb, bt), init)
|
2015-02-17 22:13:49 -05:00
|
|
|
if bounded == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
chk2 = Nod(OLT, hb, lb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if chk0 != nil || chk1 != nil || chk2 != nil {
|
2015-02-23 16:07:24 -05:00
|
|
|
chk := Nod(OIF, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
2015-02-17 22:13:49 -05:00
|
|
|
if !slice3 {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-02-23 16:07:24 -05:00
|
|
|
var w int64
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
|
|
|
|
// 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).
|
2015-02-23 16:07:24 -05:00
|
|
|
a := algtype1(t, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if a != AMEM && a != -1 {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("eqfor %v", t)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if a == AMEM {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := syslook("memequal", 1)
|
2015-03-08 13:33:49 -04:00
|
|
|
substArgTypes(n, t, t)
|
2015-02-13 14:40:36 -05:00
|
|
|
*needsize = 1
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
sym := typesymprefix(".eq", t)
|
|
|
|
|
n := newname(sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
n.Class = PFUNC
|
2015-02-23 16:07:24 -05:00
|
|
|
ntype := Nod(OTFUNC, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := 0
|
|
|
|
|
for t1 := t.Type; t1 != nil; t1 = t1.Down {
|
2015-02-13 14:40:36 -05:00
|
|
|
n++
|
|
|
|
|
}
|
|
|
|
|
return n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func walkcompare(np **Node, init **NodeList) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// 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.
|
2015-03-02 14:22:05 -05:00
|
|
|
var l *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-02 14:22:05 -05:00
|
|
|
var r *Node
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isinter(n.Left.Type) && !Isinter(n.Right.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
l = n.Left
|
|
|
|
|
r = n.Right
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if !Isinter(n.Left.Type) && Isinter(n.Right.Type) {
|
2015-02-13 14:40:36 -05:00
|
|
|
l = n.Right
|
|
|
|
|
r = n.Left
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l != nil {
|
2015-02-23 16:07:24 -05:00
|
|
|
x := temp(r.Type)
|
|
|
|
|
ok := temp(Types[TBOOL])
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// l.(type(r))
|
2015-02-23 16:07:24 -05:00
|
|
|
a := Nod(ODOTTYPE, l, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
a.Type = r.Type
|
|
|
|
|
|
|
|
|
|
// x, ok := l.(type(r))
|
2015-02-23 16:07:24 -05:00
|
|
|
expr := Nod(OAS2, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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)
|
2015-03-02 20:34:22 -05:00
|
|
|
finishcompare(np, n, r, init)
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Must be comparison of array or struct.
|
|
|
|
|
// Otherwise back end handles it.
|
2015-03-02 20:34:22 -05:00
|
|
|
t := n.Left.Type
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
switch t.Etype {
|
|
|
|
|
default:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
case TARRAY:
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isslice(t) {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TSTRUCT:
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 20:34:22 -05:00
|
|
|
cmpl := n.Left
|
2015-02-13 14:40:36 -05:00
|
|
|
for cmpl != nil && cmpl.Op == OCONVNOP {
|
|
|
|
|
cmpl = cmpl.Left
|
|
|
|
|
}
|
2015-03-02 20:34:22 -05:00
|
|
|
cmpr := n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
for cmpr != nil && cmpr.Op == OCONVNOP {
|
|
|
|
|
cmpr = cmpr.Left
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if !islvalue(cmpl) || !islvalue(cmpr) {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("arguments of comparison must be lvalues - %v %v", cmpl, cmpr)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l = temp(Ptrto(t))
|
2015-03-02 20:34:22 -05:00
|
|
|
a := Nod(OAS, l, Nod(OADDR, cmpl, nil))
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
|
|
|
|
|
2015-03-02 20:34:22 -05:00
|
|
|
andor := OANDAND
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Op == ONE {
|
|
|
|
|
andor = OOROR
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 20:34:22 -05:00
|
|
|
var expr *Node
|
2015-03-01 07:54:01 +00:00
|
|
|
if t.Etype == TARRAY && t.Bound <= 4 && issimple[t.Type.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
// Four or fewer elements of a basic type.
|
|
|
|
|
// Unroll comparisons.
|
2015-02-23 16:07:24 -05:00
|
|
|
var li *Node
|
|
|
|
|
var ri *Node
|
|
|
|
|
for i := 0; int64(i) < t.Bound; i++ {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-02-17 22:13:49 -05:00
|
|
|
expr = Nodbool(n.Op == OEQ)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-02 20:34:22 -05:00
|
|
|
finishcompare(np, n, expr, init)
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if t.Etype == TSTRUCT && countfield(t) <= 4 {
|
|
|
|
|
// Struct of four or fewer fields.
|
|
|
|
|
// Inline comparisons.
|
2015-02-23 16:07:24 -05:00
|
|
|
var li *Node
|
|
|
|
|
var ri *Node
|
|
|
|
|
for t1 := t.Type; t1 != nil; t1 = t1.Down {
|
2015-02-13 14:40:36 -05:00
|
|
|
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 {
|
2015-02-17 22:13:49 -05:00
|
|
|
expr = Nodbool(n.Op == OEQ)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-02 20:34:22 -05:00
|
|
|
finishcompare(np, n, expr, init)
|
|
|
|
|
return
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Chose not to inline. Call equality function directly.
|
2015-03-02 20:34:22 -05:00
|
|
|
var needsize int
|
|
|
|
|
call := Nod(OCALL, eqfor(t, &needsize), nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 20:34:22 -05:00
|
|
|
finishcompare(np, n, r, init)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func finishcompare(np **Node, n, r *Node, init **NodeList) {
|
|
|
|
|
// Using np here to avoid passing &r to typecheck.
|
|
|
|
|
*np = r
|
|
|
|
|
typecheck(np, Erv)
|
|
|
|
|
walkexpr(np, init)
|
|
|
|
|
r = *np
|
2015-02-13 14:40:36 -05:00
|
|
|
if r.Type != n.Type {
|
|
|
|
|
r = Nod(OCONVNOP, r, nil)
|
|
|
|
|
r.Type = n.Type
|
|
|
|
|
r.Typecheck = 1
|
2015-03-02 20:34:22 -05:00
|
|
|
*np = r
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func samecheap(a *Node, b *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
var ar *Node
|
|
|
|
|
var br *Node
|
|
|
|
|
for a != nil && b != nil && a.Op == b.Op {
|
|
|
|
|
switch a.Op {
|
|
|
|
|
default:
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
case ONAME:
|
2015-02-17 22:13:49 -05:00
|
|
|
return a == b
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case ODOT, ODOTPTR:
|
2015-02-13 14:40:36 -05:00
|
|
|
ar = a.Right
|
|
|
|
|
br = b.Right
|
|
|
|
|
if ar.Op != ONAME || br.Op != ONAME || ar.Sym != br.Sym {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OINDEX:
|
|
|
|
|
ar = a.Right
|
|
|
|
|
br = b.Right
|
2015-02-17 22:13:49 -05:00
|
|
|
if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.Xval, br.Val.U.Xval) != 0 {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = a.Left
|
|
|
|
|
b = b.Left
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func walkrotate(np **Node) {
|
2015-03-08 14:16:29 +01:00
|
|
|
if Thearch.Thechar == '7' || Thearch.Thechar == '9' {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value.
|
2015-02-23 16:07:24 -05:00
|
|
|
l := n.Left
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
r := n.Right
|
2015-03-01 07:54:01 +00:00
|
|
|
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] || l.Op == r.Op {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Want same, side effect-free expression on lhs of both shifts.
|
2015-02-17 22:13:49 -05:00
|
|
|
if !samecheap(l.Left, r.Left) {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Constants adding to width?
|
2015-02-23 16:07:24 -05:00
|
|
|
w := int(l.Type.Width * 8)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Smallintconst(l.Right) && Smallintconst(r.Right) {
|
2015-02-23 16:07:24 -05:00
|
|
|
sl := int(Mpgetfix(l.Right.Val.U.Xval))
|
2015-02-13 14:40:36 -05:00
|
|
|
if sl >= 0 {
|
2015-02-23 16:07:24 -05:00
|
|
|
sr := int(Mpgetfix(r.Right.Val.U.Xval))
|
2015-02-13 14:40:36 -05:00
|
|
|
if sr >= 0 && sl+sr == w {
|
2015-03-02 12:35:15 -05:00
|
|
|
// Rewrite left shift half to left rotate.
|
|
|
|
|
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
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31).
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* walkmul rewrites integer multiplication by powers of two as shifts.
|
|
|
|
|
*/
|
|
|
|
|
func walkmul(np **Node, init **NodeList) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-03-01 07:54:01 +00:00
|
|
|
if !Isint[n.Type.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
var nr *Node
|
|
|
|
|
var nl *Node
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
neg := 0
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// x*0 is 0 (and side effects of x).
|
2015-02-23 16:07:24 -05:00
|
|
|
var pow int
|
|
|
|
|
var w int
|
2015-02-13 14:40:36 -05:00
|
|
|
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) {
|
|
|
|
|
// if >= 0, nr is 1<<pow // 1 if nr is negative.
|
|
|
|
|
|
|
|
|
|
// TODO(minux)
|
2015-03-08 14:16:29 +01:00
|
|
|
if Thearch.Thechar == '7' || Thearch.Thechar == '9' {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
if n.Right.Op != OLITERAL {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// nr is a constant.
|
2015-02-23 16:07:24 -05:00
|
|
|
nl := cheapexpr(n.Left, init)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nr := n.Right
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// special cases of mod/div
|
|
|
|
|
// by a constant
|
2015-02-23 16:07:24 -05:00
|
|
|
w := int(nl.Type.Width * 8)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-03-05 13:57:36 -05:00
|
|
|
s := 0 // 1 if nr is negative.
|
|
|
|
|
pow := powtwo(nr) // if >= 0, nr is 1<<pow
|
2015-02-13 14:40:36 -05:00
|
|
|
if pow >= 1000 {
|
|
|
|
|
// negative power of 2
|
|
|
|
|
s = 1
|
|
|
|
|
|
|
|
|
|
pow -= 1000
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if pow+1 >= w {
|
|
|
|
|
// divisor too large.
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if pow < 0 {
|
2015-03-02 12:35:15 -05:00
|
|
|
// try to do division by multiply by (2^w)/d
|
|
|
|
|
// see hacker's delight chapter 10
|
|
|
|
|
// TODO: support 64-bit magic multiply here.
|
|
|
|
|
var m Magic
|
|
|
|
|
m.W = w
|
|
|
|
|
|
2015-03-01 07:54:01 +00:00
|
|
|
if Issigned[nl.Type.Etype] {
|
2015-03-02 12:35:15 -05:00
|
|
|
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 {
|
|
|
|
|
// rewrite as A%B = A - (A/B*B).
|
|
|
|
|
n1 := Nod(ODIV, nl, nr)
|
|
|
|
|
|
|
|
|
|
n2 := Nod(OMUL, n1, nr)
|
|
|
|
|
n = Nod(OSUB, nl, n2)
|
|
|
|
|
goto ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch Simtype[nl.Type.Etype] {
|
|
|
|
|
default:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
// n1 = nl * magic >> w (HMUL)
|
2015-04-01 09:38:44 -07:00
|
|
|
case TUINT8, TUINT16, TUINT32:
|
2015-03-02 12:35:15 -05:00
|
|
|
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.
|
|
|
|
|
var twide *Type
|
|
|
|
|
switch Simtype[nl.Type.Etype] {
|
|
|
|
|
default:
|
|
|
|
|
return
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case TUINT8, TUINT16:
|
2015-03-02 12:35:15 -05:00
|
|
|
twide = Types[TUINT32]
|
|
|
|
|
|
|
|
|
|
case TUINT32:
|
|
|
|
|
twide = Types[TUINT64]
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case TINT8, TINT16:
|
2015-03-02 12:35:15 -05:00
|
|
|
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
|
2015-04-01 09:38:44 -07:00
|
|
|
case TINT8, TINT16, TINT32:
|
2015-03-02 12:35:15 -05:00
|
|
|
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
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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:
|
2015-03-01 07:54:01 +00:00
|
|
|
if Issigned[n.Type.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
nc := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1)
|
2015-02-23 16:07:24 -05:00
|
|
|
n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0.
|
2015-02-13 14:40:36 -05:00
|
|
|
if pow == 1 {
|
|
|
|
|
typecheck(&n1, Erv)
|
|
|
|
|
n1 = cheapexpr(n1, init)
|
|
|
|
|
|
|
|
|
|
// n = (nl+ε)&1 -ε where ε=1 iff nl<0.
|
2015-02-23 16:07:24 -05:00
|
|
|
n2 := Nod(OSUB, nl, n1)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nc := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
Nodconst(nc, nl.Type, 1)
|
2015-02-23 16:07:24 -05:00
|
|
|
n3 := Nod(OAND, n2, nc)
|
2015-02-13 14:40:36 -05:00
|
|
|
n = Nod(OADD, n3, n1)
|
|
|
|
|
} else {
|
|
|
|
|
// n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0.
|
2015-02-23 16:07:24 -05:00
|
|
|
nc := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
Nodconst(nc, nl.Type, (1<<uint(pow))-1)
|
2015-02-23 16:07:24 -05:00
|
|
|
n2 := Nod(OAND, n1, nc) // n2 = 2^pow-1 iff nl<0.
|
2015-02-13 14:40:36 -05:00
|
|
|
typecheck(&n2, Erv)
|
|
|
|
|
n2 = cheapexpr(n2, init)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
n3 := Nod(OADD, nl, n2)
|
|
|
|
|
n4 := Nod(OAND, n3, nc)
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
nc := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
Nodconst(nc, Types[Simtype[TUINT]], int64(w)-1)
|
2015-02-23 16:07:24 -05:00
|
|
|
n1 := Nod(ORSH, nl, nc) // n1 = -1 iff nl < 0.
|
2015-02-13 14:40:36 -05:00
|
|
|
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.
|
2015-02-23 16:07:24 -05:00
|
|
|
nc := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
Nodconst(nc, Types[Simtype[TUINT]], int64(w)-int64(pow))
|
2015-02-23 16:07:24 -05:00
|
|
|
n2 := Nod(ORSH, conv(n1, tounsigned(nl.Type)), nc)
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
nc := Nod(OXXX, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
ret:
|
|
|
|
|
typecheck(&n, Erv)
|
|
|
|
|
walkexpr(&n, init)
|
|
|
|
|
*np = n
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return 1 if integer n must be in range [0, max), 0 otherwise
|
2015-02-17 22:13:49 -05:00
|
|
|
func bounded(n *Node, max int64) bool {
|
2015-03-01 07:54:01 +00:00
|
|
|
if n.Type == nil || !Isint[n.Type.Etype] {
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-03-01 07:54:01 +00:00
|
|
|
sign := Issigned[n.Type.Etype]
|
2015-02-23 16:07:24 -05:00
|
|
|
bits := int32(8 * n.Type.Width)
|
2015-02-13 14:40:36 -05:00
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
if Smallintconst(n) {
|
2015-02-23 16:07:24 -05:00
|
|
|
v := Mpgetfix(n.Val.U.Xval)
|
2015-02-17 22:13:49 -05:00
|
|
|
return 0 <= v && v < max
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
|
case OAND:
|
2015-02-23 16:07:24 -05:00
|
|
|
v := int64(-1)
|
2015-02-17 22:13:49 -05:00
|
|
|
if Smallintconst(n.Left) {
|
2015-02-13 14:40:36 -05:00
|
|
|
v = Mpgetfix(n.Left.Val.U.Xval)
|
2015-02-17 22:13:49 -05:00
|
|
|
} else if Smallintconst(n.Right) {
|
2015-02-13 14:40:36 -05:00
|
|
|
v = Mpgetfix(n.Right.Val.U.Xval)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if 0 <= v && v < max {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OMOD:
|
2015-03-01 07:54:01 +00:00
|
|
|
if !sign && Smallintconst(n.Right) {
|
2015-02-23 16:07:24 -05:00
|
|
|
v := Mpgetfix(n.Right.Val.U.Xval)
|
2015-02-13 14:40:36 -05:00
|
|
|
if 0 <= v && v <= max {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ODIV:
|
2015-03-01 07:54:01 +00:00
|
|
|
if !sign && Smallintconst(n.Right) {
|
2015-02-23 16:07:24 -05:00
|
|
|
v := Mpgetfix(n.Right.Val.U.Xval)
|
2015-02-13 14:40:36 -05:00
|
|
|
for bits > 0 && v >= 2 {
|
|
|
|
|
bits--
|
|
|
|
|
v >>= 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ORSH:
|
2015-03-01 07:54:01 +00:00
|
|
|
if !sign && Smallintconst(n.Right) {
|
2015-02-23 16:07:24 -05:00
|
|
|
v := Mpgetfix(n.Right.Val.U.Xval)
|
2015-02-13 14:40:36 -05:00
|
|
|
if v > int64(bits) {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
bits -= int32(v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-01 07:54:01 +00:00
|
|
|
if !sign && bits <= 62 && 1<<uint(bits) <= max {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func usefield(n *Node) {
|
2015-02-17 22:13:49 -05:00
|
|
|
if obj.Fieldtrack_enabled == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
|
default:
|
|
|
|
|
Fatal("usefield %v", Oconv(int(n.Op), 0))
|
|
|
|
|
|
2015-04-01 09:38:44 -07:00
|
|
|
case ODOT, ODOTPTR:
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
field := n.Paramfld
|
2015-02-13 14:40:36 -05:00
|
|
|
if field == nil {
|
2015-04-17 12:03:22 -04:00
|
|
|
Fatal("usefield %v %v without paramfld", n.Left.Type, n.Right.Sym)
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
2015-03-02 16:03:26 -05:00
|
|
|
if field.Note == nil || !strings.Contains(*field.Note, "go:\"track\"") {
|
2015-02-13 14:40:36 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dedup on list
|
|
|
|
|
if field.Lastfn == Curfn {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
field.Lastfn = Curfn
|
|
|
|
|
field.Outer = n.Left.Type
|
2015-03-01 07:54:01 +00:00
|
|
|
if Isptr[field.Outer.Etype] {
|
2015-02-13 14:40:36 -05:00
|
|
|
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)")
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
l := typ(0)
|
2015-02-13 14:40:36 -05:00
|
|
|
l.Type = field
|
|
|
|
|
l.Down = Curfn.Paramfld
|
|
|
|
|
Curfn.Paramfld = l
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func candiscardlist(l *NodeList) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
for ; l != nil; l = l.Next {
|
2015-02-17 22:13:49 -05:00
|
|
|
if !candiscard(l.N) {
|
|
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
func candiscard(n *Node) bool {
|
2015-02-13 14:40:36 -05:00
|
|
|
if n == nil {
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch n.Op {
|
|
|
|
|
default:
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// 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.
|
2015-04-01 09:38:44 -07:00
|
|
|
case ODIV, OMOD:
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(n.Right, CTINT) && mpcmpfixc(n.Right.Val.U.Xval, 0) != 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(n.Right, CTFLT) && mpcmpfltc(n.Right.Val.U.Fval, 0) != 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Discardable as long as we know it won't fail because of a bad size.
|
2015-04-01 09:38:44 -07:00
|
|
|
case OMAKECHAN, OMAKEMAP:
|
2015-02-17 22:13:49 -05:00
|
|
|
if Isconst(n.Left, CTINT) && mpcmpfixc(n.Left.Val.U.Xval, 0) == 0 {
|
2015-02-13 14:40:36 -05:00
|
|
|
break
|
|
|
|
|
}
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
// Difficult to tell what sizes are okay.
|
|
|
|
|
case OMAKESLICE:
|
2015-02-17 22:13:49 -05:00
|
|
|
return false
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
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
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
2015-02-17 22:13:49 -05:00
|
|
|
return true
|
2015-02-13 14:40:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
2015-02-23 16:07:24 -05:00
|
|
|
n := *np
|
2015-02-13 14:40:36 -05:00
|
|
|
|
|
|
|
|
if n.Ninit != nil {
|
|
|
|
|
walkstmtlist(n.Ninit)
|
|
|
|
|
*init = concat(*init, n.Ninit)
|
|
|
|
|
n.Ninit = nil
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
t := Nod(OTFUNC, nil, nil)
|
|
|
|
|
num := 0
|
2015-03-02 14:22:05 -05:00
|
|
|
var printargs *NodeList
|
2015-02-23 16:07:24 -05:00
|
|
|
var a *Node
|
|
|
|
|
var buf string
|
|
|
|
|
for l := n.List; l != nil; l = l.Next {
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
fn := Nod(ODCLFUNC, nil, nil)
|
2015-02-13 14:40:36 -05:00
|
|
|
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)
|
|
|
|
|
|
2015-02-23 16:07:24 -05:00
|
|
|
oldfn := Curfn
|
2015-02-13 14:40:36 -05:00
|
|
|
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
|
|
|
|
|
}
|