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  }