func ExampleNewGraph() { g := dot.NewGraph() g.SetName("G") g.SetDir(true) g.AddNode("G", "Hello", nil) g.AddNode("G", "World", nil) g.AddEdge("Hello", "World", true, nil) s := g.String() fmt.Println(s) }
// GetInterval returns the interval in g with the entry node h. The boolean // value is false if no interval with two or more nodes could be located. func GetInterval(g *dot.Graph, h *dot.Node) (i *Interval, ok bool) { // Create a new interval with the entry node h. i = &Interval{ Graph: dot.NewGraph(), head: h, } i.Directed = true i.Name = g.Name + "_interval_" + h.Name // TODO: Add dot.Attrs{"label": "entry"}? i.AddNode(i.Name, h.Name, nil) // Add each node dominated by h to the interval. for _, n := range g.Nodes.Nodes { if h == n { continue } if h.Dominates(n) { i.AddNode(i.Name, n.Name, nil) ok = true } } if !ok { return nil, false } // Add the original edges between the nodes of the interval. for _, m := range i.Nodes.Nodes { n, ok := g.Nodes.Lookup[m.Name] if !ok { log.Fatalf("unable to locate interval node %q in graph", m.Name) } for _, e := range g.Edges.SrcToDsts[n.Name] { if _, ok := i.Nodes.Lookup[e.Src]; !ok { continue } if _, ok := i.Nodes.Lookup[e.Dst]; !ok { continue } i.AddEdge(e.Src, e.Dst, true, e.Attrs) } } return i, true }
// createCFG generates a control flow graph for the given function using one // node per basic block. func createCFG(module llvm.Module, funcName string) (*dot.Graph, error) { f := module.NamedFunction(funcName) if f.IsNil() { return nil, errutil.Newf("unable to locate function %q", funcName) } if f.IsDeclaration() { return nil, errutil.Newf("unable to generate CFG for %q; expected function definition, got function declaration (e.g. no body)", funcName) } // Create a new directed graph. graph := dot.NewGraph() graph.SetDir(true) graph.SetName(funcName) // Populate the graph with one node per basic block. for _, bb := range f.BasicBlocks() { // Add node (i.e. basic block) to the graph. bbName, err := getBBName(bb.AsValue()) if err != nil { return nil, errutil.Err(err) } if bb == f.EntryBasicBlock() { attrs := map[string]string{"label": "entry"} graph.AddNode(funcName, bbName, attrs) } else { graph.AddNode(funcName, bbName, nil) } // Add edges from node (i.e. target basic blocks) to the graph. term := bb.LastInstruction() nops := term.OperandsCount() switch opcode := term.InstructionOpcode(); opcode { case llvm.Ret: // exit node. // ret <type> <value> // ret void case llvm.Br: switch nops { case 1: // unconditional branch. // br label <target> target := term.Operand(0) targetName, err := getBBName(target) if err != nil { return nil, errutil.Err(err) } graph.AddEdge(bbName, targetName, true, nil) case 3: // 2-way conditional branch. // br i1 <cond>, label <target_true>, label <target_false> // NOTE: The LLVM library has a peculiar way of ordering the operands: // term.Operand(0) refers to the 1st operand // term.Operand(1) refers to the 3rd operand // term.Operand(2) refers to the 2nd operand // TODO: Make the order logical with the pure Go implementation of // LLVM IR. targetTrue, targetFalse := term.Operand(2), term.Operand(1) targetTrueName, err := getBBName(targetTrue) if err != nil { return nil, errutil.Err(err) } targetFalseName, err := getBBName(targetFalse) if err != nil { return nil, errutil.Err(err) } attrs := map[string]string{"label": "false"} graph.AddEdge(bbName, targetFalseName, true, attrs) attrs = map[string]string{"label": "true"} graph.AddEdge(bbName, targetTrueName, true, attrs) default: return nil, errutil.Newf("invalid number of operands (%d) for br instruction", nops) } case llvm.Switch: // n-way conditional branch. // switch <type> <value>, label <default_target> [ // <type> <case1>, label <case1_target> // <type> <case2>, label <case2_target> // ... // ] if nops < 2 { return nil, errutil.Newf("invalid number of operands (%d) for switch instruction", nops) } // Default branch. targetDefault := term.Operand(1) targetDefaultName, err := getBBName(targetDefault) if err != nil { return nil, errutil.Err(err) } attrs := map[string]string{"label": "default"} graph.AddEdge(bbName, targetDefaultName, true, attrs) // Case branches. for i := 3; i < nops; i += 2 { // Case branch. targetCase := term.Operand(i) targetCaseName, err := getBBName(targetCase) if err != nil { return nil, errutil.Err(err) } caseID := (i - 3) / 2 label := fmt.Sprintf("case %d", caseID) attrs := map[string]string{"label": label} graph.AddEdge(bbName, targetCaseName, true, attrs) } case llvm.Unreachable: // unreachable node. // unreachable default: // TODO: Implement support for: // - indirectbr // - invoke // - resume panic(fmt.Sprintf("not yet implemented; support for terminator %v", opcode)) } } return graph, nil }