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)
|
tEface = new(types.Interface)
|
||||||
|
|
||||||
// SSA Value constants.
|
// SSA Value constants.
|
||||||
vZero = intLiteral(0)
|
vZero = intConst(0)
|
||||||
vOne = intLiteral(1)
|
vOne = intConst(1)
|
||||||
vTrue = NewLiteral(exact.MakeBool(true), tBool)
|
vTrue = NewConst(exact.MakeBool(true), tBool)
|
||||||
vFalse = NewLiteral(exact.MakeBool(false), tBool)
|
vFalse = NewConst(exact.MakeBool(false), tBool)
|
||||||
)
|
)
|
||||||
|
|
||||||
// builder holds state associated with the package currently being built.
|
// 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) {
|
switch cond := b.expr(fn, e).(type) {
|
||||||
case *Literal:
|
case *Const:
|
||||||
// Dispatch constant conditions statically.
|
// Dispatch constant conditions statically.
|
||||||
if exact.BoolVal(cond.Value) {
|
if exact.BoolVal(cond.Value) {
|
||||||
emitJump(fn, t)
|
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()
|
t := deref(fn.Pkg.typeOf(args[0])).Underlying()
|
||||||
if at, ok := t.(*types.Array); ok {
|
if at, ok := t.(*types.Array); ok {
|
||||||
b.expr(fn, args[0]) // for effects only
|
b.expr(fn, args[0]) // for effects only
|
||||||
return intLiteral(at.Len())
|
return intConst(at.Len())
|
||||||
}
|
}
|
||||||
// Otherwise treat as normal.
|
// 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 {
|
func (b *builder) expr(fn *Function, e ast.Expr) Value {
|
||||||
if v := fn.Pkg.info.ValueOf(e); v != nil {
|
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 {
|
if id, ok := unparen(e).(*ast.Ident); ok {
|
||||||
emitDebugRef(fn, id, lit)
|
emitDebugRef(fn, id, c)
|
||||||
}
|
}
|
||||||
return lit
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
switch e := e.(type) {
|
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)
|
st := sig.Params().At(np).Type().(*types.Slice)
|
||||||
vt := st.Elem()
|
vt := st.Elem()
|
||||||
if len(varargs) == 0 {
|
if len(varargs) == 0 {
|
||||||
args = append(args, nilLiteral(st))
|
args = append(args, nilConst(st))
|
||||||
} else {
|
} else {
|
||||||
// Replace a suffix of args with a slice containing it.
|
// Replace a suffix of args with a slice containing it.
|
||||||
at := types.NewArray(vt, int64(len(varargs)))
|
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 {
|
for i, arg := range varargs {
|
||||||
iaddr := &IndexAddr{
|
iaddr := &IndexAddr{
|
||||||
X: a,
|
X: a,
|
||||||
Index: intLiteral(int64(i)),
|
Index: intConst(int64(i)),
|
||||||
}
|
}
|
||||||
iaddr.setType(types.NewPointer(vt))
|
iaddr.setType(types.NewPointer(vt))
|
||||||
fn.emit(iaddr)
|
fn.emit(iaddr)
|
||||||
|
@ -1120,8 +1120,6 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
|
||||||
for _, id := range spec.Names {
|
for _, id := range spec.Names {
|
||||||
if !isBlankIdent(id) {
|
if !isBlankIdent(id) {
|
||||||
lhs := fn.addLocalForIdent(id)
|
lhs := fn.addLocalForIdent(id)
|
||||||
// TODO(adonovan): opt: use zero literal in
|
|
||||||
// lieu of load, if type permits.
|
|
||||||
if fn.debugInfo() {
|
if fn.debugInfo() {
|
||||||
emitDebugRef(fn, id, emitLoad(fn, lhs))
|
emitDebugRef(fn, id, emitLoad(fn, lhs))
|
||||||
}
|
}
|
||||||
|
@ -1201,7 +1199,7 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
|
||||||
var i int64 = -1
|
var i int64 = -1
|
||||||
for _, e := range elts {
|
for _, e := range elts {
|
||||||
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
||||||
i = b.expr(fn, kv.Key).(*Literal).Int64()
|
i = b.expr(fn, kv.Key).(*Const).Int64()
|
||||||
} else {
|
} else {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@ -1258,17 +1256,17 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ
|
||||||
array = addr
|
array = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
var idx *Literal
|
var idx *Const
|
||||||
for _, e := range e.Elts {
|
for _, e := range e.Elts {
|
||||||
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
if kv, ok := e.(*ast.KeyValueExpr); ok {
|
||||||
idx = b.expr(fn, kv.Key).(*Literal)
|
idx = b.expr(fn, kv.Key).(*Const)
|
||||||
e = kv.Value
|
e = kv.Value
|
||||||
} else {
|
} else {
|
||||||
var idxval int64
|
var idxval int64
|
||||||
if idx != nil {
|
if idx != nil {
|
||||||
idxval = idx.Int64() + 1
|
idxval = idx.Int64() + 1
|
||||||
}
|
}
|
||||||
idx = intLiteral(idxval)
|
idx = intConst(idxval)
|
||||||
}
|
}
|
||||||
iaddr := &IndexAddr{
|
iaddr := &IndexAddr{
|
||||||
X: array,
|
X: array,
|
||||||
|
@ -1286,7 +1284,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, typ typ
|
||||||
}
|
}
|
||||||
|
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
m := &MakeMap{Reserve: intLiteral(int64(len(e.Elts)))}
|
m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))}
|
||||||
m.setPos(e.Lbrace)
|
m.setPos(e.Lbrace)
|
||||||
m.setType(typ)
|
m.setType(typ)
|
||||||
emitStore(fn, addr, fn.emit(m))
|
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)
|
casetype = fn.Pkg.typeOf(cond)
|
||||||
var condv Value
|
var condv Value
|
||||||
if casetype == tUntypedNil {
|
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 {
|
} else {
|
||||||
yok := emitTypeTest(fn, x, casetype, token.NoPos)
|
yok := emitTypeTest(fn, x, casetype, token.NoPos)
|
||||||
ti = emitExtract(fn, yok, 0, casetype)
|
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")
|
body := fn.newBasicBlock("select.body")
|
||||||
next := fn.newBasicBlock("select.next")
|
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.currentBlock = body
|
||||||
fn.targets = &targets{
|
fn.targets = &targets{
|
||||||
tail: fn.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
|
// data dependence upon x, permitting later dead-code
|
||||||
// elimination if x is pure, static unrolling, etc.
|
// elimination if x is pure, static unrolling, etc.
|
||||||
// Ranging over a nil *array may have >0 iterations.
|
// Ranging over a nil *array may have >0 iterations.
|
||||||
length = intLiteral(arr.Len())
|
length = intConst(arr.Len())
|
||||||
} else {
|
} else {
|
||||||
// length = len(x).
|
// length = len(x).
|
||||||
var c Call
|
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)
|
index := fn.addLocal(tInt, token.NoPos)
|
||||||
emitStore(fn, index, intLiteral(-1))
|
emitStore(fn, index, intConst(-1))
|
||||||
|
|
||||||
loop = fn.newBasicBlock("rangeindex.loop")
|
loop = fn.newBasicBlock("rangeindex.loop")
|
||||||
emitJump(fn, 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}
|
pkg.Members[name] = &Type{object: obj}
|
||||||
|
|
||||||
case *types.Const:
|
case *types.Const:
|
||||||
c := &Constant{
|
c := &NamedConst{
|
||||||
object: obj,
|
object: obj,
|
||||||
Value: NewLiteral(obj.Val(), obj.Type()),
|
Value: NewConst(obj.Val(), obj.Type()),
|
||||||
}
|
}
|
||||||
pkg.values[obj] = c.Value
|
pkg.values[obj] = c.Value
|
||||||
pkg.Members[name] = c
|
pkg.Members[name] = c
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
// *Capture ✔
|
// *Capture ✔
|
||||||
// *ChangeInterface ✔ ✔
|
// *ChangeInterface ✔ ✔
|
||||||
// *ChangeType ✔ ✔
|
// *ChangeType ✔ ✔
|
||||||
// *Constant ✔ (const)
|
// *Const ✔
|
||||||
// *Convert ✔ ✔
|
// *Convert ✔ ✔
|
||||||
// *Defer ✔
|
// *Defer ✔
|
||||||
// *Extract ✔ ✔
|
// *Extract ✔ ✔
|
||||||
|
@ -70,7 +70,6 @@
|
||||||
// *Index ✔ ✔
|
// *Index ✔ ✔
|
||||||
// *IndexAddr ✔ ✔
|
// *IndexAddr ✔ ✔
|
||||||
// *Jump ✔
|
// *Jump ✔
|
||||||
// *Literal ✔
|
|
||||||
// *Lookup ✔ ✔
|
// *Lookup ✔ ✔
|
||||||
// *MakeChan ✔ ✔
|
// *MakeChan ✔ ✔
|
||||||
// *MakeClosure ✔ ✔
|
// *MakeClosure ✔ ✔
|
||||||
|
@ -78,6 +77,7 @@
|
||||||
// *MakeMap ✔ ✔
|
// *MakeMap ✔ ✔
|
||||||
// *MakeSlice ✔ ✔
|
// *MakeSlice ✔ ✔
|
||||||
// *MapUpdate ✔
|
// *MapUpdate ✔
|
||||||
|
// *NamedConst ✔ (const)
|
||||||
// *Next ✔ ✔
|
// *Next ✔ ✔
|
||||||
// *Panic ✔
|
// *Panic ✔
|
||||||
// *Parameter ✔
|
// *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())
|
y = emitConv(f, y, x.Type())
|
||||||
} else if _, ok := yt.(*types.Interface); ok {
|
} else if _, ok := yt.(*types.Interface); ok {
|
||||||
x = emitConv(f, x, y.Type())
|
x = emitConv(f, x, y.Type())
|
||||||
} else if _, ok := x.(*Literal); ok {
|
} else if _, ok := x.(*Const); ok {
|
||||||
x = emitConv(f, x, y.Type())
|
x = emitConv(f, x, y.Type())
|
||||||
} else if _, ok := y.(*Literal); ok {
|
} else if _, ok := y.(*Const); ok {
|
||||||
y = emitConv(f, y, x.Type())
|
y = emitConv(f, y, x.Type())
|
||||||
} else {
|
} else {
|
||||||
// other cases, e.g. channels. No-op.
|
// 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)
|
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 {
|
if ut_src == tUntypedNil {
|
||||||
return nilLiteral(typ)
|
return nilConst(typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert (non-nil) "untyped" literals to their default type.
|
// 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)
|
return f.emit(mi)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conversion of a literal to a non-interface type results in
|
// Conversion of a constant to a non-interface type results in
|
||||||
// a new literal of the destination type and (initially) the
|
// a new constant of the destination type and (initially) the
|
||||||
// same abstract value. We don't compute the representation
|
// same abstract value. We don't compute the representation
|
||||||
// change yet; this defers the point at which the number of
|
// change yet; this defers the point at which the number of
|
||||||
// possible representations explodes.
|
// possible representations explodes.
|
||||||
if l, ok := val.(*Literal); ok {
|
if c, ok := val.(*Const); ok {
|
||||||
return NewLiteral(l.Value, typ)
|
return NewConst(c.Value, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A representation-changing conversion.
|
// A representation-changing conversion.
|
||||||
|
|
|
@ -110,8 +110,8 @@ func (fr *frame) get(key ssa.Value) value {
|
||||||
return nil
|
return nil
|
||||||
case *ssa.Function, *ssa.Builtin:
|
case *ssa.Function, *ssa.Builtin:
|
||||||
return key
|
return key
|
||||||
case *ssa.Literal:
|
case *ssa.Const:
|
||||||
return literalValue(key)
|
return constValue(key)
|
||||||
case *ssa.Global:
|
case *ssa.Global:
|
||||||
if r, ok := fr.i.globals[key]; ok {
|
if r, ok := fr.i.globals[key]; ok {
|
||||||
return r
|
return r
|
||||||
|
|
|
@ -20,60 +20,60 @@ type targetPanic struct {
|
||||||
// If the target program calls exit, the interpreter panics with this type.
|
// If the target program calls exit, the interpreter panics with this type.
|
||||||
type exitPanic int
|
type exitPanic int
|
||||||
|
|
||||||
// literalValue returns the value of the literal with the
|
// constValue returns the value of the constant with the
|
||||||
// dynamic type tag appropriate for l.Type().
|
// dynamic type tag appropriate for c.Type().
|
||||||
func literalValue(l *ssa.Literal) value {
|
func constValue(c *ssa.Const) value {
|
||||||
if l.IsNil() {
|
if c.IsNil() {
|
||||||
return zero(l.Type()) // typed nil
|
return zero(c.Type()) // typed nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// By destination type:
|
// By destination type:
|
||||||
switch t := l.Type().Underlying().(type) {
|
switch t := c.Type().Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
// TODO(adonovan): eliminate untyped literals from SSA form.
|
// TODO(adonovan): eliminate untyped constants from SSA form.
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case types.Bool, types.UntypedBool:
|
case types.Bool, types.UntypedBool:
|
||||||
return exact.BoolVal(l.Value)
|
return exact.BoolVal(c.Value)
|
||||||
case types.Int, types.UntypedInt:
|
case types.Int, types.UntypedInt:
|
||||||
// Assume sizeof(int) is same on host and target.
|
// Assume sizeof(int) is same on host and target.
|
||||||
return int(l.Int64())
|
return int(c.Int64())
|
||||||
case types.Int8:
|
case types.Int8:
|
||||||
return int8(l.Int64())
|
return int8(c.Int64())
|
||||||
case types.Int16:
|
case types.Int16:
|
||||||
return int16(l.Int64())
|
return int16(c.Int64())
|
||||||
case types.Int32, types.UntypedRune:
|
case types.Int32, types.UntypedRune:
|
||||||
return int32(l.Int64())
|
return int32(c.Int64())
|
||||||
case types.Int64:
|
case types.Int64:
|
||||||
return l.Int64()
|
return c.Int64()
|
||||||
case types.Uint:
|
case types.Uint:
|
||||||
// Assume sizeof(uint) is same on host and target.
|
// Assume sizeof(uint) is same on host and target.
|
||||||
return uint(l.Uint64())
|
return uint(c.Uint64())
|
||||||
case types.Uint8:
|
case types.Uint8:
|
||||||
return uint8(l.Uint64())
|
return uint8(c.Uint64())
|
||||||
case types.Uint16:
|
case types.Uint16:
|
||||||
return uint16(l.Uint64())
|
return uint16(c.Uint64())
|
||||||
case types.Uint32:
|
case types.Uint32:
|
||||||
return uint32(l.Uint64())
|
return uint32(c.Uint64())
|
||||||
case types.Uint64:
|
case types.Uint64:
|
||||||
return l.Uint64()
|
return c.Uint64()
|
||||||
case types.Uintptr:
|
case types.Uintptr:
|
||||||
// Assume sizeof(uintptr) is same on host and target.
|
// Assume sizeof(uintptr) is same on host and target.
|
||||||
return uintptr(l.Uint64())
|
return uintptr(c.Uint64())
|
||||||
case types.Float32:
|
case types.Float32:
|
||||||
return float32(l.Float64())
|
return float32(c.Float64())
|
||||||
case types.Float64, types.UntypedFloat:
|
case types.Float64, types.UntypedFloat:
|
||||||
return l.Float64()
|
return c.Float64()
|
||||||
case types.Complex64:
|
case types.Complex64:
|
||||||
return complex64(l.Complex128())
|
return complex64(c.Complex128())
|
||||||
case types.Complex128, types.UntypedComplex:
|
case types.Complex128, types.UntypedComplex:
|
||||||
return l.Complex128()
|
return c.Complex128()
|
||||||
case types.String, types.UntypedString:
|
case types.String, types.UntypedString:
|
||||||
if l.Value.Kind() == exact.String {
|
if c.Value.Kind() == exact.String {
|
||||||
return exact.StringVal(l.Value)
|
return exact.StringVal(c.Value)
|
||||||
}
|
}
|
||||||
return string(rune(l.Int64()))
|
return string(rune(c.Int64()))
|
||||||
case types.UnsafePointer:
|
case types.UnsafePointer:
|
||||||
panic("unsafe.Pointer literal") // not possible
|
panic("unsafe.Pointer constant") // not possible
|
||||||
case types.UntypedNil:
|
case types.UntypedNil:
|
||||||
// nil was handled above.
|
// nil was handled above.
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,13 @@ func literalValue(l *ssa.Literal) value {
|
||||||
switch et.Kind() {
|
switch et.Kind() {
|
||||||
case types.Byte: // string -> []byte
|
case types.Byte: // string -> []byte
|
||||||
var v []value
|
var v []value
|
||||||
for _, b := range []byte(exact.StringVal(l.Value)) {
|
for _, b := range []byte(exact.StringVal(c.Value)) {
|
||||||
v = append(v, b)
|
v = append(v, b)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
case types.Rune: // string -> []rune
|
case types.Rune: // string -> []rune
|
||||||
var v []value
|
var v []value
|
||||||
for _, r := range []rune(exact.StringVal(l.Value)) {
|
for _, r := range []rune(exact.StringVal(c.Value)) {
|
||||||
v = append(v, r)
|
v = append(v, r)
|
||||||
}
|
}
|
||||||
return v
|
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
|
// 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 {
|
if t.Info()&types.IsUntyped != 0 {
|
||||||
// TODO(adonovan): make it an invariant that
|
// TODO(adonovan): make it an invariant that
|
||||||
// this is unreachable. Currently some
|
// this is unreachable. Currently some
|
||||||
// literals have 'untyped' types when they
|
// constants have 'untyped' types when they
|
||||||
// should be defaulted by the typechecker.
|
// should be defaulted by the typechecker.
|
||||||
t = ssa.DefaultType(t).(*types.Basic)
|
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:
|
// buildDomTree. For example:
|
||||||
//
|
//
|
||||||
// - Alloc never loaded? Eliminate.
|
// - 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;
|
// - Alloc stored once? Replace loads with dominating store;
|
||||||
// don't forget that an Alloc is itself an effective store
|
// don't forget that an Alloc is itself an effective store
|
||||||
// of zero.
|
// of zero.
|
||||||
|
@ -187,8 +187,8 @@ func lift(fn *Function) {
|
||||||
|
|
||||||
// renaming maps an alloc (keyed by index) to its replacement
|
// renaming maps an alloc (keyed by index) to its replacement
|
||||||
// value. Initially the renaming contains nil, signifying the
|
// value. Initially the renaming contains nil, signifying the
|
||||||
// zero literal of the appropriate type; we construct the
|
// zero constant of the appropriate type; we construct the
|
||||||
// Literal lazily at most once on each path through the domtree.
|
// Const lazily at most once on each path through the domtree.
|
||||||
// TODO(adonovan): opt: cache per-function not per subtree.
|
// TODO(adonovan): opt: cache per-function not per subtree.
|
||||||
renaming := make([]Value, numAllocs)
|
renaming := make([]Value, numAllocs)
|
||||||
|
|
||||||
|
@ -304,8 +304,8 @@ type newPhiMap map[*BasicBlock][]newPhi
|
||||||
//
|
//
|
||||||
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
|
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
|
||||||
// Don't lift aggregates into registers, because we don't have
|
// Don't lift aggregates into registers, because we don't have
|
||||||
// a way to express their zero-literals.
|
// a way to express their zero-constants.
|
||||||
// TODO(adonovan): define zero-literals for aggregates, or
|
// TODO(adonovan): define zero-constants for aggregates, or
|
||||||
// add a separate SRA pass. Lifting aggregates is an
|
// add a separate SRA pass. Lifting aggregates is an
|
||||||
// important optimisation for pointer analysis because the
|
// important optimisation for pointer analysis because the
|
||||||
// extra indirection really hurts precision under Das's
|
// extra indirection really hurts precision under Das's
|
||||||
|
@ -426,7 +426,7 @@ func replaceAll(x, y Value) {
|
||||||
func renamed(renaming []Value, alloc *Alloc) Value {
|
func renamed(renaming []Value, alloc *Alloc) Value {
|
||||||
v := renaming[alloc.index]
|
v := renaming[alloc.index]
|
||||||
if v == nil {
|
if v == nil {
|
||||||
v = zeroLiteral(deref(alloc.Type()))
|
v = zeroConst(deref(alloc.Type()))
|
||||||
renaming[alloc.index] = v
|
renaming[alloc.index] = v
|
||||||
}
|
}
|
||||||
return 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.
|
// This method is provided only for debugging.
|
||||||
// It never appears in disassembly, which uses Value.Name().
|
// It never appears in disassembly, which uses Value.Name().
|
||||||
|
|
||||||
func (v *Literal) String() string {
|
func (v *Const) String() string {
|
||||||
return v.Name()
|
return v.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,7 +379,7 @@ func (p *Package) DumpTo(w io.Writer) {
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
switch mem := p.Members[name].(type) {
|
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())
|
fmt.Fprintf(w, " const %-*s %s = %s\n", maxname, name, mem.Name(), mem.Value.Name())
|
||||||
|
|
||||||
case *Function:
|
case *Function:
|
||||||
|
|
|
@ -173,8 +173,8 @@ func (s *sanity) checkInstr(idx int, instr Instruction) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(adonovan): sanity-check Literals used as instruction Operands(),
|
// TODO(adonovan): sanity-check Consts used as instruction Operands(),
|
||||||
// e.g. reject Literals with "untyped" types.
|
// e.g. reject Consts with "untyped" types.
|
||||||
//
|
//
|
||||||
// All other non-Instruction Values can be found via their
|
// All other non-Instruction Values can be found via their
|
||||||
// enclosing Function or Package.
|
// enclosing Function or Package.
|
||||||
|
|
|
@ -201,7 +201,7 @@ func (prog *Program) Package(obj *types.Package) *Package {
|
||||||
|
|
||||||
// packageLevelValue returns the package-level value corresponding to
|
// packageLevelValue returns the package-level value corresponding to
|
||||||
// the specified named object, which may be a package-level const
|
// 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.
|
// prog. It returns nil if the object is not found.
|
||||||
//
|
//
|
||||||
func (prog *Program) packageLevelValue(obj types.Object) Value {
|
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
|
// 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)
|
// TODO(adonovan): opt: share (don't reallocate)
|
||||||
// Literals for const objects.
|
// Consts for const objects.
|
||||||
|
|
||||||
// Universal constant? {true,false,nil}
|
// Universal constant? {true,false,nil}
|
||||||
if obj.Parent() == types.Universe {
|
if obj.Parent() == types.Universe {
|
||||||
return NewLiteral(obj.Val(), obj.Type())
|
return NewConst(obj.Val(), obj.Type())
|
||||||
}
|
}
|
||||||
// Package-level named constant?
|
// Package-level named constant?
|
||||||
if v := prog.packageLevelValue(obj); v != nil {
|
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
|
// 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
|
// 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)
|
expectations := make(map[string]string)
|
||||||
|
|
||||||
// Find all annotations of form x::BinOp, &y::Alloc, etc.
|
// 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) {
|
func checkConstValue(t *testing.T, prog *ssa.Program, obj *types.Const) {
|
||||||
lit := prog.ConstValue(obj)
|
c := prog.ConstValue(obj)
|
||||||
// fmt.Printf("ConstValue(%s) = %s\n", obj, lit) // debugging
|
// fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging
|
||||||
if lit == nil {
|
if c == nil {
|
||||||
t.Errorf("ConstValue(%s) == nil", obj)
|
t.Errorf("ConstValue(%s) == nil", obj)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !types.IsIdentical(lit.Type(), obj.Type()) {
|
if !types.IsIdentical(c.Type(), obj.Type()) {
|
||||||
t.Errorf("ConstValue(%s).Type() == %s", obj, lit.Type())
|
t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if obj.Name() != "nil" {
|
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",
|
t.Errorf("ConstValue(%s).Value (%s) != %s",
|
||||||
obj, lit.Value, obj.Val())
|
obj, c.Value, obj.Val())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
48
ssa/ssa.go
48
ssa/ssa.go
|
@ -50,7 +50,7 @@ type Package struct {
|
||||||
info *importer.PackageInfo // package ASTs and type information
|
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
|
// *Global, *Function, or *Type; they are created by package-level
|
||||||
// const, var, func and type declarations respectively.
|
// const, var, func and type declarations respectively.
|
||||||
//
|
//
|
||||||
|
@ -100,18 +100,18 @@ type Type struct {
|
||||||
object *types.TypeName
|
object *types.TypeName
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Constant is a Member of Package representing a package-level
|
// A NamedConst is a Member of Package representing a package-level
|
||||||
// constant value.
|
// named constant value.
|
||||||
//
|
//
|
||||||
// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
|
// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
|
||||||
// identifier.
|
// 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.
|
// it augments with the name and position of its 'const' declaration.
|
||||||
//
|
//
|
||||||
type Constant struct {
|
type NamedConst struct {
|
||||||
object *types.Const
|
object *types.Const
|
||||||
Value *Literal
|
Value *Const
|
||||||
pos token.Pos
|
pos token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ type Value interface {
|
||||||
//
|
//
|
||||||
// This is the same as the source name for Parameters,
|
// This is the same as the source name for Parameters,
|
||||||
// Builtins, Functions, Captures, Globals and some Allocs.
|
// 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
|
// and type. For all other Values this is the name of the
|
||||||
// virtual register defined by the instruction.
|
// virtual register defined by the instruction.
|
||||||
//
|
//
|
||||||
|
@ -151,7 +151,7 @@ type Value interface {
|
||||||
//
|
//
|
||||||
// Referrers is currently only defined for the function-local
|
// Referrers is currently only defined for the function-local
|
||||||
// values Capture, Parameter and all value-defining instructions.
|
// 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.
|
// Instruction.Operands contains the inverse of this relation.
|
||||||
Referrers() *[]Instruction
|
Referrers() *[]Instruction
|
||||||
|
@ -358,22 +358,22 @@ type Parameter struct {
|
||||||
referrers []Instruction
|
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
|
// It may have a nil, boolean, string or numeric (integer, fraction or
|
||||||
// complex) value, or a []byte or []rune conversion of a string
|
// 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
|
// 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,
|
// have any reference type: interface, map, channel, pointer, slice,
|
||||||
// or function---but not "untyped nil".
|
// 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.
|
// 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
|
// Type(), using the same representation as package go/exact uses for
|
||||||
// constants.
|
// constants.
|
||||||
//
|
//
|
||||||
|
@ -384,7 +384,7 @@ type Parameter struct {
|
||||||
// "hello":untyped string
|
// "hello":untyped string
|
||||||
// 3+4i:MyComplex
|
// 3+4i:MyComplex
|
||||||
//
|
//
|
||||||
type Literal struct {
|
type Const struct {
|
||||||
typ types.Type
|
typ types.Type
|
||||||
Value exact.Value
|
Value exact.Value
|
||||||
}
|
}
|
||||||
|
@ -619,7 +619,7 @@ type ChangeInterface struct {
|
||||||
// Use Program.MethodSet(X.Type()) to find the method-set of X.
|
// Use Program.MethodSet(X.Type()) to find the method-set of X.
|
||||||
//
|
//
|
||||||
// To construct the zero value of an interface type T, use:
|
// 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
|
// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
|
||||||
// from an explicit conversion in the source.
|
// 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())
|
return fmt.Sprintf("%s.%s", t.object.Pkg().Path(), t.object.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Constant) Name() string { return c.object.Name() }
|
func (c *NamedConst) Name() string { return c.object.Name() }
|
||||||
func (c *Constant) Pos() token.Pos { return c.object.Pos() }
|
func (c *NamedConst) Pos() token.Pos { return c.object.Pos() }
|
||||||
func (c *Constant) String() string {
|
func (c *NamedConst) String() string {
|
||||||
return fmt.Sprintf("%s.%s", c.object.Pkg().Path(), c.object.Name())
|
return fmt.Sprintf("%s.%s", c.object.Pkg().Path(), c.object.Name())
|
||||||
}
|
}
|
||||||
func (c *Constant) Type() types.Type { return c.object.Type() }
|
func (c *NamedConst) Type() types.Type { return c.object.Type() }
|
||||||
func (c *Constant) Token() token.Token { return token.CONST }
|
func (c *NamedConst) Token() token.Token { return token.CONST }
|
||||||
func (c *Constant) Object() types.Object { return c.object }
|
func (c *NamedConst) Object() types.Object { return c.object }
|
||||||
|
|
||||||
// Func returns the package-level function of the specified name,
|
// Func returns the package-level function of the specified name,
|
||||||
// or nil if not found.
|
// 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,
|
// Const returns the package-level constant of the specified name,
|
||||||
// or nil if not found.
|
// or nil if not found.
|
||||||
//
|
//
|
||||||
func (p *Package) Const(name string) (c *Constant) {
|
func (p *Package) Const(name string) (c *NamedConst) {
|
||||||
c, _ = p.Members[name].(*Constant)
|
c, _ = p.Members[name].(*NamedConst)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,28 +35,28 @@ type S struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var v0 int = 1 // v0::Literal (simple local value spec)
|
var v0 int = 1 // v0::Const (simple local value spec)
|
||||||
if v0 > 0 { // v0::Literal
|
if v0 > 0 { // v0::Const
|
||||||
v0 = 2 // v0::Literal
|
v0 = 2 // v0::Const
|
||||||
}
|
}
|
||||||
print(v0) // v0::Phi
|
print(v0) // v0::Phi
|
||||||
|
|
||||||
// v1 is captured and thus implicitly address-taken.
|
// v1 is captured and thus implicitly address-taken.
|
||||||
var v1 int = 1 // v1::Literal
|
var v1 int = 1 // v1::Const
|
||||||
v1 = 2 // v1::Literal
|
v1 = 2 // v1::Const
|
||||||
fmt.Println(v1) // v1::UnOp (load)
|
fmt.Println(v1) // v1::UnOp (load)
|
||||||
f := func(param int) { // f::MakeClosure param::Parameter
|
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
|
print(v1, param) // v1::UnOp (load) param::Parameter
|
||||||
}
|
}
|
||||||
param = 2 // param::Literal
|
param = 2 // param::Const
|
||||||
println(param) // param::Literal
|
println(param) // param::Const
|
||||||
}
|
}
|
||||||
|
|
||||||
f(0) // f::MakeClosure
|
f(0) // f::MakeClosure
|
||||||
|
|
||||||
var v2 int // v2::Literal (implicitly zero-initialized local value spec)
|
var v2 int // v2::Const (implicitly zero-initialized local value spec)
|
||||||
print(v2) // v2::Literal
|
print(v2) // v2::Const
|
||||||
|
|
||||||
m := make(map[string]int) // m::MakeMap
|
m := make(map[string]int) // m::MakeMap
|
||||||
|
|
||||||
|
@ -68,9 +68,9 @@ func main() {
|
||||||
v3++ // v3::BinOp (assign with op)
|
v3++ // v3::BinOp (assign with op)
|
||||||
v3 += 2 // v3::BinOp (assign with op)
|
v3 += 2 // v3::BinOp (assign with op)
|
||||||
|
|
||||||
v5, v6 := false, "" // v5::Literal v6::Literal (defining assignment)
|
v5, v6 := false, "" // v5::Const v6::Const (defining assignment)
|
||||||
print(v5) // v5::Literal
|
print(v5) // v5::Const
|
||||||
print(v6) // v6::Literal
|
print(v6) // v6::Const
|
||||||
|
|
||||||
var v7 S // v7::UnOp (load from Alloc)
|
var v7 S // v7::UnOp (load from Alloc)
|
||||||
v7.x = 1 // &v7::Alloc
|
v7.x = 1 // &v7::Alloc
|
||||||
|
@ -88,8 +88,8 @@ func main() {
|
||||||
|
|
||||||
v10 := &v9 // v10::Alloc &v9::Alloc
|
v10 := &v9 // v10::Alloc &v9::Alloc
|
||||||
|
|
||||||
var v11 *J = nil // v11::Literal
|
var v11 *J = nil // v11::Const
|
||||||
v11.method() // v11::Literal
|
v11.method() // v11::Const
|
||||||
|
|
||||||
var v12 J // v12::UnOp (load from Alloc)
|
var v12 J // v12::UnOp (load from Alloc)
|
||||||
v12.method() // &v12::Alloc (implicitly address-taken)
|
v12.method() // &v12::Alloc (implicitly address-taken)
|
||||||
|
@ -100,7 +100,7 @@ func main() {
|
||||||
println(v13) // v13::nil
|
println(v13) // v13::nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch x := 1; x { // x::Literal
|
switch x := 1; x { // x::Const
|
||||||
case v0: // v0::Phi
|
case v0: // v0::Phi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,10 +108,10 @@ func main() {
|
||||||
v++ // v::BinOp
|
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
|
i = 1 // i::MakeInterface
|
||||||
switch i := i.(type) { // i::MakeInterface i::MakeInterface
|
switch i := i.(type) { // i::MakeInterface i::MakeInterface
|
||||||
case int:
|
case int:
|
||||||
|
|
Loading…
Reference in New Issue