// 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) }
// 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 }