github.com/aykevl/tinygo@v0.5.0/ir/passes.go (about) 1 package ir 2 3 import ( 4 "go/types" 5 6 "golang.org/x/tools/go/ssa" 7 ) 8 9 // This file implements several optimization passes (analysis + transform) to 10 // optimize code in SSA form before it is compiled to LLVM IR. It is based on 11 // the IR defined in ir.go. 12 13 // Make a readable version of a method signature (including the function name, 14 // excluding the receiver name). This string is used internally to match 15 // interfaces and to call the correct method on an interface. Examples: 16 // 17 // String() string 18 // Read([]byte) (int, error) 19 func MethodSignature(method *types.Func) string { 20 return method.Name() + signature(method.Type().(*types.Signature)) 21 } 22 23 // Make a readable version of a function (pointer) signature. 24 // Examples: 25 // 26 // () string 27 // (string, int) (int, error) 28 func signature(sig *types.Signature) string { 29 s := "" 30 if sig.Params().Len() == 0 { 31 s += "()" 32 } else { 33 s += "(" 34 for i := 0; i < sig.Params().Len(); i++ { 35 if i > 0 { 36 s += ", " 37 } 38 s += sig.Params().At(i).Type().String() 39 } 40 s += ")" 41 } 42 if sig.Results().Len() == 0 { 43 // keep as-is 44 } else if sig.Results().Len() == 1 { 45 s += " " + sig.Results().At(0).Type().String() 46 } else { 47 s += " (" 48 for i := 0; i < sig.Results().Len(); i++ { 49 if i > 0 { 50 s += ", " 51 } 52 s += sig.Results().At(i).Type().String() 53 } 54 s += ")" 55 } 56 return s 57 } 58 59 // Simple pass that removes dead code. This pass makes later analysis passes 60 // more useful. 61 func (p *Program) SimpleDCE() { 62 // Unmark all functions. 63 for _, f := range p.Functions { 64 f.flag = false 65 } 66 67 // Initial set of live functions. Include main.main, *.init and runtime.* 68 // functions. 69 main := p.mainPkg.Members["main"].(*ssa.Function) 70 runtimePkg := p.Program.ImportedPackage("runtime") 71 mathPkg := p.Program.ImportedPackage("math") 72 p.GetFunction(main).flag = true 73 worklist := []*ssa.Function{main} 74 for _, f := range p.Functions { 75 if f.exported || f.Synthetic == "package initializer" || f.Pkg == runtimePkg || (f.Pkg == mathPkg && f.Pkg != nil) { 76 if f.flag { 77 continue 78 } 79 f.flag = true 80 worklist = append(worklist, f.Function) 81 } 82 } 83 84 // Mark all called functions recursively. 85 for len(worklist) != 0 { 86 f := worklist[len(worklist)-1] 87 worklist = worklist[:len(worklist)-1] 88 for _, block := range f.Blocks { 89 for _, instr := range block.Instrs { 90 if instr, ok := instr.(*ssa.MakeInterface); ok { 91 for _, sel := range getAllMethods(p.Program, instr.X.Type()) { 92 fn := p.Program.MethodValue(sel) 93 callee := p.GetFunction(fn) 94 if callee == nil { 95 // TODO: why is this necessary? 96 p.addFunction(fn) 97 callee = p.GetFunction(fn) 98 } 99 if !callee.flag { 100 callee.flag = true 101 worklist = append(worklist, callee.Function) 102 } 103 } 104 } 105 for _, operand := range instr.Operands(nil) { 106 if operand == nil || *operand == nil { 107 continue 108 } 109 switch operand := (*operand).(type) { 110 case *ssa.Function: 111 f := p.GetFunction(operand) 112 if f == nil { 113 // FIXME HACK: this function should have been 114 // discovered already. It is not for bound methods. 115 p.addFunction(operand) 116 f = p.GetFunction(operand) 117 } 118 if !f.flag { 119 f.flag = true 120 worklist = append(worklist, operand) 121 } 122 } 123 } 124 } 125 } 126 } 127 128 // Remove unmarked functions. 129 livefunctions := []*Function{} 130 for _, f := range p.Functions { 131 if f.flag { 132 livefunctions = append(livefunctions, f) 133 } else { 134 delete(p.functionMap, f.Function) 135 } 136 } 137 p.Functions = livefunctions 138 }