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 }