Beispiel #1
0
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)
}
Beispiel #2
0
// 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
}
Beispiel #3
0
// 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
}