mirror of
https://github.com/golang/go
synced 2024-11-19 03:54:42 -07:00
302 lines
8.4 KiB
Go
302 lines
8.4 KiB
Go
|
package ssa
|
||
|
|
||
|
// Helpers for emitting SSA instructions.
|
||
|
|
||
|
import (
|
||
|
"go/token"
|
||
|
|
||
|
"code.google.com/p/go.tools/go/types"
|
||
|
)
|
||
|
|
||
|
// emitNew emits to f a new (heap Alloc) instruction allocating an
|
||
|
// object of type typ. pos is the optional source location.
|
||
|
//
|
||
|
func emitNew(f *Function, typ types.Type, pos token.Pos) Value {
|
||
|
return f.emit(&Alloc{
|
||
|
Type_: pointer(typ),
|
||
|
Heap: true,
|
||
|
Pos: pos,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// emitLoad emits to f an instruction to load the address addr into a
|
||
|
// new temporary, and returns the value so defined.
|
||
|
//
|
||
|
func emitLoad(f *Function, addr Value) Value {
|
||
|
v := &UnOp{Op: token.MUL, X: addr}
|
||
|
v.setType(indirectType(addr.Type()))
|
||
|
return f.emit(v)
|
||
|
}
|
||
|
|
||
|
// emitArith emits to f code to compute the binary operation op(x, y)
|
||
|
// where op is an eager shift, logical or arithmetic operation.
|
||
|
// (Use emitCompare() for comparisons and Builder.logicalBinop() for
|
||
|
// non-eager operations.)
|
||
|
//
|
||
|
func emitArith(f *Function, op token.Token, x, y Value, t types.Type) Value {
|
||
|
switch op {
|
||
|
case token.SHL, token.SHR:
|
||
|
x = emitConv(f, x, t)
|
||
|
y = emitConv(f, y, types.Typ[types.Uint64])
|
||
|
|
||
|
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
|
||
|
x = emitConv(f, x, t)
|
||
|
y = emitConv(f, y, t)
|
||
|
|
||
|
default:
|
||
|
panic("illegal op in emitArith: " + op.String())
|
||
|
|
||
|
}
|
||
|
v := &BinOp{
|
||
|
Op: op,
|
||
|
X: x,
|
||
|
Y: y,
|
||
|
}
|
||
|
v.setType(t)
|
||
|
return f.emit(v)
|
||
|
}
|
||
|
|
||
|
// emitCompare emits to f code compute the boolean result of
|
||
|
// comparison comparison 'x op y'.
|
||
|
//
|
||
|
func emitCompare(f *Function, op token.Token, x, y Value) Value {
|
||
|
xt := underlyingType(x.Type())
|
||
|
yt := underlyingType(y.Type())
|
||
|
|
||
|
// Special case to optimise a tagless SwitchStmt so that
|
||
|
// these are equivalent
|
||
|
// switch { case e: ...}
|
||
|
// switch true { case e: ... }
|
||
|
// if e==true { ... }
|
||
|
// even in the case when e's type is an interface.
|
||
|
// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
|
||
|
if x == vTrue && op == token.EQL {
|
||
|
if yt, ok := yt.(*types.Basic); ok && yt.Info&types.IsBoolean != 0 {
|
||
|
return y
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if types.IsIdentical(xt, yt) {
|
||
|
// no conversion necessary
|
||
|
} else if _, ok := xt.(*types.Interface); ok {
|
||
|
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 {
|
||
|
x = emitConv(f, x, y.Type())
|
||
|
} else if _, ok := y.(*Literal); ok {
|
||
|
y = emitConv(f, y, x.Type())
|
||
|
} else {
|
||
|
// other cases, e.g. channels. No-op.
|
||
|
}
|
||
|
|
||
|
v := &BinOp{
|
||
|
Op: op,
|
||
|
X: x,
|
||
|
Y: y,
|
||
|
}
|
||
|
v.setType(tBool)
|
||
|
return f.emit(v)
|
||
|
}
|
||
|
|
||
|
// emitConv emits to f code to convert Value val to exactly type typ,
|
||
|
// and returns the converted value. Implicit conversions are implied
|
||
|
// by language assignability rules in the following operations:
|
||
|
//
|
||
|
// - from rvalue type to lvalue type in assignments.
|
||
|
// - from actual- to formal-parameter types in function calls.
|
||
|
// - from return value type to result type in return statements.
|
||
|
// - population of struct fields, array and slice elements, and map
|
||
|
// keys and values within compoisite literals
|
||
|
// - from index value to index type in indexing expressions.
|
||
|
// - for both arguments of comparisons.
|
||
|
// - from value type to channel type in send expressions.
|
||
|
//
|
||
|
func emitConv(f *Function, val Value, typ types.Type) Value {
|
||
|
// fmt.Printf("emitConv %s -> %s, %T", val.Type(), typ, val) // debugging
|
||
|
|
||
|
// Identical types? Conversion is a no-op.
|
||
|
if types.IsIdentical(val.Type(), typ) {
|
||
|
return val
|
||
|
}
|
||
|
|
||
|
ut_dst := underlyingType(typ)
|
||
|
ut_src := underlyingType(val.Type())
|
||
|
|
||
|
// Identical underlying types? Conversion is a name change.
|
||
|
if types.IsIdentical(ut_dst, ut_src) {
|
||
|
// TODO(adonovan): make this use a distinct
|
||
|
// instruction, ChangeType. This instruction must
|
||
|
// also cover the cases of channel type restrictions and
|
||
|
// conversions between pointers to identical base
|
||
|
// types.
|
||
|
c := &Conv{X: val}
|
||
|
c.setType(typ)
|
||
|
return f.emit(c)
|
||
|
}
|
||
|
|
||
|
// Conversion to, or construction of a value of, an interface type?
|
||
|
if _, ok := ut_dst.(*types.Interface); ok {
|
||
|
|
||
|
// Assignment from one interface type to another?
|
||
|
if _, ok := ut_src.(*types.Interface); ok {
|
||
|
return emitTypeAssert(f, val, typ)
|
||
|
}
|
||
|
|
||
|
// Untyped nil literal? Return interface-typed nil literal.
|
||
|
if ut_src == tUntypedNil {
|
||
|
return nilLiteral(typ)
|
||
|
}
|
||
|
|
||
|
// Convert (non-nil) "untyped" literals to their default type.
|
||
|
// TODO(gri): expose types.isUntyped().
|
||
|
if t, ok := ut_src.(*types.Basic); ok && t.Info&types.IsUntyped != 0 {
|
||
|
val = emitConv(f, val, DefaultType(ut_src))
|
||
|
}
|
||
|
|
||
|
mi := &MakeInterface{
|
||
|
X: val,
|
||
|
Methods: f.Prog.MethodSet(val.Type()),
|
||
|
}
|
||
|
mi.setType(typ)
|
||
|
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
|
||
|
// 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)
|
||
|
}
|
||
|
|
||
|
// A representation-changing conversion.
|
||
|
c := &Conv{X: val}
|
||
|
c.setType(typ)
|
||
|
return f.emit(c)
|
||
|
}
|
||
|
|
||
|
// emitStore emits to f an instruction to store value val at location
|
||
|
// addr, applying implicit conversions as required by assignabilty rules.
|
||
|
//
|
||
|
func emitStore(f *Function, addr, val Value) {
|
||
|
f.emit(&Store{
|
||
|
Addr: addr,
|
||
|
Val: emitConv(f, val, indirectType(addr.Type())),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// emitJump emits to f a jump to target, and updates the control-flow graph.
|
||
|
// Postcondition: f.currentBlock is nil.
|
||
|
//
|
||
|
func emitJump(f *Function, target *BasicBlock) {
|
||
|
b := f.currentBlock
|
||
|
b.emit(new(Jump))
|
||
|
addEdge(b, target)
|
||
|
f.currentBlock = nil
|
||
|
}
|
||
|
|
||
|
// emitIf emits to f a conditional jump to tblock or fblock based on
|
||
|
// cond, and updates the control-flow graph.
|
||
|
// Postcondition: f.currentBlock is nil.
|
||
|
//
|
||
|
func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
|
||
|
b := f.currentBlock
|
||
|
b.emit(&If{Cond: cond})
|
||
|
addEdge(b, tblock)
|
||
|
addEdge(b, fblock)
|
||
|
f.currentBlock = nil
|
||
|
}
|
||
|
|
||
|
// emitExtract emits to f an instruction to extract the index'th
|
||
|
// component of tuple, ascribing it type typ. It returns the
|
||
|
// extracted value.
|
||
|
//
|
||
|
func emitExtract(f *Function, tuple Value, index int, typ types.Type) Value {
|
||
|
e := &Extract{Tuple: tuple, Index: index}
|
||
|
// In all cases but one (tSelect's recv), typ is redundant w.r.t.
|
||
|
// tuple.Type().(*types.Result).Values[index].Type.
|
||
|
e.setType(typ)
|
||
|
return f.emit(e)
|
||
|
}
|
||
|
|
||
|
// emitTypeAssert emits to f a type assertion value := x.(t) and
|
||
|
// returns the value. x.Type() must be an interface.
|
||
|
//
|
||
|
func emitTypeAssert(f *Function, x Value, t types.Type) Value {
|
||
|
// Simplify infallible assertions.
|
||
|
txi := underlyingType(x.Type()).(*types.Interface)
|
||
|
if ti, ok := underlyingType(t).(*types.Interface); ok {
|
||
|
if types.IsIdentical(ti, txi) {
|
||
|
return x
|
||
|
}
|
||
|
if isSuperinterface(ti, txi) {
|
||
|
c := &ChangeInterface{X: x}
|
||
|
c.setType(t)
|
||
|
return f.emit(c)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
a := &TypeAssert{X: x, AssertedType: t}
|
||
|
a.setType(t)
|
||
|
return f.emit(a)
|
||
|
}
|
||
|
|
||
|
// emitTypeTest emits to f a type test value,ok := x.(t) and returns
|
||
|
// a (value, ok) tuple. x.Type() must be an interface.
|
||
|
//
|
||
|
func emitTypeTest(f *Function, x Value, t types.Type) Value {
|
||
|
// TODO(adonovan): opt: simplify infallible tests as per
|
||
|
// emitTypeAssert, and return (x, vTrue).
|
||
|
// (Requires that exprN returns a slice of extracted values,
|
||
|
// not a single Value of type *types.Results.)
|
||
|
a := &TypeAssert{
|
||
|
X: x,
|
||
|
AssertedType: t,
|
||
|
CommaOk: true,
|
||
|
}
|
||
|
a.setType(&types.Result{Values: []*types.Var{
|
||
|
{Name: "value", Type: t},
|
||
|
varOk,
|
||
|
}})
|
||
|
return f.emit(a)
|
||
|
}
|
||
|
|
||
|
// emitTailCall emits to f a function call in tail position,
|
||
|
// passing on all but the first formal parameter to f as actual
|
||
|
// values in the call. Intended for delegating bridge methods.
|
||
|
// Precondition: f does/will not use deferred procedure calls.
|
||
|
// Postcondition: f.currentBlock is nil.
|
||
|
//
|
||
|
func emitTailCall(f *Function, call *Call) {
|
||
|
for _, arg := range f.Params[1:] {
|
||
|
call.Call.Args = append(call.Call.Args, arg)
|
||
|
}
|
||
|
nr := len(f.Signature.Results)
|
||
|
if nr == 1 {
|
||
|
call.Type_ = f.Signature.Results[0].Type
|
||
|
} else {
|
||
|
call.Type_ = &types.Result{Values: f.Signature.Results}
|
||
|
}
|
||
|
tuple := f.emit(call)
|
||
|
var ret Ret
|
||
|
switch nr {
|
||
|
case 0:
|
||
|
// no-op
|
||
|
case 1:
|
||
|
ret.Results = []Value{tuple}
|
||
|
default:
|
||
|
for i, o := range call.Type().(*types.Result).Values {
|
||
|
v := emitExtract(f, tuple, i, o.Type)
|
||
|
// TODO(adonovan): in principle, this is required:
|
||
|
// v = emitConv(f, o.Type, f.Signature.Results[i].Type)
|
||
|
// but in practice emitTailCall is only used when
|
||
|
// the types exactly match.
|
||
|
ret.Results = append(ret.Results, v)
|
||
|
}
|
||
|
}
|
||
|
f.emit(&ret)
|
||
|
f.currentBlock = nil
|
||
|
}
|