Example #1
0
// parseFunc parses the given function and attempts to construct an equivalent
// Go function declaration AST node.
func parseFunc(graph *dot.Graph, module llvm.Module, funcName string, hprims []*xprimitive.Primitive) (*ast.FuncDecl, error) {
	llFunc := module.NamedFunction(funcName)
	if llFunc.IsNil() {
		return nil, errutil.Newf("unable to locate function %q", funcName)
	}
	if llFunc.IsDeclaration() {
		return nil, errutil.Newf("unable to create AST for %q; expected function definition, got function declaration (e.g. no body)", funcName)
	}

	// Parse each basic block.
	bbs := make(map[string]BasicBlock)
	for _, llBB := range llFunc.BasicBlocks() {
		bb, err := parseBasicBlock(llBB)
		if err != nil {
			return nil, err
		}
		bbs[bb.Name()] = bb
		if flagVerbose && !flagQuiet {
			printBB(bb)
		}
	}

	// Replace PHI instructions with assignment statements in the appropriate
	// basic blocks.
	for _, bb := range bbs {
		block, ok := bb.(*basicBlock)
		if !ok {
			return nil, errutil.Newf("invalid basic block type; expected *basicBlock, got %T", bb)
		}
		for ident, defs := range block.phis {
			for _, def := range defs {
				assign := &ast.AssignStmt{
					Lhs: []ast.Expr{newIdent(ident)},
					Tok: token.ASSIGN,
					Rhs: []ast.Expr{def.expr},
				}
				bbSrc := bbs[def.bb]
				stmts := bbSrc.Stmts()
				stmts = append(stmts, assign)
				bbSrc.SetStmts(stmts)
			}
		}
	}

	// Perform control flow analysis.
	body, err := restructure(graph, bbs, hprims)
	if err != nil {
		return nil, errutil.Err(err)
	}
	sig := &ast.FuncType{
		Params: &ast.FieldList{},
	}
	if funcName != "main" {
		// TODO: Implement parsing of function signature.
	}
	return createFunc(funcName, sig, body)
}
Example #2
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
}