go.tools/ssa: s/Literal/Const/g, s/Constant/NamedConst/g
(Motivation: "Literal" is a syntactic property, not a semantic one.) Also: delete a "TODO: opt" that the lifting pass already does for us. R=gri CC=golang-dev https://golang.org/cl/11351043
This commit is contained in:
parent
80ec883f7b
commit
732dbe9ff8
|
@ -55,10 +55,10 @@ var (
|
|||
tEface = new(types.Interface)
|
||||
|
||||
// SSA Value constants.
|
||||
vZero = intLiteral(0)
|
||||
vOne = intLiteral(1)
|
||||
vTrue = NewLiteral(exact.MakeBool(true), tBool)
|
||||
vFalse = NewLiteral(exact.MakeBool(false), tBool)
|
||||
vZero = intConst(0)
|
||||
vOne = intConst(1)
|
||||
vTrue = NewConst(exact.MakeBool(true), tBool)
|
||||
vFalse = NewConst(exact.MakeBool(false), tBool)
|
||||
)
|
||||
|
||||
// builder holds state associated with the package currently being built.
|
||||
|
@ -126,7 +126,7 @@ func (b *builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) {
|
|||
}
|
||||
|
||||
switch cond := b.expr(fn, e).(type) {
|
||||
case *Literal:
|
||||
case *Const:
|
||||
// Dispatch constant conditions statically.
|
||||
if exact.BoolVal(cond.Value) {
|
||||
emitJump(fn, t)
|
||||
|
@ -313,7 +313,7 @@ func (b *builder) builtin(fn *Function, name string, args []ast.Expr, typ types.
|
|||
t := deref(fn.Pkg.typeOf(args[0])).Underlying()
|
||||
if at, ok := t.(*types.Array); ok {
|
||||
b.expr(fn, args[0]) // for effects only
|
||||
return intLiteral(at.Len())
|
||||
return intConst(at.Len())
|
||||
}
|
||||
// Otherwise treat as normal.
|
||||
|
||||
|
@ -547,11 +547,11 @@ func (b *builder) exprInPlace(fn *Function, loc lvalue, e ast.Expr) {
|
|||
//
|
||||
func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
||||
if v := fn.Pkg.info.ValueOf(e); v != nil {
|
||||
lit := NewLiteral(v, fn.Pkg.typeOf(e))
|
||||
c := NewConst(v, fn.Pkg.typeOf(e))
|
||||
if id, ok := unparen(e).(*ast.Ident); ok {
|
||||
emitDebugRef(fn, id, lit)
|
||||
emitDebugRef(fn, id, c)
|
||||
}
|
||||
return lit
|
||||
return c
|
||||
}
|
||||
|
||||
switch e := e.(type) {
|
||||
|
@ -933,7 +933,7 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx
|
|||
st := sig.Params().At(np).Type().(*types.Slice)
|
||||
vt := st.Elem()
|
||||
if len(varargs) == 0 {
|
||||
args = append(args, nilLiteral(st))
|
||||
args = append(args, nilConst(st))
|
||||
} else {
|
||||
// Replace a suffix of args with a slice containing it.
|
||||
at := types.NewArray(vt, int64(len(varargs)))
|
||||
|
@ -942,7 +942,7 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx
|
|||
for i, arg := range varargs {
|
||||
iaddr := &IndexAddr{
|
||||
X: a,
|
||||
Index: intLiteral(int64(i)),
|
||||
Index: intConst(int64(i)),
|
||||
}
|
||||
iaddr.setType(types.NewPointer(vt))
|
||||
fn.emit(iaddr)
|
||||
|
@ -1120,8 +1120,6 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
|
|||
for _, id := range spec.Names {
|
||||
if !isBlankIdent(id) {
|
||||
lhs := fn.addLocalForIdent(id)
|
||||
// TODO(adonovan): opt: use zero literal in
|
||||
// lieu of load, if type permits.
|
||||
if fn.debugInfo() {
|
||||
emitDebugRef(fn, id, emitLoad(fn, lhs))
|
||||
}
|
||||
|
@ -1201,7 +1199,7 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
|
|||
var i int64 = -1
|
||||
for _, e := range elts {
|
||||
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
||||
i = b.expr(fn, kv.Key).(*Literal).Int64()
|
||||
i = b.expr(fn, kv.Key).(*Const).Int64()
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
|
@ -1258,17 +1256,17 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ
|
|||
array = addr
|
||||
}
|
||||
|
||||
var idx *Literal
|
||||
var idx *Const
|
||||
for _, e := range e.Elts {
|
||||
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
||||
idx = b.expr(fn, kv.Key).(*Literal)
|
||||
idx = b.expr(fn, kv.Key).(*Const)
|
||||
e = kv.Value
|
||||
} else {
|
||||
var idxval int64
|
||||
if idx != nil {
|
||||
idxval = idx.Int64() + 1
|
||||
}
|
||||
idx = intLiteral(idxval)
|
||||
idx = intConst(idxval)
|
||||
}
|
||||
iaddr := &IndexAddr{
|
||||
X: array,
|
||||
|
@ -1286,7 +1284,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ
|
|||
}
|
||||
|
||||
case *types.Map:
|
||||
m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts)))}
|
||||
m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))}
|
||||
m.setPos(e.Lbrace)
|
||||
m.setType(typ)
|
||||
emitStore(fn, addr, fn.emit(m))
|
||||
|
@ -1474,7 +1472,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl
|
|||
casetype = fn.Pkg.typeOf(cond)
|
||||
var condv Value
|
||||
if casetype == tUntypedNil {
|
||||
condv = emitCompare(fn, token.EQL, x, nilLiteral(x.Type()), token.NoPos)
|
||||
condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
|
||||
} else {
|
||||
yok := emitTypeTest(fn, x, casetype, token.NoPos)
|
||||
ti = emitExtract(fn, yok, 0, casetype)
|
||||
|
@ -1616,7 +1614,7 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
|
|||
}
|
||||
body := fn.newBasicBlock("select.body")
|
||||
next := fn.newBasicBlock("select.next")
|
||||
emitIf(fn, emitCompare(fn, token.EQL, idx, intLiteral(int64(state)), token.NoPos), body, next)
|
||||
emitIf(fn, emitCompare(fn, token.EQL, idx, intConst(int64(state)), token.NoPos), body, next)
|
||||
fn.currentBlock = body
|
||||
fn.targets = &targets{
|
||||
tail: fn.targets,
|
||||
|
@ -1741,7 +1739,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value
|
|||
// data dependence upon x, permitting later dead-code
|
||||
// elimination if x is pure, static unrolling, etc.
|
||||
// Ranging over a nil *array may have >0 iterations.
|
||||
length = intLiteral(arr.Len())
|
||||
length = intConst(arr.Len())
|
||||
} else {
|
||||
// length = len(x).
|
||||
var c Call
|
||||
|
@ -1752,7 +1750,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type) (k, v Value
|
|||
}
|
||||
|
||||
index := fn.addLocal(tInt, token.NoPos)
|
||||
emitStore(fn, index, intLiteral(-1))
|
||||
emitStore(fn, index, intConst(-1))
|
||||
|
||||
loop = fn.newBasicBlock("rangeindex.loop")
|
||||
emitJump(fn, loop)
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
package ssa
|
||||
|
||||
// This file defines the Const SSA value type.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"strconv"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// NewConst returns a new constant of the specified value and type.
|
||||
// val must be valid according to the specification of Const.Value.
|
||||
//
|
||||
func NewConst(val exact.Value, typ types.Type) *Const {
|
||||
return &Const{typ, val}
|
||||
}
|
||||
|
||||
// intConst returns an untyped integer constant that evaluates to i.
|
||||
func intConst(i int64) *Const {
|
||||
return NewConst(exact.MakeInt64(i), types.Typ[types.UntypedInt])
|
||||
}
|
||||
|
||||
// nilConst returns a nil constant of the specified type, which may
|
||||
// be any reference type, including interfaces.
|
||||
//
|
||||
func nilConst(typ types.Type) *Const {
|
||||
return NewConst(exact.MakeNil(), typ)
|
||||
}
|
||||
|
||||
// zeroConst returns a new "zero" constant of the specified type,
|
||||
// which must not be an array or struct type: the zero values of
|
||||
// aggregates are well-defined but cannot be represented by Const.
|
||||
//
|
||||
func zeroConst(t types.Type) *Const {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
switch {
|
||||
case t.Info()&types.IsBoolean != 0:
|
||||
return NewConst(exact.MakeBool(false), t)
|
||||
case t.Info()&types.IsNumeric != 0:
|
||||
return NewConst(exact.MakeInt64(0), t)
|
||||
case t.Info()&types.IsString != 0:
|
||||
return NewConst(exact.MakeString(""), t)
|
||||
case t.Kind() == types.UnsafePointer:
|
||||
fallthrough
|
||||
case t.Kind() == types.UntypedNil:
|
||||
return nilConst(t)
|
||||
default:
|
||||
panic(fmt.Sprint("zeroConst for unexpected type:", t))
|
||||
}
|
||||
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
|
||||
return nilConst(t)
|
||||
case *types.Named:
|
||||
return NewConst(zeroConst(t.Underlying()).Value, t)
|
||||
case *types.Array, *types.Struct, *types.Tuple:
|
||||
panic(fmt.Sprint("zeroConst applied to aggregate:", t))
|
||||
}
|
||||
panic(fmt.Sprint("zeroConst: unexpected ", t))
|
||||
}
|
||||
|
||||
func (c *Const) Name() string {
|
||||
var s string
|
||||
if c.Value.Kind() == exact.String {
|
||||
s = exact.StringVal(c.Value)
|
||||
const max = 20
|
||||
if len(s) > max {
|
||||
s = s[:max-3] + "..." // abbreviate
|
||||
}
|
||||
s = strconv.Quote(s)
|
||||
} else {
|
||||
s = c.Value.String()
|
||||
}
|
||||
return s + ":" + c.typ.String()
|
||||
}
|
||||
|
||||
func (c *Const) Type() types.Type {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c *Const) Referrers() *[]Instruction {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Const) Pos() token.Pos {
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
// IsNil returns true if this constant represents a typed or untyped nil value.
|
||||
func (c *Const) IsNil() bool {
|
||||
return c.Value.Kind() == exact.Nil
|
||||
}
|
||||
|
||||
// Int64 returns the numeric value of this constant truncated to fit
|
||||
// a signed 64-bit integer.
|
||||
//
|
||||
func (c *Const) Int64() int64 {
|
||||
switch x := c.Value; x.Kind() {
|
||||
case exact.Int:
|
||||
if i, ok := exact.Int64Val(x); ok {
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
case exact.Float:
|
||||
f, _ := exact.Float64Val(x)
|
||||
return int64(f)
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
|
||||
}
|
||||
|
||||
// Uint64 returns the numeric value of this constant truncated to fit
|
||||
// an unsigned 64-bit integer.
|
||||
//
|
||||
func (c *Const) Uint64() uint64 {
|
||||
switch x := c.Value; x.Kind() {
|
||||
case exact.Int:
|
||||
if u, ok := exact.Uint64Val(x); ok {
|
||||
return u
|
||||
}
|
||||
return 0
|
||||
case exact.Float:
|
||||
f, _ := exact.Float64Val(x)
|
||||
return uint64(f)
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
|
||||
}
|
||||
|
||||
// Float64 returns the numeric value of this constant truncated to fit
|
||||
// a float64.
|
||||
//
|
||||
func (c *Const) Float64() float64 {
|
||||
f, _ := exact.Float64Val(c.Value)
|
||||
return f
|
||||
}
|
||||
|
||||
// Complex128 returns the complex value of this constant truncated to
|
||||
// fit a complex128.
|
||||
//
|
||||
func (c *Const) Complex128() complex128 {
|
||||
re, _ := exact.Float64Val(exact.Real(c.Value))
|
||||
im, _ := exact.Float64Val(exact.Imag(c.Value))
|
||||
return complex(re, im)
|
||||
}
|
|
@ -89,9 +89,9 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
|
|||
pkg.Members[name] = &Type{object: obj}
|
||||
|
||||
case *types.Const:
|
||||
c := &Constant{
|
||||
c := &NamedConst{
|
||||
object: obj,
|
||||
Value: NewLiteral(obj.Val(), obj.Type()),
|
||||
Value: NewConst(obj.Val(), obj.Type()),
|
||||
}
|
||||
pkg.values[obj] = c.Value
|
||||
pkg.Members[name] = c
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
// *Capture ✔
|
||||
// *ChangeInterface ✔ ✔
|
||||
// *ChangeType ✔ ✔
|
||||
// *Constant ✔ (const)
|
||||
// *Const ✔
|
||||
// *Convert ✔ ✔
|
||||
// *Defer ✔
|
||||
// *Extract ✔ ✔
|
||||
|
@ -70,7 +70,6 @@
|
|||
// *Index ✔ ✔
|
||||
// *IndexAddr ✔ ✔
|
||||
// *Jump ✔
|
||||
// *Literal ✔
|
||||
// *Lookup ✔ ✔
|
||||
// *MakeChan ✔ ✔
|
||||
// *MakeClosure ✔ ✔
|
||||
|
@ -78,6 +77,7 @@
|
|||
// *MakeMap ✔ ✔
|
||||
// *MakeSlice ✔ ✔
|
||||
// *MapUpdate ✔
|
||||
// *NamedConst ✔ (const)
|
||||
// *Next ✔ ✔
|
||||
// *Panic ✔
|
||||
// *Parameter ✔
|
||||
|
|
16
ssa/emit.go
16
ssa/emit.go
|
@ -110,9 +110,9 @@ func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
|
|||
y = emitConv(f, y, x.Type())
|
||||
} else if _, ok := yt.(*types.Interface); ok {
|
||||
x = emitConv(f, x, y.Type())
|
||||
} else if _, ok := x.(*Literal); ok {
|
||||
} else if _, ok := x.(*Const); ok {
|
||||
x = emitConv(f, x, y.Type())
|
||||
} else if _, ok := y.(*Literal); ok {
|
||||
} else if _, ok := y.(*Const); ok {
|
||||
y = emitConv(f, y, x.Type())
|
||||
} else {
|
||||
// other cases, e.g. channels. No-op.
|
||||
|
@ -188,9 +188,9 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
|||
return emitTypeAssert(f, val, typ, token.NoPos)
|
||||
}
|
||||
|
||||
// Untyped nil literal? Return interface-typed nil literal.
|
||||
// Untyped nil constant? Return interface-typed nil constant.
|
||||
if ut_src == tUntypedNil {
|
||||
return nilLiteral(typ)
|
||||
return nilConst(typ)
|
||||
}
|
||||
|
||||
// Convert (non-nil) "untyped" literals to their default type.
|
||||
|
@ -203,13 +203,13 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
|
|||
return f.emit(mi)
|
||||
}
|
||||
|
||||
// Conversion of a literal to a non-interface type results in
|
||||
// a new literal of the destination type and (initially) the
|
||||
// Conversion of a constant to a non-interface type results in
|
||||
// a new constant of the destination type and (initially) the
|
||||
// same abstract value. We don't compute the representation
|
||||
// change yet; this defers the point at which the number of
|
||||
// possible representations explodes.
|
||||
if l, ok := val.(*Literal); ok {
|
||||
return NewLiteral(l.Value, typ)
|
||||
if c, ok := val.(*Const); ok {
|
||||
return NewConst(c.Value, typ)
|
||||
}
|
||||
|
||||
// A representation-changing conversion.
|
||||
|
|
|
@ -110,8 +110,8 @@ func (fr *frame) get(key ssa.Value) value {
|
|||
return nil
|
||||
case *ssa.Function, *ssa.Builtin:
|
||||
return key
|
||||
case *ssa.Literal:
|
||||
return literalValue(key)
|
||||
case *ssa.Const:
|
||||
return constValue(key)
|
||||
case *ssa.Global:
|
||||
if r, ok := fr.i.globals[key]; ok {
|
||||
return r
|
||||
|
|
|
@ -20,60 +20,60 @@ type targetPanic struct {
|
|||
// If the target program calls exit, the interpreter panics with this type.
|
||||
type exitPanic int
|
||||
|
||||
// literalValue returns the value of the literal with the
|
||||
// dynamic type tag appropriate for l.Type().
|
||||
func literalValue(l *ssa.Literal) value {
|
||||
if l.IsNil() {
|
||||
return zero(l.Type()) // typed nil
|
||||
// constValue returns the value of the constant with the
|
||||
// dynamic type tag appropriate for c.Type().
|
||||
func constValue(c *ssa.Const) value {
|
||||
if c.IsNil() {
|
||||
return zero(c.Type()) // typed nil
|
||||
}
|
||||
|
||||
// By destination type:
|
||||
switch t := l.Type().Underlying().(type) {
|
||||
switch t := c.Type().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
// TODO(adonovan): eliminate untyped literals from SSA form.
|
||||
// TODO(adonovan): eliminate untyped constants from SSA form.
|
||||
switch t.Kind() {
|
||||
case types.Bool, types.UntypedBool:
|
||||
return exact.BoolVal(l.Value)
|
||||
return exact.BoolVal(c.Value)
|
||||
case types.Int, types.UntypedInt:
|
||||
// Assume sizeof(int) is same on host and target.
|
||||
return int(l.Int64())
|
||||
return int(c.Int64())
|
||||
case types.Int8:
|
||||
return int8(l.Int64())
|
||||
return int8(c.Int64())
|
||||
case types.Int16:
|
||||
return int16(l.Int64())
|
||||
return int16(c.Int64())
|
||||
case types.Int32, types.UntypedRune:
|
||||
return int32(l.Int64())
|
||||
return int32(c.Int64())
|
||||
case types.Int64:
|
||||
return l.Int64()
|
||||
return c.Int64()
|
||||
case types.Uint:
|
||||
// Assume sizeof(uint) is same on host and target.
|
||||
return uint(l.Uint64())
|
||||
return uint(c.Uint64())
|
||||
case types.Uint8:
|
||||
return uint8(l.Uint64())
|
||||
return uint8(c.Uint64())
|
||||
case types.Uint16:
|
||||
return uint16(l.Uint64())
|
||||
return uint16(c.Uint64())
|
||||
case types.Uint32:
|
||||
return uint32(l.Uint64())
|
||||
return uint32(c.Uint64())
|
||||
case types.Uint64:
|
||||
return l.Uint64()
|
||||
return c.Uint64()
|
||||
case types.Uintptr:
|
||||
// Assume sizeof(uintptr) is same on host and target.
|
||||
return uintptr(l.Uint64())
|
||||
return uintptr(c.Uint64())
|
||||
case types.Float32:
|
||||
return float32(l.Float64())
|
||||
return float32(c.Float64())
|
||||
case types.Float64, types.UntypedFloat:
|
||||
return l.Float64()
|
||||
return c.Float64()
|
||||
case types.Complex64:
|
||||
return complex64(l.Complex128())
|
||||
return complex64(c.Complex128())
|
||||
case types.Complex128, types.UntypedComplex:
|
||||
return l.Complex128()
|
||||
return c.Complex128()
|
||||
case types.String, types.UntypedString:
|
||||
if l.Value.Kind() == exact.String {
|
||||
return exact.StringVal(l.Value)
|
||||
if c.Value.Kind() == exact.String {
|
||||
return exact.StringVal(c.Value)
|
||||
}
|
||||
return string(rune(l.Int64()))
|
||||
return string(rune(c.Int64()))
|
||||
case types.UnsafePointer:
|
||||
panic("unsafe.Pointer literal") // not possible
|
||||
panic("unsafe.Pointer constant") // not possible
|
||||
case types.UntypedNil:
|
||||
// nil was handled above.
|
||||
}
|
||||
|
@ -84,13 +84,13 @@ func literalValue(l *ssa.Literal) value {
|
|||
switch et.Kind() {
|
||||
case types.Byte: // string -> []byte
|
||||
var v []value
|
||||
for _, b := range []byte(exact.StringVal(l.Value)) {
|
||||
for _, b := range []byte(exact.StringVal(c.Value)) {
|
||||
v = append(v, b)
|
||||
}
|
||||
return v
|
||||
case types.Rune: // string -> []rune
|
||||
var v []value
|
||||
for _, r := range []rune(exact.StringVal(l.Value)) {
|
||||
for _, r := range []rune(exact.StringVal(c.Value)) {
|
||||
v = append(v, r)
|
||||
}
|
||||
return v
|
||||
|
@ -98,7 +98,7 @@ func literalValue(l *ssa.Literal) value {
|
|||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("literalValue: Value.(type)=%T Type()=%s", l.Value, l.Type()))
|
||||
panic(fmt.Sprintf("constValue: Value.(type)=%T Type()=%s", c.Value, c.Type()))
|
||||
}
|
||||
|
||||
// asInt converts x, which must be an integer, to an int suitable for
|
||||
|
@ -161,7 +161,7 @@ func zero(t types.Type) value {
|
|||
if t.Info()&types.IsUntyped != 0 {
|
||||
// TODO(adonovan): make it an invariant that
|
||||
// this is unreachable. Currently some
|
||||
// literals have 'untyped' types when they
|
||||
// constants have 'untyped' types when they
|
||||
// should be defaulted by the typechecker.
|
||||
t = ssa.DefaultType(t).(*types.Basic)
|
||||
}
|
||||
|
|
12
ssa/lift.go
12
ssa/lift.go
|
@ -115,7 +115,7 @@ func lift(fn *Function) {
|
|||
// buildDomTree. For example:
|
||||
//
|
||||
// - Alloc never loaded? Eliminate.
|
||||
// - Alloc never stored? Replace all loads with a zero literal.
|
||||
// - Alloc never stored? Replace all loads with a zero constant.
|
||||
// - Alloc stored once? Replace loads with dominating store;
|
||||
// don't forget that an Alloc is itself an effective store
|
||||
// of zero.
|
||||
|
@ -187,8 +187,8 @@ func lift(fn *Function) {
|
|||
|
||||
// renaming maps an alloc (keyed by index) to its replacement
|
||||
// value. Initially the renaming contains nil, signifying the
|
||||
// zero literal of the appropriate type; we construct the
|
||||
// Literal lazily at most once on each path through the domtree.
|
||||
// zero constant of the appropriate type; we construct the
|
||||
// Const lazily at most once on each path through the domtree.
|
||||
// TODO(adonovan): opt: cache per-function not per subtree.
|
||||
renaming := make([]Value, numAllocs)
|
||||
|
||||
|
@ -304,8 +304,8 @@ type newPhiMap map[*BasicBlock][]newPhi
|
|||
//
|
||||
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
|
||||
// Don't lift aggregates into registers, because we don't have
|
||||
// a way to express their zero-literals.
|
||||
// TODO(adonovan): define zero-literals for aggregates, or
|
||||
// a way to express their zero-constants.
|
||||
// TODO(adonovan): define zero-constants for aggregates, or
|
||||
// add a separate SRA pass. Lifting aggregates is an
|
||||
// important optimisation for pointer analysis because the
|
||||
// extra indirection really hurts precision under Das's
|
||||
|
@ -426,7 +426,7 @@ func replaceAll(x, y Value) {
|
|||
func renamed(renaming []Value, alloc *Alloc) Value {
|
||||
v := renaming[alloc.index]
|
||||
if v == nil {
|
||||
v = zeroLiteral(deref(alloc.Type()))
|
||||
v = zeroConst(deref(alloc.Type()))
|
||||
renaming[alloc.index] = v
|
||||
}
|
||||
return v
|
||||
|
|
145
ssa/literal.go
145
ssa/literal.go
|
@ -1,145 +0,0 @@
|
|||
package ssa
|
||||
|
||||
// This file defines the Literal SSA value type.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"strconv"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// NewLiteral returns a new literal of the specified value and type.
|
||||
// val must be valid according to the specification of Literal.Value.
|
||||
//
|
||||
func NewLiteral(val exact.Value, typ types.Type) *Literal {
|
||||
return &Literal{typ, val}
|
||||
}
|
||||
|
||||
// intLiteral returns an untyped integer literal that evaluates to i.
|
||||
func intLiteral(i int64) *Literal {
|
||||
return NewLiteral(exact.MakeInt64(i), types.Typ[types.UntypedInt])
|
||||
}
|
||||
|
||||
// nilLiteral returns a nil literal of the specified type, which may
|
||||
// be any reference type, including interfaces.
|
||||
//
|
||||
func nilLiteral(typ types.Type) *Literal {
|
||||
return NewLiteral(exact.MakeNil(), typ)
|
||||
}
|
||||
|
||||
// zeroLiteral returns a new "zero" literal of the specified type,
|
||||
// which must not be an array or struct type: the zero values of
|
||||
// aggregates are well-defined but cannot be represented by Literal.
|
||||
//
|
||||
func zeroLiteral(t types.Type) *Literal {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
switch {
|
||||
case t.Info()&types.IsBoolean != 0:
|
||||
return NewLiteral(exact.MakeBool(false), t)
|
||||
case t.Info()&types.IsNumeric != 0:
|
||||
return NewLiteral(exact.MakeInt64(0), t)
|
||||
case t.Info()&types.IsString != 0:
|
||||
return NewLiteral(exact.MakeString(""), t)
|
||||
case t.Kind() == types.UnsafePointer:
|
||||
fallthrough
|
||||
case t.Kind() == types.UntypedNil:
|
||||
return nilLiteral(t)
|
||||
default:
|
||||
panic(fmt.Sprint("zeroLiteral for unexpected type:", t))
|
||||
}
|
||||
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
|
||||
return nilLiteral(t)
|
||||
case *types.Named:
|
||||
return NewLiteral(zeroLiteral(t.Underlying()).Value, t)
|
||||
case *types.Array, *types.Struct:
|
||||
panic(fmt.Sprint("zeroLiteral applied to aggregate:", t))
|
||||
}
|
||||
panic(fmt.Sprint("zeroLiteral: unexpected ", t))
|
||||
}
|
||||
|
||||
func (l *Literal) Name() string {
|
||||
var s string
|
||||
if l.Value.Kind() == exact.String {
|
||||
s = exact.StringVal(l.Value)
|
||||
const maxLit = 20
|
||||
if len(s) > maxLit {
|
||||
s = s[:maxLit-3] + "..." // abbreviate
|
||||
}
|
||||
s = strconv.Quote(s)
|
||||
} else {
|
||||
s = l.Value.String()
|
||||
}
|
||||
return s + ":" + l.typ.String()
|
||||
}
|
||||
|
||||
func (l *Literal) Type() types.Type {
|
||||
return l.typ
|
||||
}
|
||||
|
||||
func (l *Literal) Referrers() *[]Instruction {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Literal) Pos() token.Pos {
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
// IsNil returns true if this literal represents a typed or untyped nil value.
|
||||
func (l *Literal) IsNil() bool {
|
||||
return l.Value.Kind() == exact.Nil
|
||||
}
|
||||
|
||||
// Int64 returns the numeric value of this literal truncated to fit
|
||||
// a signed 64-bit integer.
|
||||
//
|
||||
func (l *Literal) Int64() int64 {
|
||||
switch x := l.Value; x.Kind() {
|
||||
case exact.Int:
|
||||
if i, ok := exact.Int64Val(x); ok {
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
case exact.Float:
|
||||
f, _ := exact.Float64Val(x)
|
||||
return int64(f)
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected literal value: %T", l.Value))
|
||||
}
|
||||
|
||||
// Uint64 returns the numeric value of this literal truncated to fit
|
||||
// an unsigned 64-bit integer.
|
||||
//
|
||||
func (l *Literal) Uint64() uint64 {
|
||||
switch x := l.Value; x.Kind() {
|
||||
case exact.Int:
|
||||
if u, ok := exact.Uint64Val(x); ok {
|
||||
return u
|
||||
}
|
||||
return 0
|
||||
case exact.Float:
|
||||
f, _ := exact.Float64Val(x)
|
||||
return uint64(f)
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected literal value: %T", l.Value))
|
||||
}
|
||||
|
||||
// Float64 returns the numeric value of this literal truncated to fit
|
||||
// a float64.
|
||||
//
|
||||
func (l *Literal) Float64() float64 {
|
||||
f, _ := exact.Float64Val(l.Value)
|
||||
return f
|
||||
}
|
||||
|
||||
// Complex128 returns the complex value of this literal truncated to
|
||||
// fit a complex128.
|
||||
//
|
||||
func (l *Literal) Complex128() complex128 {
|
||||
re, _ := exact.Float64Val(exact.Real(l.Value))
|
||||
im, _ := exact.Float64Val(exact.Imag(l.Value))
|
||||
return complex(re, im)
|
||||
}
|
|
@ -47,7 +47,7 @@ func relName(v Value, i Instruction) string {
|
|||
// This method is provided only for debugging.
|
||||
// It never appears in disassembly, which uses Value.Name().
|
||||
|
||||
func (v *Literal) String() string {
|
||||
func (v *Const) String() string {
|
||||
return v.Name()
|
||||
}
|
||||
|
||||
|
@ -379,7 +379,7 @@ func (p *Package) DumpTo(w io.Writer) {
|
|||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
switch mem := p.Members[name].(type) {
|
||||
case *Constant:
|
||||
case *NamedConst:
|
||||
fmt.Fprintf(w, " const %-*s %s = %s\n", maxname, name, mem.Name(), mem.Value.Name())
|
||||
|
||||
case *Function:
|
||||
|
|
|
@ -173,8 +173,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(adonovan): sanity-check Literals used as instruction Operands(),
|
||||
// e.g. reject Literals with "untyped" types.
|
||||
// TODO(adonovan): sanity-check Consts used as instruction Operands(),
|
||||
// e.g. reject Consts with "untyped" types.
|
||||
//
|
||||
// All other non-Instruction Values can be found via their
|
||||
// enclosing Function or Package.
|
||||
|
|
|
@ -201,7 +201,7 @@ func (prog *Program) Package(obj *types.Package) *Package {
|
|||
|
||||
// packageLevelValue returns the package-level value corresponding to
|
||||
// the specified named object, which may be a package-level const
|
||||
// (*Literal), var (*Global) or func (*Function) of some package in
|
||||
// (*Const), var (*Global) or func (*Function) of some package in
|
||||
// prog. It returns nil if the object is not found.
|
||||
//
|
||||
func (prog *Program) packageLevelValue(obj types.Object) Value {
|
||||
|
@ -233,21 +233,21 @@ func (prog *Program) FuncValue(obj *types.Func) Value {
|
|||
}
|
||||
|
||||
// ConstValue returns the SSA Value denoted by the source-level named
|
||||
// constant obj. The result may be a *Literal, or nil if not found.
|
||||
// constant obj. The result may be a *Const, or nil if not found.
|
||||
//
|
||||
func (prog *Program) ConstValue(obj *types.Const) *Literal {
|
||||
func (prog *Program) ConstValue(obj *types.Const) *Const {
|
||||
// TODO(adonovan): opt: share (don't reallocate)
|
||||
// Literals for const objects.
|
||||
// Consts for const objects.
|
||||
|
||||
// Universal constant? {true,false,nil}
|
||||
if obj.Parent() == types.Universe {
|
||||
return NewLiteral(obj.Val(), obj.Type())
|
||||
return NewConst(obj.Val(), obj.Type())
|
||||
}
|
||||
// Package-level named constant?
|
||||
if v := prog.packageLevelValue(obj); v != nil {
|
||||
return v.(*Literal)
|
||||
return v.(*Const)
|
||||
}
|
||||
return NewLiteral(obj.Val(), obj.Type())
|
||||
return NewConst(obj.Val(), obj.Type())
|
||||
}
|
||||
|
||||
// VarValue returns the SSA Value that corresponds to a specific
|
||||
|
|
|
@ -25,7 +25,7 @@ func TestObjValueLookup(t *testing.T) {
|
|||
}
|
||||
|
||||
// Maps each var Ident (represented "name:linenum") to the
|
||||
// kind of ssa.Value we expect (represented "Literal", "&Alloc").
|
||||
// kind of ssa.Value we expect (represented "Constant", "&Alloc").
|
||||
expectations := make(map[string]string)
|
||||
|
||||
// Find all annotations of form x::BinOp, &y::Alloc, etc.
|
||||
|
@ -127,20 +127,20 @@ func checkFuncValue(t *testing.T, prog *ssa.Program, obj *types.Func) {
|
|||
}
|
||||
|
||||
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) {
|
||||
lit := prog.ConstValue(obj)
|
||||
// fmt.Printf("ConstValue(%s) = %s\n", obj, lit) // debugging
|
||||
if lit == nil {
|
||||
c := prog.ConstValue(obj)
|
||||
// fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging
|
||||
if c == nil {
|
||||
t.Errorf("ConstValue(%s) == nil", obj)
|
||||
return
|
||||
}
|
||||
if !types.IsIdentical(lit.Type(), obj.Type()) {
|
||||
t.Errorf("ConstValue(%s).Type() == %s", obj, lit.Type())
|
||||
if !types.IsIdentical(c.Type(), obj.Type()) {
|
||||
t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
|
||||
return
|
||||
}
|
||||
if obj.Name() != "nil" {
|
||||
if !exact.Compare(lit.Value, token.EQL, obj.Val()) {
|
||||
if !exact.Compare(c.Value, token.EQL, obj.Val()) {
|
||||
t.Errorf("ConstValue(%s).Value (%s) != %s",
|
||||
obj, lit.Value, obj.Val())
|
||||
obj, c.Value, obj.Val())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
48
ssa/ssa.go
48
ssa/ssa.go
|
@ -50,7 +50,7 @@ type Package struct {
|
|||
info *importer.PackageInfo // package ASTs and type information
|
||||
}
|
||||
|
||||
// A Member is a member of a Go package, implemented by *Constant,
|
||||
// A Member is a member of a Go package, implemented by *NamedConst,
|
||||
// *Global, *Function, or *Type; they are created by package-level
|
||||
// const, var, func and type declarations respectively.
|
||||
//
|
||||
|
@ -100,18 +100,18 @@ type Type struct {
|
|||
object *types.TypeName
|
||||
}
|
||||
|
||||
// A Constant is a Member of Package representing a package-level
|
||||
// constant value.
|
||||
// A NamedConst is a Member of Package representing a package-level
|
||||
// named constant value.
|
||||
//
|
||||
// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
|
||||
// identifier.
|
||||
//
|
||||
// NB: a Constant is not a Value; it contains a literal Value, which
|
||||
// NB: a NamedConst is not a Value; it contains a constant Value, which
|
||||
// it augments with the name and position of its 'const' declaration.
|
||||
//
|
||||
type Constant struct {
|
||||
type NamedConst struct {
|
||||
object *types.Const
|
||||
Value *Literal
|
||||
Value *Const
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ type Value interface {
|
|||
//
|
||||
// This is the same as the source name for Parameters,
|
||||
// Builtins, Functions, Captures, Globals and some Allocs.
|
||||
// For literals, it is a representation of the literal's value
|
||||
// For constants, it is a representation of the constant's value
|
||||
// and type. For all other Values this is the name of the
|
||||
// virtual register defined by the instruction.
|
||||
//
|
||||
|
@ -151,7 +151,7 @@ type Value interface {
|
|||
//
|
||||
// Referrers is currently only defined for the function-local
|
||||
// values Capture, Parameter and all value-defining instructions.
|
||||
// It returns nil for Function, Builtin, Literal and Global.
|
||||
// It returns nil for Function, Builtin, Const and Global.
|
||||
//
|
||||
// Instruction.Operands contains the inverse of this relation.
|
||||
Referrers() *[]Instruction
|
||||
|
@ -358,22 +358,22 @@ type Parameter struct {
|
|||
referrers []Instruction
|
||||
}
|
||||
|
||||
// A Literal represents the value of a constant expression.
|
||||
// A Const represents the value of a constant expression.
|
||||
//
|
||||
// It may have a nil, boolean, string or numeric (integer, fraction or
|
||||
// complex) value, or a []byte or []rune conversion of a string
|
||||
// literal.
|
||||
// constant.
|
||||
//
|
||||
// Literals may be of named types. A literal's underlying type can be
|
||||
// Consts may be of named types. A constant's underlying type can be
|
||||
// a basic type, possibly one of the "untyped" types, or a slice type
|
||||
// whose elements' underlying type is byte or rune. A nil literal can
|
||||
// whose elements' underlying type is byte or rune. A nil constant can
|
||||
// have any reference type: interface, map, channel, pointer, slice,
|
||||
// or function---but not "untyped nil".
|
||||
//
|
||||
// All source-level constant expressions are represented by a Literal
|
||||
// All source-level constant expressions are represented by a Const
|
||||
// of equal type and value.
|
||||
//
|
||||
// Value holds the exact value of the literal, independent of its
|
||||
// Value holds the exact value of the constant, independent of its
|
||||
// Type(), using the same representation as package go/exact uses for
|
||||
// constants.
|
||||
//
|
||||
|
@ -384,7 +384,7 @@ type Parameter struct {
|
|||
// "hello":untyped string
|
||||
// 3+4i:MyComplex
|
||||
//
|
||||
type Literal struct {
|
||||
type Const struct {
|
||||
typ types.Type
|
||||
Value exact.Value
|
||||
}
|
||||
|
@ -619,7 +619,7 @@ type ChangeInterface struct {
|
|||
// Use Program.MethodSet(X.Type()) to find the method-set of X.
|
||||
//
|
||||
// To construct the zero value of an interface type T, use:
|
||||
// NewLiteral(exact.MakeNil(), T, pos)
|
||||
// NewConst(exact.MakeNil(), T, pos)
|
||||
//
|
||||
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
|
||||
// from an explicit conversion in the source.
|
||||
|
@ -1384,14 +1384,14 @@ func (t *Type) String() string {
|
|||
return fmt.Sprintf("%s.%s", t.object.Pkg().Path(), t.object.Name())
|
||||
}
|
||||
|
||||
func (c *Constant) Name() string { return c.object.Name() }
|
||||
func (c *Constant) Pos() token.Pos { return c.object.Pos() }
|
||||
func (c *Constant) String() string {
|
||||
func (c *NamedConst) Name() string { return c.object.Name() }
|
||||
func (c *NamedConst) Pos() token.Pos { return c.object.Pos() }
|
||||
func (c *NamedConst) String() string {
|
||||
return fmt.Sprintf("%s.%s", c.object.Pkg().Path(), c.object.Name())
|
||||
}
|
||||
func (c *Constant) Type() types.Type { return c.object.Type() }
|
||||
func (c *Constant) Token() token.Token { return token.CONST }
|
||||
func (c *Constant) Object() types.Object { return c.object }
|
||||
func (c *NamedConst) Type() types.Type { return c.object.Type() }
|
||||
func (c *NamedConst) Token() token.Token { return token.CONST }
|
||||
func (c *NamedConst) Object() types.Object { return c.object }
|
||||
|
||||
// Func returns the package-level function of the specified name,
|
||||
// or nil if not found.
|
||||
|
@ -1412,8 +1412,8 @@ func (p *Package) Var(name string) (g *Global) {
|
|||
// Const returns the package-level constant of the specified name,
|
||||
// or nil if not found.
|
||||
//
|
||||
func (p *Package) Const(name string) (c *Constant) {
|
||||
c, _ = p.Members[name].(*Constant)
|
||||
func (p *Package) Const(name string) (c *NamedConst) {
|
||||
c, _ = p.Members[name].(*NamedConst)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -35,28 +35,28 @@ type S struct {
|
|||
}
|
||||
|
||||
func main() {
|
||||
var v0 int = 1 // v0::Literal (simple local value spec)
|
||||
if v0 > 0 { // v0::Literal
|
||||
v0 = 2 // v0::Literal
|
||||
var v0 int = 1 // v0::Const (simple local value spec)
|
||||
if v0 > 0 { // v0::Const
|
||||
v0 = 2 // v0::Const
|
||||
}
|
||||
print(v0) // v0::Phi
|
||||
|
||||
// v1 is captured and thus implicitly address-taken.
|
||||
var v1 int = 1 // v1::Literal
|
||||
v1 = 2 // v1::Literal
|
||||
var v1 int = 1 // v1::Const
|
||||
v1 = 2 // v1::Const
|
||||
fmt.Println(v1) // v1::UnOp (load)
|
||||
f := func(param int) { // f::MakeClosure param::Parameter
|
||||
if y := 1; y > 0 { // y::Literal
|
||||
if y := 1; y > 0 { // y::Const
|
||||
print(v1, param) // v1::UnOp (load) param::Parameter
|
||||
}
|
||||
param = 2 // param::Literal
|
||||
println(param) // param::Literal
|
||||
param = 2 // param::Const
|
||||
println(param) // param::Const
|
||||
}
|
||||
|
||||
f(0) // f::MakeClosure
|
||||
|
||||
var v2 int // v2::Literal (implicitly zero-initialized local value spec)
|
||||
print(v2) // v2::Literal
|
||||
var v2 int // v2::Const (implicitly zero-initialized local value spec)
|
||||
print(v2) // v2::Const
|
||||
|
||||
m := make(map[string]int) // m::MakeMap
|
||||
|
||||
|
@ -68,9 +68,9 @@ func main() {
|
|||
v3++ // v3::BinOp (assign with op)
|
||||
v3 += 2 // v3::BinOp (assign with op)
|
||||
|
||||
v5, v6 := false, "" // v5::Literal v6::Literal (defining assignment)
|
||||
print(v5) // v5::Literal
|
||||
print(v6) // v6::Literal
|
||||
v5, v6 := false, "" // v5::Const v6::Const (defining assignment)
|
||||
print(v5) // v5::Const
|
||||
print(v6) // v6::Const
|
||||
|
||||
var v7 S // v7::UnOp (load from Alloc)
|
||||
v7.x = 1 // &v7::Alloc
|
||||
|
@ -88,8 +88,8 @@ func main() {
|
|||
|
||||
v10 := &v9 // v10::Alloc &v9::Alloc
|
||||
|
||||
var v11 *J = nil // v11::Literal
|
||||
v11.method() // v11::Literal
|
||||
var v11 *J = nil // v11::Const
|
||||
v11.method() // v11::Const
|
||||
|
||||
var v12 J // v12::UnOp (load from Alloc)
|
||||
v12.method() // &v12::Alloc (implicitly address-taken)
|
||||
|
@ -100,7 +100,7 @@ func main() {
|
|||
println(v13) // v13::nil
|
||||
}
|
||||
|
||||
switch x := 1; x { // x::Literal
|
||||
switch x := 1; x { // x::Const
|
||||
case v0: // v0::Phi
|
||||
}
|
||||
|
||||
|
@ -108,10 +108,10 @@ func main() {
|
|||
v++ // v::BinOp
|
||||
}
|
||||
|
||||
if y := 0; y > 1 { // y::Literal y::Literal
|
||||
if y := 0; y > 1 { // y::Const y::Const
|
||||
}
|
||||
|
||||
var i interface{} // i::Literal (nil interface)
|
||||
var i interface{} // i::Const (nil interface)
|
||||
i = 1 // i::MakeInterface
|
||||
switch i := i.(type) { // i::MakeInterface i::MakeInterface
|
||||
case int:
|
||||
|
|
Loading…
Reference in New Issue