github.com/llir/llvm@v0.3.6/ir/callgraph_test.go (about)

     1  // This example program analyses an LLVM IR module to produce a callgraph in
     2  // Graphviz DOT format.
     3  package ir_test
     4  
     5  import (
     6  	"fmt"
     7  	"strings"
     8  
     9  	"github.com/llir/llvm/asm"
    10  	"github.com/llir/llvm/ir"
    11  )
    12  
    13  func Example_callgraph() {
    14  	// Parse LLVM IR assembly file.
    15  	m, err := asm.ParseFile("testdata/eval.ll")
    16  	if err != nil {
    17  		panic(err)
    18  	}
    19  	// Produce callgraph of module.
    20  	callgraph := genCallgraph(m)
    21  	// Output callgraph in Graphviz DOT format.
    22  	fmt.Println(callgraph)
    23  
    24  	// Output:
    25  	//
    26  	// digraph {
    27  	// 	"@add"
    28  	// 	"@sub"
    29  	// 	"@f"
    30  	// 	"@f" -> "@printf"
    31  	// 	"@main"
    32  	// 	"@main" -> "@add"
    33  	// 	"@main" -> "@sub"
    34  	// 	"@main" -> "@f"
    35  	// 	"@printf"
    36  	// }
    37  }
    38  
    39  // genCallgraph returns the callgraph in Graphviz DOT format of the given LLVM
    40  // IR module.
    41  func genCallgraph(m *ir.Module) string {
    42  	buf := &strings.Builder{}
    43  	buf.WriteString("digraph {\n")
    44  	// For each function of the module.
    45  	for _, f := range m.Funcs {
    46  		// Add caller node.
    47  		caller := f.Ident()
    48  		fmt.Fprintf(buf, "\t%q\n", caller)
    49  		// For each basic block of the function.
    50  		for _, block := range f.Blocks {
    51  			// For each non-branching instruction of the basic block.
    52  			for _, inst := range block.Insts {
    53  				// Type switch on instruction to find call instructions.
    54  				switch inst := inst.(type) {
    55  				case *ir.InstCall:
    56  					callee := inst.Callee.Ident()
    57  					// Add edges from caller to callee.
    58  					fmt.Fprintf(buf, "\t%q -> %q\n", caller, callee)
    59  				}
    60  			}
    61  			// Terminator of basic block.
    62  			switch term := block.Term.(type) {
    63  			case *ir.TermRet:
    64  				// do something.
    65  				_ = term
    66  			}
    67  		}
    68  	}
    69  	buf.WriteString("}")
    70  	return buf.String()
    71  }