| package ssa |
| |
| // Helpers for emitting SSA instructions. |
| |
| import ( |
| "go/token" |
| |
| "code.google.com/p/go.exp/go/types" |
| ) |
| |
| // emitNew emits to f a new (heap Alloc) instruction allocating an |
| // object of type typ. |
| // |
| func emitNew(f *Function, typ types.Type) Value { |
| return f.emit(&Alloc{ |
| Type_: pointer(typ), |
| Heap: true, |
| }) |
| } |
| |
| // 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 a different one? |
| if _, ok := ut_src.(*types.Interface); ok { |
| c := &ChangeInterface{X: val} |
| c.setType(typ) |
| return f.emit(c) |
| } |
| |
| // 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) |
| } |
| |
| // 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.Args = append(call.Args, arg) |
| } |
| call.Type_ = &types.Result{Values: f.Signature.Results} |
| tuple := f.emit(call) |
| var ret Ret |
| switch { |
| case len(f.Signature.Results) > 1: |
| 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) |
| } |
| case len(f.Signature.Results) == 1: |
| ret.Results = []Value{tuple} |
| default: |
| // no-op |
| } |
| f.emit(&ret) |
| f.currentBlock = nil |
| } |