package main import "go/token" // SSA operator codes (replacing go/token.Token for BinOp/UnOp). type SSAOp int32 const ( OpIllegal SSAOp = iota OpAdd OpSub OpMul OpQuo OpRem OpAnd OpXor OpShl OpShr OpOr OpAndNot OpLand OpLor OpArrow OpNot OpEql OpNeq OpLss OpLeq OpGtr OpGeq ) func (op SSAOp) String() string { switch op { case OpAdd: return "+" case OpSub: return "-" case OpMul: return "*" case OpQuo: return "/" case OpRem: return "%" case OpAnd: return "&" case OpXor: return "^" case OpOr: return "|" case OpShl: return "<<" case OpShr: return ">>" case OpAndNot: return "&^" case OpLand: return "&&" case OpLor: return "||" case OpArrow: return "<-" case OpNot: return "!" case OpEql: return "==" case OpNeq: return "!=" case OpLss: return "<" case OpLeq: return "<=" case OpGtr: return ">" case OpGeq: return ">=" } return "?" } // ssaConstString returns a string representation of a constant value. func ssaConstString(val ConstVal) string { if val == nil { return "nil" } return val.String() } // SSA Value interface. type SSAValue interface { SSAName() string String() string SSAType() Type SSAParent() *SSAFunction SSAPos() int32 } // SSA Instruction interface. type SSAInstruction interface { InstrBlock() *SSABasicBlock InstrParent() *SSAFunction InstrPos() int32 InstrString() string setBlock(*SSABasicBlock) } // SSA Member interface (package-level declaration). type SSAMember interface { MemberName() string MemberString() string MemberType() Type MemberPkg() *SSAPackage } // SSA Program - top-level container. type SSAProgram struct { imported map[string]*SSAPackage packages map[*TCPackage]*SSAPackage } func NewSSAProgram() *SSAProgram { return &SSAProgram{ imported: map[string]*SSAPackage{}, packages: map[*TCPackage]*SSAPackage{}, } } func (prog *SSAProgram) ImportedPackage(path string) *SSAPackage { return prog.imported[path] } // SSA Package. type SSAPackage struct { Prog *SSAProgram Pkg *TCPackage Members map[string]SSAMember } func (p *SSAPackage) Func(name string) *SSAFunction { m := p.Members[name] if m == nil { return nil } fn, _ := m.(*SSAFunction) return fn } // SSA Function. type SSAFunction struct { name string object *TCFunc Signature *Signature pos int32 Synthetic string Pkg *SSAPackage Prog *SSAProgram Params []*SSAParameter FreeVars []*SSAFreeVar Locals []*SSAAlloc Blocks []*SSABasicBlock AnonFuncs []*SSAFunction parent *SSAFunction vars map[Object]SSAValue } func (f *SSAFunction) MemberName() string { return f.name } func (f *SSAFunction) MemberString() string { return f.name } func (f *SSAFunction) MemberType() Type { if f.Signature == nil { return nil } return f.Signature } func (f *SSAFunction) MemberPkg() *SSAPackage { return f.Pkg } func (f *SSAFunction) SSAName() string { return f.name } func (f *SSAFunction) String() string { return f.name } func (f *SSAFunction) SSAType() Type { return f.MemberType() } func (f *SSAFunction) SSAParent() *SSAFunction { return f.parent } func (f *SSAFunction) SSAPos() int32 { return f.pos } // SSA BasicBlock. type SSABasicBlock struct { Index int Comment string parent *SSAFunction Instrs []SSAInstruction Preds []*SSABasicBlock Succs []*SSABasicBlock } func (b *SSABasicBlock) Parent() *SSAFunction { return b.parent } func NewSSABasicBlock(parent *SSAFunction, comment string) *SSABasicBlock { b := &SSABasicBlock{ Index: len(parent.Blocks), Comment: comment, parent: parent, } parent.Blocks = append(parent.Blocks, b) return b } // ssaRegister - shared base for instructions that produce a value. type ssaRegister struct { ssaInstr name string typ Type pos int32 } func (r *ssaRegister) SSAName() string { return r.name } func (r *ssaRegister) SSAType() Type { return r.typ } func (r *ssaRegister) SSAPos() int32 { return r.pos } func (r *ssaRegister) SSAParent() *SSAFunction { return r.block.parent } func (r *ssaRegister) String() string { return r.name } // ssaInstr - base for all instructions. type ssaInstr struct { block *SSABasicBlock } func (a *ssaInstr) InstrBlock() *SSABasicBlock { return a.block } func (a *ssaInstr) InstrParent() *SSAFunction { return a.block.parent } func (a *ssaInstr) setBlock(b *SSABasicBlock) { a.block = b } // Package-level global variable. type SSAGlobal struct { name string object *TCVar typ Type pos int32 pkg *SSAPackage } func (g *SSAGlobal) MemberName() string { return g.name } func (g *SSAGlobal) MemberString() string { return g.pkg.Pkg.Path() | "." | g.name } func (g *SSAGlobal) MemberType() Type { return g.typ } func (g *SSAGlobal) MemberPkg() *SSAPackage { return g.pkg } func (g *SSAGlobal) SSAName() string { return g.name } func (g *SSAGlobal) String() string { return g.MemberString() } func (g *SSAGlobal) SSAType() Type { return g.typ } func (g *SSAGlobal) SSAParent() *SSAFunction { return nil } func (g *SSAGlobal) SSAPos() int32 { return g.pos } // Package-level named type. type SSAType_ struct { object *TypeName pkg *SSAPackage } func (t *SSAType_) MemberName() string { return t.object.Name() } func (t *SSAType_) MemberString() string { return t.pkg.Pkg.Path() | "." | t.object.Name() } func (t *SSAType_) MemberType() Type { return t.object.Type() } func (t *SSAType_) MemberPkg() *SSAPackage { return t.pkg } // Package-level named constant. type SSANamedConst struct { object *TCConst Value *SSAConst pkg *SSAPackage } func (c *SSANamedConst) MemberName() string { return c.object.Name() } func (c *SSANamedConst) MemberString() string { return c.pkg.Pkg.Path() | "." | c.object.Name() } func (c *SSANamedConst) MemberType() Type { return c.object.Type() } func (c *SSANamedConst) MemberPkg() *SSAPackage { return c.pkg } // Parameter. type SSAParameter struct { name string object *TCVar typ Type pos int32 parent *SSAFunction } func (p *SSAParameter) SSAName() string { return p.name } func (p *SSAParameter) String() string { return p.name } func (p *SSAParameter) SSAType() Type { return p.typ } func (p *SSAParameter) SSAParent() *SSAFunction { return p.parent } func (p *SSAParameter) SSAPos() int32 { return p.pos } // FreeVar (closure capture). type SSAFreeVar struct { name string typ Type pos int32 parent *SSAFunction } func (v *SSAFreeVar) SSAName() string { return v.name } func (v *SSAFreeVar) String() string { return v.name } func (v *SSAFreeVar) SSAType() Type { return v.typ } func (v *SSAFreeVar) SSAParent() *SSAFunction { return v.parent } func (v *SSAFreeVar) SSAPos() int32 { return v.pos } // Const value. type SSAConst struct { typ Type val ConstVal } func NewSSAConst(val ConstVal, typ Type) *SSAConst { return &SSAConst{typ: typ, val: val} } func (c *SSAConst) SSAName() string { return ssaConstString(c.val) } func (c *SSAConst) String() string { return ssaConstString(c.val) } func (c *SSAConst) SSAType() Type { return c.typ } func (c *SSAConst) SSAParent() *SSAFunction { return nil } func (c *SSAConst) SSAPos() int32 { return 0 } func (c *SSAConst) Value() ConstVal { return c.val } type SSABuiltin struct { id BuiltinID name string } func (b *SSABuiltin) SSAName() string { return b.name } func (b *SSABuiltin) String() string { return b.name } func (b *SSABuiltin) SSAType() Type { return nil } func (b *SSABuiltin) SSAParent() *SSAFunction { return nil } func (b *SSABuiltin) SSAPos() int32 { return 0 } // CallCommon describes a function/method call. type SSACallCommon struct { Value SSAValue Args []SSAValue pos int32 } // -- Instruction types -- // Alloc allocates space for a variable. type SSAAlloc struct { ssaRegister Heap bool Comment string } func (a *SSAAlloc) InstrPos() int32 { return a.pos } func (a *SSAAlloc) InstrString() string { if a.Heap { return "new " | a.typ.String() } return "local " | a.typ.String() } // Phi node. type SSAPhi struct { ssaRegister Comment string Edges []SSAValue } func (p *SSAPhi) InstrPos() int32 { return p.pos } func (p *SSAPhi) InstrString() string { return "phi " | p.Comment } // Call instruction. type SSACall struct { ssaRegister Call SSACallCommon } func (c *SSACall) InstrPos() int32 { return c.Call.pos } func (c *SSACall) InstrString() string { return "call " | c.Call.Value.SSAName() } // BinOp. type SSABinOp struct { ssaRegister Op SSAOp X SSAValue Y SSAValue } func (b *SSABinOp) InstrPos() int32 { return b.pos } func (b *SSABinOp) InstrString() string { return "binop " | b.Op.String() } // UnOp. type SSAUnOp struct { ssaRegister Op SSAOp X SSAValue CommaOk bool } func (u *SSAUnOp) InstrPos() int32 { return u.pos } func (u *SSAUnOp) InstrString() string { return "unop " | u.Op.String() } // ChangeType. type SSAChangeType struct { ssaRegister X SSAValue } func (c *SSAChangeType) InstrPos() int32 { return c.pos } func (c *SSAChangeType) InstrString() string { return "changetype" } // Convert. type SSAConvert struct { ssaRegister X SSAValue } func (c *SSAConvert) InstrPos() int32 { return c.pos } func (c *SSAConvert) InstrString() string { return "convert" } // MakeInterface. type SSAMakeInterface struct { ssaRegister X SSAValue IType Type } func (m *SSAMakeInterface) InstrPos() int32 { return m.pos } func (m *SSAMakeInterface) InstrString() string { return "makeinterface" } // Invoke - interface method call. type SSAInvoke struct { ssaRegister X SSAValue MethodName string IfaceType *TCInterface Args []SSAValue } func (inv *SSAInvoke) InstrPos() int32 { return inv.pos } func (inv *SSAInvoke) InstrString() string { return "invoke " | inv.MethodName } // MakeClosure. type SSAMakeClosure struct { ssaRegister Fn SSAValue Bindings []SSAValue } func (m *SSAMakeClosure) InstrPos() int32 { return m.pos } func (m *SSAMakeClosure) InstrString() string { return "makeclosure" } // MakeMap. type SSAMakeMap struct { ssaRegister Reserve SSAValue } func (m *SSAMakeMap) InstrPos() int32 { return m.pos } func (m *SSAMakeMap) InstrString() string { return "makemap" } // MakeChan. type SSAMakeChan struct { ssaRegister Size SSAValue } func (m *SSAMakeChan) InstrPos() int32 { return m.pos } func (m *SSAMakeChan) InstrString() string { return "makechan" } // MakeSlice. type SSAMakeSlice struct { ssaRegister Len SSAValue Cap SSAValue } func (m *SSAMakeSlice) InstrPos() int32 { return m.pos } func (m *SSAMakeSlice) InstrString() string { return "makeslice" } // Slice. type SSASlice struct { ssaRegister X SSAValue Low SSAValue High SSAValue Max SSAValue } func (s *SSASlice) InstrPos() int32 { return s.pos } func (s *SSASlice) InstrString() string { return "slice" } // FieldAddr. type SSAFieldAddr struct { ssaRegister X SSAValue Field int } func (f *SSAFieldAddr) InstrPos() int32 { return f.pos } func (f *SSAFieldAddr) InstrString() string { return "fieldaddr" } // IndexAddr. type SSAIndexAddr struct { ssaRegister X SSAValue Index SSAValue } func (i *SSAIndexAddr) InstrPos() int32 { return i.pos } func (i *SSAIndexAddr) InstrString() string { return "indexaddr" } // Lookup (map[k]). type SSALookup struct { ssaRegister X SSAValue Index SSAValue CommaOk bool } func (l *SSALookup) InstrPos() int32 { return l.pos } func (l *SSALookup) InstrString() string { return "lookup" } // Range. type SSARange struct { ssaRegister X SSAValue } func (r *SSARange) InstrPos() int32 { return r.pos } func (r *SSARange) InstrString() string { return "range" } // Next (iterator advance). type SSANext struct { ssaRegister Iter SSAValue IsString bool } func (n *SSANext) InstrPos() int32 { return n.pos } func (n *SSANext) InstrString() string { return "next" } // TypeAssert. type SSATypeAssert struct { ssaRegister X SSAValue AssertedType Type CommaOk bool } func (t *SSATypeAssert) InstrPos() int32 { return t.pos } func (t *SSATypeAssert) InstrString() string { return "typeassert" } // Extract (tuple component). type SSAExtract struct { ssaRegister Tuple SSAValue Index int } func (e *SSAExtract) InstrPos() int32 { return e.pos } func (e *SSAExtract) InstrString() string { return "extract" } // -- Terminator instructions -- // Jump. type SSAJump struct { ssaInstr Comment string } func (j *SSAJump) InstrPos() int32 { return 0 } func (j *SSAJump) InstrString() string { return "jump " | j.Comment } // If. type SSAIf struct { ssaInstr Cond SSAValue } func (i *SSAIf) InstrPos() int32 { return 0 } func (i *SSAIf) InstrString() string { return "if" } // Return. type SSAReturn struct { ssaInstr Results []SSAValue pos int32 } func (r *SSAReturn) InstrPos() int32 { return r.pos } func (r *SSAReturn) InstrString() string { return "return" } // RunDefers. type SSARunDefers struct { ssaInstr } func (rd *SSARunDefers) InstrPos() int32 { return 0 } func (rd *SSARunDefers) InstrString() string { return "rundefers" } // Panic. type SSAPanic struct { ssaInstr X SSAValue pos int32 } func (p *SSAPanic) InstrPos() int32 { return p.pos } func (p *SSAPanic) InstrString() string { return "panic" } // Go (spawn). type SSAGo struct { ssaInstr Call SSACallCommon pos int32 } func (g *SSAGo) InstrPos() int32 { return g.pos } func (g *SSAGo) InstrString() string { return "go" } // Defer. type SSADefer struct { ssaInstr Call SSACallCommon pos int32 } func (d *SSADefer) InstrPos() int32 { return d.pos } func (d *SSADefer) InstrString() string { return "defer" } // -- Side-effecting instructions -- // Send. type SSASend struct { ssaInstr Chan SSAValue X SSAValue pos int32 } func (s *SSASend) InstrPos() int32 { return s.pos } func (s *SSASend) InstrString() string { return "send" } // Store. type SSAStore struct { ssaInstr Addr SSAValue Val SSAValue pos int32 } func (s *SSAStore) InstrPos() int32 { return s.pos } func (s *SSAStore) InstrString() string { return "store" } // MapUpdate. type SSAMapUpdate struct { ssaInstr Map SSAValue Key SSAValue Value SSAValue pos int32 } func (m *SSAMapUpdate) InstrPos() int32 { return m.pos } func (m *SSAMapUpdate) InstrString() string { return "mapupdate" } // SelectState describes one case of a select statement. type SSASelectState struct { Dir token.Token // token.ARROW for send, token.DEFAULT for recv Chan SSAValue Send SSAValue } // Select instruction. type SSASelect struct { ssaRegister States []*SSASelectState Blocking bool } func (s *SSASelect) InstrPos() int32 { return s.pos } func (s *SSASelect) InstrString() string { return "select" } // Operator mapping from syntax.Operator to SSAOp. func syntaxOpToSSAOp(op Operator, unary bool) SSAOp { if unary { switch op { case Not: return OpNot case Recv: return OpArrow case And: return OpAnd case Mul: return OpMul case Sub: return OpSub case Xor: return OpXor } } switch op { case Add: return OpAdd case Or: return OpOr case Sub: return OpSub case Mul: return OpMul case Div: return OpQuo case Rem: return OpRem case And: return OpAnd case Xor: return OpXor case Shl: return OpShl case Shr: return OpShr case AndNot: return OpAndNot case OrOr: return OpLor case AndAnd: return OpLand case Eql: return OpEql case Neq: return OpNeq case Lss: return OpLss case Leq: return OpLeq case Gtr: return OpGtr case Geq: return OpGeq } return OpIllegal } func compoundOp(op Operator) SSAOp { switch op { case Add: return OpAdd case Or: return OpOr case Sub: return OpSub case Mul: return OpMul case Div: return OpQuo case Rem: return OpRem case And: return OpAnd case Xor: return OpXor case Shl: return OpShl case Shr: return OpShr case AndNot: return OpAndNot } return OpIllegal } func ssaBuiltinID(name string) (BuiltinID, bool) { switch name { case "append": return BuiltinAppend, true case "cap": return BuiltinCap, true case "clear": return BuiltinClear, true case "close": return BuiltinClose, true case "copy": return BuiltinCopy, true case "delete": return BuiltinDelete, true case "len": return BuiltinLen, true case "make": return BuiltinMake, true case "max": return BuiltinMax, true case "min": return BuiltinMin, true case "new": return BuiltinNew, true case "panic": return BuiltinPanic, true case "print": return BuiltinPrint, true case "println": return BuiltinPrintln, true case "recover": return BuiltinRecover, true } return 0, false } func ssaItoa(n int) string { if n == 0 { return "0" } neg := n < 0 if neg { n = -n } buf := []byte{:0:20} for n > 0 { buf = append(buf, byte('0'+n%10)) n /= 10 } if neg { buf = append(buf, '-') } for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { buf[i], buf[j] = buf[j], buf[i] } return string(buf) } // Type helpers for SSA builder. func ssaElemType(t Type) Type { if t == nil { return nil } switch t := t.Underlying().(type) { case *Slice: return t.Elem() case *Array: return t.Elem() case *TCMap: return t.Elem() case *Pointer: return t.Elem() case *Basic: if t.Info()&IsString != 0 { return Typ[Uint8] } } return nil } func ssaChanElemType(t Type) Type { if t == nil { return nil } if ch, ok := t.Underlying().(*TCChan); ok { return ch.Elem() } return nil } func ssaIsStringType(t Type) bool { if t == nil { return false } if b, ok := t.Underlying().(*Basic); ok { return b.Info()&IsString != 0 } return false } func ssaSliceOf(t Type) Type { if t == nil { return nil } if b, ok := t.Underlying().(*Basic); ok && b.Info()&IsString != 0 { return t } switch t := t.Underlying().(type) { case *Slice: return t case *Array: if b, ok := t.Elem().(*Basic); ok && b.kind == Uint8 { return Typ[TCString] } return NewSlice(t.Elem()) } return Typ[TCString] } func ssaTupleElemType(t Type, i int) Type { if t == nil { return nil } if tup, ok := t.(*Tuple); ok && i < tup.Len() { return tup.At(i).Type() } return nil } func ssaParseInt64(s string) int64 { var n int64 for i := 0; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { break } n = n*10 + int64(c-'0') } return n }