Commit 0250ef91 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: refactor constant rewriting

Extract all rewrite-to-OLITERAL expressions to use a single setconst
helper function.

Does not pass toolstash-check for two reasons:

1) We now consistently clear Left/Right/etc when rewriting Nodes into
OLITERALs, which results in their inlining complexity being correctly
computed. So more functions can now be inlined.

2) We preserve Pos, so PC line tables change somewhat.

Change-Id: I2b5c293bee7c69c2ccd704677f5aba4ec40e3155
Reviewed-on: https://go-review.googlesource.com/103860
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarDaniel Martí <mvdan@mvdan.cc>
parent 3c588b3f
...@@ -6,7 +6,6 @@ package gc ...@@ -6,7 +6,6 @@ package gc
import ( import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/src"
"math/big" "math/big"
"strings" "strings"
) )
...@@ -564,8 +563,8 @@ func overflow(v Val, t *types.Type) bool { ...@@ -564,8 +563,8 @@ func overflow(v Val, t *types.Type) bool {
return false return false
} }
// Only uintptrs may be converted to unsafe.Pointer, which cannot overflow. // Only uintptrs may be converted to pointers, which cannot overflow.
if t.Etype == TUNSAFEPTR { if t.IsPtr() || t.IsUnsafePtr() {
return false return false
} }
...@@ -610,18 +609,6 @@ func Isconst(n *Node, ct Ctype) bool { ...@@ -610,18 +609,6 @@ func Isconst(n *Node, ct Ctype) bool {
return t == ct || (ct == CTINT && t == CTRUNE) return t == ct || (ct == CTINT && t == CTRUNE)
} }
func saveorig(n *Node) *Node {
if n == n.Orig {
// duplicate node for n->orig.
n1 := nod(OLITERAL, nil, nil)
n.Orig = n1
*n1 = *n
}
return n.Orig
}
// if n is constant, rewrite as OLITERAL node. // if n is constant, rewrite as OLITERAL node.
func evconst(n *Node) { func evconst(n *Node) {
// pick off just the opcodes that can be // pick off just the opcodes that can be
...@@ -745,20 +732,13 @@ func evconst(n *Node) { ...@@ -745,20 +732,13 @@ func evconst(n *Node) {
nr := n.Right nr := n.Right
var rv Val var rv Val
var lno src.XPos
var wr types.EType var wr types.EType
var ctype uint32 var ctype uint32
var v Val var v Val
var norig *Node
var nn *Node
if nr == nil { if nr == nil {
// copy numeric value to avoid modifying // copy numeric value to avoid modifying
// nl, in case someone still refers to it (e.g. iota). // nl, in case someone still refers to it (e.g. iota).
v = nl.Val() v = copyval(nl.Val())
if wl == TIDEAL {
v = copyval(v)
}
// rune values are int values for the purpose of constant folding. // rune values are int values for the purpose of constant folding.
ctype = uint32(v.Ctype()) ctype = uint32(v.Ctype())
...@@ -900,12 +880,7 @@ func evconst(n *Node) { ...@@ -900,12 +880,7 @@ func evconst(n *Node) {
// copy numeric value to avoid modifying // copy numeric value to avoid modifying
// n->left, in case someone still refers to it (e.g. iota). // n->left, in case someone still refers to it (e.g. iota).
v = nl.Val() v = copyval(nl.Val())
if wl == TIDEAL {
v = copyval(v)
}
rv = nr.Val() rv = nr.Val()
// convert to common ideal // convert to common ideal
...@@ -1202,41 +1177,15 @@ func evconst(n *Node) { ...@@ -1202,41 +1177,15 @@ func evconst(n *Node) {
} }
ret: ret:
norig = saveorig(n) setconst(n, v)
*n = *nl
// restore value of n->orig.
n.Orig = norig
n.SetVal(v)
// check range.
lno = setlineno(n)
overflow(v, n.Type)
lineno = lno
// truncate precision for non-ideal float.
if v.Ctype() == CTFLT && n.Type.Etype != TIDEAL {
n.SetVal(Val{truncfltlit(v.U.(*Mpflt), n.Type)})
}
return return
settrue: settrue:
nn = nodbool(true) setconst(n, Val{true})
nn.Orig = saveorig(n)
if !iscmp[n.Op] {
nn.Type = nl.Type
}
*n = *nn
return return
setfalse: setfalse:
nn = nodbool(false) setconst(n, Val{false})
nn.Orig = saveorig(n)
if !iscmp[n.Op] {
nn.Type = nl.Type
}
*n = *nn
return return
illegal: illegal:
...@@ -1246,6 +1195,42 @@ illegal: ...@@ -1246,6 +1195,42 @@ illegal:
} }
} }
// setconst rewrites n as an OLITERAL with value v.
func setconst(n *Node, v Val) {
// Ensure n.Orig still points to a semantically-equivalent
// expression after we rewrite n into a constant.
if n.Orig == n {
var ncopy Node
n.Orig = &ncopy
ncopy = *n
}
*n = Node{
Op: OLITERAL,
Pos: n.Pos,
Orig: n.Orig,
Type: n.Type,
Xoffset: BADWIDTH,
}
n.SetVal(v)
// Check range.
lno := setlineno(n)
overflow(v, n.Type)
lineno = lno
// Truncate precision for non-ideal float.
if v.Ctype() == CTFLT && n.Type.Etype != TIDEAL {
n.SetVal(Val{truncfltlit(v.U.(*Mpflt), n.Type)})
}
}
func setintconst(n *Node, v int64) {
u := new(Mpint)
u.SetInt64(v)
setconst(n, Val{u})
}
// nodlit returns a new untyped constant with value v. // nodlit returns a new untyped constant with value v.
func nodlit(v Val) *Node { func nodlit(v Val) *Node {
n := nod(OLITERAL, nil, nil) n := nod(OLITERAL, nil, nil)
...@@ -1270,24 +1255,6 @@ func nodlit(v Val) *Node { ...@@ -1270,24 +1255,6 @@ func nodlit(v Val) *Node {
return n return n
} }
func nodcplxlit(r Val, i Val) *Node {
r = toflt(r)
i = toflt(i)
c := new(Mpcplx)
n := nod(OLITERAL, nil, nil)
n.Type = types.Types[TIDEAL]
n.SetVal(Val{c})
if r.Ctype() != CTFLT || i.Ctype() != CTFLT {
Fatalf("nodcplxlit ctype %d/%d", r.Ctype(), i.Ctype())
}
c.Real.Set(r.U.(*Mpflt))
c.Imag.Set(i.U.(*Mpflt))
return n
}
// idealkind returns a constant kind like consttype // idealkind returns a constant kind like consttype
// but for an arbitrary "ideal" (untyped constant) expression. // but for an arbitrary "ideal" (untyped constant) expression.
func idealkind(n *Node) Ctype { func idealkind(n *Node) Ctype {
......
...@@ -790,7 +790,8 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) { ...@@ -790,7 +790,8 @@ func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
} }
var v Node var v Node
nodconst(&v, types.Types[TINT], t.NumElem()) v.Type = types.Types[TINT]
setintconst(&v, t.NumElem())
nam.Xoffset += int64(array_array) nam.Xoffset += int64(array_array)
gdata(&nam, nod(OADDR, vstat, nil), Widthptr) gdata(&nam, nod(OADDR, vstat, nil), Widthptr)
......
...@@ -364,15 +364,6 @@ func nodSym(op Op, left *Node, sym *types.Sym) *Node { ...@@ -364,15 +364,6 @@ func nodSym(op Op, left *Node, sym *types.Sym) *Node {
return n return n
} }
func saveorignode(n *Node) {
if n.Orig != nil {
return
}
norig := nod(n.Op, nil, nil)
*norig = *n
n.Orig = norig
}
// methcmp sorts methods by name with exported methods first, // methcmp sorts methods by name with exported methods first,
// and then non-exported methods by their package path. // and then non-exported methods by their package path.
type methcmp []*types.Field type methcmp []*types.Field
...@@ -424,19 +415,6 @@ func nodfltconst(v *Mpflt) *Node { ...@@ -424,19 +415,6 @@ func nodfltconst(v *Mpflt) *Node {
return nodlit(Val{u}) return nodlit(Val{u})
} }
func nodconst(n *Node, t *types.Type, v int64) {
*n = Node{}
n.Op = OLITERAL
n.SetAddable(true)
n.SetVal(Val{new(Mpint)})
n.Val().U.(*Mpint).SetInt64(v)
n.Type = t
if t.IsFloat() {
Fatalf("nodconst: bad type %v", t)
}
}
func nodnil() *Node { func nodnil() *Node {
return nodlit(Val{new(NilVal)}) return nodlit(Val{new(NilVal)})
} }
......
...@@ -1294,12 +1294,10 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1294,12 +1294,10 @@ func typecheck1(n *Node, top int) *Node {
n.Type = nil n.Type = nil
return n return n
} }
n.Type = types.Types[TUINTPTR]
// any side effects disappear; ignore init // any side effects disappear; ignore init
var r Node setintconst(n, evalunsafe(n))
nodconst(&r, types.Types[TUINTPTR], evalunsafe(n))
r.Orig = n
n = &r
case OCAP, OLEN: case OCAP, OLEN:
ok |= Erv ok |= Erv
...@@ -1330,7 +1328,9 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1330,7 +1328,9 @@ func typecheck1(n *Node, top int) *Node {
return n return n
} }
// result might be constant n.Type = types.Types[TINT]
// Result might be constant.
var res int64 = -1 // valid if >= 0 var res int64 = -1 // valid if >= 0
switch t.Etype { switch t.Etype {
case TSTRING: case TSTRING:
...@@ -1344,14 +1344,9 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1344,14 +1344,9 @@ func typecheck1(n *Node, top int) *Node {
} }
} }
if res >= 0 { if res >= 0 {
var r Node setintconst(n, res)
nodconst(&r, types.Types[TINT], res)
r.Orig = n
n = &r
} }
n.Type = types.Types[TINT]
case OREAL, OIMAG: case OREAL, OIMAG:
ok |= Erv ok |= Erv
if !onearg(n, "%v", n.Op) { if !onearg(n, "%v", n.Op) {
...@@ -1367,11 +1362,21 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1367,11 +1362,21 @@ func typecheck1(n *Node, top int) *Node {
return n return n
} }
if t.Etype != TIDEAL && !t.IsComplex() { // Determine result type.
et := t.Etype
switch et {
case TIDEAL:
// result is ideal
case TCOMPLEX64:
et = TFLOAT32
case TCOMPLEX128:
et = TFLOAT64
default:
yyerror("invalid argument %L for %v", l, n.Op) yyerror("invalid argument %L for %v", l, n.Op)
n.Type = nil n.Type = nil
return n return n
} }
n.Type = types.Types[et]
// if the argument is a constant, the result is a constant // if the argument is a constant, the result is a constant
// (any untyped numeric constant can be represented as a // (any untyped numeric constant can be represented as a
...@@ -1400,24 +1405,8 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1400,24 +1405,8 @@ func typecheck1(n *Node, top int) *Node {
} }
re = im re = im
} }
orig := n setconst(n, Val{re})
n = nodfltconst(re)
n.Orig = orig
}
// determine result type
et := t.Etype
switch et {
case TIDEAL:
// result is ideal
case TCOMPLEX64:
et = TFLOAT32
case TCOMPLEX128:
et = TFLOAT64
default:
Fatalf("unexpected Etype: %v\n", et)
} }
n.Type = types.Types[et]
case OCOMPLEX: case OCOMPLEX:
ok |= Erv ok |= Erv
...@@ -1489,17 +1478,16 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1489,17 +1478,16 @@ func typecheck1(n *Node, top int) *Node {
case TFLOAT64: case TFLOAT64:
t = types.Types[TCOMPLEX128] t = types.Types[TCOMPLEX128]
} }
n.Type = t
if l.Op == OLITERAL && r.Op == OLITERAL { if l.Op == OLITERAL && r.Op == OLITERAL {
// make it a complex literal // make it a complex literal
r = nodcplxlit(l.Val(), r.Val()) c := new(Mpcplx)
c.Real.Set(toflt(l.Val()).U.(*Mpflt))
r.Orig = n c.Imag.Set(toflt(r.Val()).U.(*Mpflt))
n = r setconst(n, Val{c})
} }
n.Type = t
case OCLOSE: case OCLOSE:
if !onearg(n, "%v", n.Op) { if !onearg(n, "%v", n.Op) {
n.Type = nil n.Type = nil
...@@ -1701,7 +1689,6 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1701,7 +1689,6 @@ func typecheck1(n *Node, top int) *Node {
case OCONV: case OCONV:
ok |= Erv ok |= Erv
saveorignode(n)
checkwidth(n.Type) // ensure width is calculated for backend checkwidth(n.Type) // ensure width is calculated for backend
n.Left = typecheck(n.Left, Erv) n.Left = typecheck(n.Left, Erv)
n.Left = convlit1(n.Left, n.Type, true, noReuse) n.Left = convlit1(n.Left, n.Type, true, noReuse)
...@@ -1717,19 +1704,16 @@ func typecheck1(n *Node, top int) *Node { ...@@ -1717,19 +1704,16 @@ func typecheck1(n *Node, top int) *Node {
yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why) yyerror("cannot convert %L to type %v%s", n.Left, n.Type, why)
n.SetDiag(true) n.SetDiag(true)
} }
n.Op = OCONV n.Op = OCONV
n.Type = nil
return n
} }
switch n.Op { switch n.Op {
case OCONVNOP: case OCONVNOP:
if n.Left.Op == OLITERAL { if n.Left.Op == OLITERAL {
r := nod(OXXX, nil, nil)
n.Op = OCONV n.Op = OCONV
n.Orig = r setconst(n, n.Left.Val())
*r = *n
n.Op = OLITERAL
n.SetVal(n.Left.Val())
} else if t.Etype == n.Type.Etype { } else if t.Etype == n.Type.Etype {
switch t.Etype { switch t.Etype {
case TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128: case TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128:
......
...@@ -548,7 +548,7 @@ opswitch: ...@@ -548,7 +548,7 @@ opswitch:
} }
if t.IsArray() { if t.IsArray() {
safeexpr(n.Left, init) safeexpr(n.Left, init)
nodconst(n, n.Type, t.NumElem()) setintconst(n, t.NumElem())
n.SetTypecheck(1) n.SetTypecheck(1)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment