// expand attempts to locate and return the definition of the provided // identifier expression. The assignment statement defining the identifier is // removed from the statement list of the basic block. func expand(bb BasicBlock, ident ast.Expr) (ast.Expr, error) { id, ok := ident.(*ast.Ident) if !ok { return nil, errutil.Newf("unable to expand expression; expected identifier, got %T", ident) } var stmts []ast.Stmt var expr ast.Expr for _, stmt := range bb.Stmts() { switch stmt := stmt.(type) { case *ast.AssignStmt: if sameIdent(stmt.Lhs, id) { if len(stmt.Rhs) != 1 { return nil, errutil.Newf("invalid right-hand side; expected length 1, got %d", len(stmt.Rhs)) } expr = stmt.Rhs[0] // TODO: Verify if the identifier is used by any other statement or // terminator instruction before removing it; this includes the use // within other basic blocks. // Remove the variable definition of the right-hand side expression // by not appending the assignment statement to the statement list. continue } } stmts = append(stmts, stmt) } if expr == nil { return nil, errutil.Newf("unable to expand definition of identifier %q", id.Name) } bb.SetStmts(stmts) return expr, nil }
// NewGetElementPtrInst returns a new getelementptr instruction based on the // given element type, address and element indices. func NewGetElementPtrInst(elem, srcAddrType, srcAddr, elemIndices interface{}) (*instruction.GetElementPtr, error) { if elem, ok := elem.(types.Type); ok { srcAddr, err := NewValue(srcAddrType, srcAddr) if err != nil { return nil, errutil.Err(err) } var indices []value.Value switch elemIndices := elemIndices.(type) { case []value.Value: indices = elemIndices case nil: default: panic(fmt.Sprintf("support for element indices type %T not yet implemented", elemIndices)) } // Validate that elem is of identical type as the element type of srcAddr. srcAddrType, ok := srcAddr.Type().(*types.Pointer) if !ok { return nil, errutil.Newf("invalid source address pointer type; expected *types.Pointer, got %T", srcAddr.Type()) } if !types.Equal(elem, srcAddrType.Elem()) { return nil, errutil.Newf("type mismatch between element type (%v) and source address element type (%v)", elem, srcAddrType.Elem()) } return instruction.NewGetElementPtr(srcAddr, indices) } return nil, errutil.Newf("invalid operand type; expected types.Type, got %T", elem) }
// NewBasicBlock returns a new basic block based on the given name, non- // terminating instructions and terminator. func NewBasicBlock(nameToken, insts, term interface{}) (*ir.BasicBlock, error) { // Get label name. var name string switch nameToken := nameToken.(type) { case *token.Token: // Strip ":" suffix. s, err := stripLabelSuffix(nameToken.Lit) if err != nil { return nil, errutil.Err(err) } name = s case nil: // Unnamed basic block. default: return nil, errutil.Newf("invalid basic block name type; expected *token.Token, got %T", nameToken) } if term, ok := term.(instruction.Terminator); ok { switch insts := insts.(type) { case []instruction.Instruction: block := ir.NewBasicBlock(name) block.SetInsts(insts) block.SetTerm(term) return block, nil case nil: block := ir.NewBasicBlock(name) block.SetTerm(term) return block, nil default: return nil, errutil.Newf("invalid non-terminating instructions type; expected []instruction.Instruction, got %T", insts) } } return nil, errutil.Newf("invalid terminator type; expected instruction.Terminator, got %T", term) }
// NewSubGraph returns a new subgraph based on graph with a dedicated entry and // exit node. The entry and exit nodes are identified using the node "label" // attribute, e.g. // // digraph if { // A->B [label="true"] // A->C [label="false"] // B->C // A [label="entry"] // B // C [label="exit"] // } func NewSubGraph(graph *dot.Graph) (*SubGraph, error) { sub := &SubGraph{Graph: graph} // Locate entry and exit nodes. var hasEntry, hasExit bool for _, node := range graph.Nodes.Nodes { label, ok := node.Attrs["label"] if !ok { continue } switch label { case "entry": if hasEntry { return nil, errutil.Newf(`redefinition of node with "entry" label; previous node %q, new node %q`, sub.entry, node.Name) } sub.entry = node.Name hasEntry = true case "exit": if hasExit { return nil, errutil.Newf(`redefinition of node with "exit" label; previous node %q, new node %q`, sub.exit, node.Name) } sub.exit = node.Name hasExit = true } } if !hasEntry { return nil, errutil.New(`unable to locate node with "entry" label`) } if !hasExit { return nil, errutil.New(`unable to locate node with "exit" label`) } return sub, nil }
// candidates locates node pair candidates for an isomorphism of sub in graph // which starts at the entry node. func candidates(graph *dot.Graph, entry string, sub *graphs.SubGraph) (*equation, error) { // Sanity checks. g, ok := graph.Nodes.Lookup[entry] if !ok { return nil, errutil.Newf("unable to locate entry node %q in graph", entry) } s, ok := sub.Nodes.Lookup[sub.Entry()] if !ok { panic(fmt.Sprintf("unable to locate entry node %q in sub", sub.Entry())) } if !isPotential(g, s, sub) { return nil, errutil.Newf("invalid entry node candidate %q; expected %d successors, got %d", g.Name, len(s.Succs), len(g.Succs)) } // Locate candidate node pairs. eq := &equation{ c: make(map[string]map[string]bool), m: make(map[string]string), } eq.findCandidates(g, s, sub) if len(eq.c) != len(sub.Nodes.Nodes) { return nil, errutil.Newf("incomplete candidate mapping; expected %d map entites, got %d", len(sub.Nodes.Nodes), len(eq.c)) } return eq, nil }
// NewArrayType returns a new array type based on the given element type and // length. func NewArrayType(elem, lbracket, length, rbracket interface{}) (*ast.ArrayType, error) { len, ok := length.(int) if !ok { return nil, errutil.Newf("invalid array length type; %T", length) } var lbrack, rbrack int switch lbracket := lbracket.(type) { case *gocctoken.Token: lbrack = lbracket.Offset case int: lbrack = lbracket default: return nil, errutil.Newf("invalid left-bracket type; expectd *gocctoken.Token or int, got %T", lbracket) } switch rbracket := rbracket.(type) { case *gocctoken.Token: rbrack = rbracket.Offset case int: rbrack = rbracket default: return nil, errutil.Newf("invalid right-bracket type; expectd *gocctoken.Token or int, got %T", rbracket) } elemType, err := NewType(elem) if err != nil { return nil, errutil.Newf("invalid array element type; %v", err) } return &ast.ArrayType{Elem: elemType, Lbracket: lbrack, Len: len, Rbracket: rbrack}, nil }
// createListPrim creates a list primitive containing a slice of Go statements // based on the identified subgraph, its node pair mapping and its basic blocks. // The new control flow primitive conceptually represents a basic block with the // given name. // // Contents of "list.dot": // // digraph list { // entry [label="entry"] // exit [label="exit"] // entry->exit // } func createListPrim(m map[string]string, bbs map[string]BasicBlock, newName string) (*primitive, error) { // Locate graph nodes. nameEntry, ok := m["entry"] if !ok { return nil, errutil.New(`unable to locate node pair for sub node "entry"`) } nameExit, ok := m["exit"] if !ok { return nil, errutil.New(`unable to locate node pair for sub node "exit"`) } bbEntry, ok := bbs[nameEntry] if !ok { return nil, errutil.Newf("unable to locate basic block %q", nameEntry) } bbExit, ok := bbs[nameExit] if !ok { return nil, errutil.Newf("unable to locate basic block %q", nameExit) } // Create and return new primitive. // // entry // exit stmts := append(bbEntry.Stmts(), bbExit.Stmts()...) prim := &primitive{ name: newName, stmts: stmts, term: bbExit.Term(), } return prim, nil }
// getBBName returns the name (or ID if unnamed) of a basic block. func getBBName(v llvm.Value) (string, error) { if !v.IsBasicBlock() { return "", errutil.Newf("invalid value type; expected basic block, got %v", v.Type()) } // Locate the name of a named basic block. if name := v.Name(); len(name) > 0 { return name, nil } // Locate the ID of an unnamed basic block by parsing the value dump in // search for its basic block label. // // Example value dump: // 0: // br i1 true, label %1, label %2 // // Each basic block is expected to have a label, which requires the // "unnamed.patch" to be applied to the llvm.org/llvm/bindings/go/llvm code // base. s, err := hackDump(v) if err != nil { return "", errutil.Err(err) } tokens := lexer.ParseString(s) if len(tokens) < 1 { return "", errutil.Newf("unable to locate basic block label in %q", s) } tok := tokens[0] if tok.Kind != token.Label { return "", errutil.Newf("invalid token; expected %v, got %v", token.Label, tok.Kind) } return tok.Val, nil }
// NewFunc returns a new function based on the given result type, function name, // and function parameters. func NewFunc(result, gname, params interface{}) (*ir.Function, error) { if result, ok := result.(types.Type); ok { name, err := getGlobalName(gname) if err != nil { return nil, errutil.Err(err) } var sig *types.Func switch params := params.(type) { case *Params: sig, err = types.NewFunc(result, params.params, params.variadic) if err != nil { return nil, errutil.Err(err) } case nil: sig, err = types.NewFunc(result, nil, false) if err != nil { return nil, errutil.Err(err) } default: return nil, errutil.Newf("invalid function parameters specifier type; expected *Params, got %T", params) } return ir.NewFunction(name, sig), nil } return nil, errutil.Newf("invalid function result type; expected types.Type, got %T", result) }
// getCmpPred parses the provided comparison instruction and returns a Go token // equivalent of the comparison predicate. // // Syntax: // <result> = icmp <pred> <type> <op1>, <op2> func getCmpPred(inst llvm.Value) (token.Token, error) { // Parse and validate tokens. tokens, err := getTokens(inst) if err != nil { return 0, errutil.Err(err) } if len(tokens) < 4 { return 0, errutil.Newf("unable to parse comparison instruction; expected >= 4 tokens, got %d", len(tokens)) } // TODO: Handle signed and unsigned predicates separately. switch pred := tokens[3]; pred.Kind { // Int predicates. case lltoken.KwEq: // eq: equal return token.EQL, nil // == case lltoken.KwNe: // ne: not equal return token.NEQ, nil // != case lltoken.KwUgt: // ugt: unsigned greater than return token.GTR, nil // > case lltoken.KwUge: // uge: unsigned greater or equal return token.GEQ, nil // >= case lltoken.KwUlt: // ult: unsigned less than return token.LSS, nil // < case lltoken.KwUle: // ule: unsigned less or equal return token.LEQ, nil // <= case lltoken.KwSgt: // sgt: signed greater than return token.GTR, nil // > case lltoken.KwSge: // sge: signed greater or equal return token.GEQ, nil // >= case lltoken.KwSlt: // slt: signed less than return token.LSS, nil // < case lltoken.KwSle: // sle: signed less or equal return token.LEQ, nil // <= // Float predicates. case lltoken.KwOeq: // oeq: ordered and equal return token.EQL, nil // == case lltoken.KwOgt: // ogt: ordered and greater than return token.GTR, nil // > case lltoken.KwOge: // oge: ordered and greater than or equal return token.GEQ, nil // >= case lltoken.KwOlt: // olt: ordered and less than return token.LSS, nil // < case lltoken.KwOle: // ole: ordered and less than or equal return token.LEQ, nil // <= case lltoken.KwOne: // one: ordered and not equal return token.NEQ, nil // != case lltoken.KwOrd: // ord: ordered (no nans) return 0, errutil.Newf(`support for the floating point comparison predicate "ord" not yet implemented`) case lltoken.KwUeq: // ueq: unordered or equal return token.EQL, nil // == case lltoken.KwUne: // une: unordered or not equal return token.NEQ, nil // != case lltoken.KwUno: // uno: unordered (either nans) return 0, errutil.Newf(`support for the floating point comparison predicate "uno" not yet implemented`) default: return 0, errutil.Newf("invalid token; expected comparison predicate, got %q", pred) } }
// AppendIncoming appends inc to the incoming values list. func AppendIncoming(list, inc interface{}) ([]*Incoming, error) { if list, ok := list.([]*Incoming); ok { if inc, ok := inc.(*Incoming); ok { return append(list, inc), nil } return nil, errutil.Newf("invalid incoming values list incoming value type; expected *Incoming, got %T", inc) } return nil, errutil.Newf("invalid incoming values list type; expected []*Incoming, got %T", list) }
// AppendValue appends val to the value list. func AppendValue(list, val interface{}) ([]value.Value, error) { if list, ok := list.([]value.Value); ok { if val, ok := val.(value.Value); ok { return append(list, val), nil } return nil, errutil.Newf("invalid value list value type; expected value.Value, got %T", val) } return nil, errutil.Newf("invalid value list type; expected []value.Value, got %T", list) }
// AppendParam appends param to the function parameter list. func AppendParam(list, param interface{}) ([]*types.Param, error) { if list, ok := list.([]*types.Param); ok { if param, ok := param.(*types.Param); ok { return append(list, param), nil } return nil, errutil.Newf("invalid function parameter list parameter type; expected *types.Param, got %T", param) } return nil, errutil.Newf("invalid function parameter list type; expected []*types.Param, got %T", list) }
// 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) }
// AppendTopLevelDecl appends decl to the top-level declaration list. func AppendTopLevelDecl(list, decl interface{}) ([]TopLevelDecl, error) { if list, ok := list.([]TopLevelDecl); ok { if decl, ok := decl.(TopLevelDecl); ok { return append(list, decl), nil } return nil, errutil.Newf("invalid top-level declaration list top-level declaration type; expected TopLevelDecl, got %T", decl) } return nil, errutil.Newf("invalid top-level declaration list type; expected []TopLevelDecl, got %T", list) }
// AppendBasicBlock appends block to the basic block list. func AppendBasicBlock(list, block interface{}) ([]*ir.BasicBlock, error) { if list, ok := list.([]*ir.BasicBlock); ok { if block, ok := block.(*ir.BasicBlock); ok { return append(list, block), nil } return nil, errutil.Newf("invalid basic block list basic block type; expected *ir.BasicBlock, got %T", block) } return nil, errutil.Newf("invalid basic block list type; expected []*ir.BasicBlock, got %T", list) }
// AppendInstruction appends inst to the instruction list. func AppendInstruction(list, inst interface{}) ([]instruction.Instruction, error) { if list, ok := list.([]instruction.Instruction); ok { if inst, ok := inst.(instruction.Instruction); ok { return append(list, inst), nil } return nil, errutil.Newf("invalid instruction list instruction type; expected instruction.Instruction, got %T", inst) } return nil, errutil.Newf("invalid instruction list type; expected []instruction.Instruction, got %T", list) }
// AppendField appends typ to the structure field list. func AppendField(list, typ interface{}) ([]types.Type, error) { if list, ok := list.([]types.Type); ok { if typ, ok := typ.(types.Type); ok { return append(list, typ), nil } return nil, errutil.Newf("invalid structure field list field type; expected types.Type, got %T", typ) } return nil, errutil.Newf("invalid structure field list type; expected []types.Type, got %T", list) }
// createPostLoopPrim creates a post-test loop primitive based on the identified // subgraph, its node pair mapping and its basic blocks. The new control flow // primitive conceptually represents a basic block with the given name. // // Contents of "post_loop.dot": // // digraph post_loop { // cond [label="entry"] // exit [label="exit"] // cond->cond [label="true"] // cond->exit [label="false"] // } func createPostLoopPrim(m map[string]string, bbs map[string]BasicBlock, newName string) (*primitive, error) { // Locate graph nodes. nameCond, ok := m["cond"] if !ok { return nil, errutil.New(`unable to locate node pair for sub node "cond"`) } nameExit, ok := m["exit"] if !ok { return nil, errutil.New(`unable to locate node pair for sub node "exit"`) } bbCond, ok := bbs[nameCond] if !ok { return nil, errutil.Newf("unable to locate basic block %q", nameCond) } bbExit, ok := bbs[nameExit] if !ok { return nil, errutil.Newf("unable to locate basic block %q", nameExit) } // Create and return new primitive. // // for { // cond_stmts // if !cond { // break // } // } // exit // Create if-statement. cond, _, _, err := getBrCond(bbCond.Term()) if err != nil { return nil, errutil.Err(err) } ifStmt := &ast.IfStmt{ Cond: &ast.UnaryExpr{Op: token.NOT, X: cond}, // negate condition Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.BREAK}}}, } // Create for-loop. body := bbCond.Stmts() body = append(body, ifStmt) forStmt := &ast.ForStmt{ Body: &ast.BlockStmt{List: body}, } // Create primitive. stmts := []ast.Stmt{forStmt} stmts = append(stmts, bbExit.Stmts()...) prim := &primitive{ name: newName, stmts: stmts, term: bbExit.Term(), } return prim, nil }
// AppendBlockItem appends item to the block item list, based on the following // production rule. // // BlockItemList // : BlockItemList BlockItem // ; func AppendBlockItem(list, item interface{}) ([]ast.BlockItem, error) { lst, ok := list.([]ast.BlockItem) if !ok { return nil, errutil.Newf("invalid block item list type; expected []ast.BlockItem, got %T", list) } if item, ok := item.(ast.BlockItem); ok { return append(lst, item), nil } return nil, errutil.Newf("invalid block item list block item type; expected ast.BlockItem, got %T", item) }
// AppendDecl appends decl to the declaration list, based on the following // production rule. // // DeclList // : DeclList Decl // ; func AppendDecl(list, decl interface{}) ([]ast.Decl, error) { lst, ok := list.([]ast.Decl) if !ok { return nil, errutil.Newf("invalid declaration list type; expected []ast.Decl, got %T", list) } if decl, ok := decl.(ast.Decl); ok { return append(lst, decl), nil } return nil, errutil.Newf("invalid declaration list declaration type; expected ast.Decl, got %T", decl) }
// AppendParam appends parameter to the parameter list, based on the following // production rule. // // ParamList // : ParamList "," Param // ; func AppendParam(list, param interface{}) ([]*ast.VarDecl, error) { lst, ok := list.([]*ast.VarDecl) if !ok { return nil, errutil.Newf("invalid parameter list type; expected []*ast.VarDecl, got %T", list) } if param, ok := param.(*ast.VarDecl); ok { return append(lst, param), nil } return nil, errutil.Newf("invalid parameter list parameter type; expected *ast.VarDecl, got %T", param) }
// AppendExpr appends x to the expression list, based on the following // production rule. // // ExprList // : ExprList "," Expr // ; func AppendExpr(list, x interface{}) ([]ast.Expr, error) { lst, ok := list.([]ast.Expr) if !ok { return nil, errutil.Newf("invalid expression list type; expected []ast.Expr, got %T", list) } if x, ok := x.(ast.Expr); ok { return append(lst, x), nil } return nil, errutil.Newf("invalid expression list expression type; expected ast.Expr, got %T", x) }
// createIfPrim creates an if-statement primitive based on the identified // subgraph, its node pair mapping and its basic blocks. The new control flow // primitive conceptually represents a basic block with the given name. // // Contents of "if.dot": // // digraph if { // cond [label="entry"] // body // exit [label="exit"] // cond->body [label="true"] // cond->exit [label="false"] // body->exit // } func createIfPrim(m map[string]string, bbs map[string]BasicBlock, newName string) (*primitive, error) { // Locate graph nodes. nameCond, ok := m["cond"] if !ok { return nil, errutil.New(`unable to locate node pair for sub node "cond"`) } nameBody, ok := m["body"] if !ok { return nil, errutil.New(`unable to locate node pair for sub node "body"`) } nameExit, ok := m["exit"] if !ok { return nil, errutil.New(`unable to locate node pair for sub node "exit"`) } bbCond, ok := bbs[nameCond] if !ok { return nil, errutil.Newf("unable to locate basic block %q", nameCond) } bbBody, ok := bbs[nameBody] if !ok { return nil, errutil.Newf("unable to locate basic block %q", nameBody) } bbExit, ok := bbs[nameExit] if !ok { return nil, errutil.Newf("unable to locate basic block %q", nameExit) } // Create and return new primitive. // // cond_stmts // if cond { // body // } // exit // Create if-statement. cond, _, _, err := getBrCond(bbCond.Term()) if err != nil { return nil, errutil.Err(err) } ifStmt := &ast.IfStmt{ Cond: cond, Body: &ast.BlockStmt{List: bbBody.Stmts()}, } // Create primitive. stmts := append(bbCond.Stmts(), ifStmt) stmts = append(stmts, bbExit.Stmts()...) prim := &primitive{ name: newName, stmts: stmts, term: bbExit.Term(), } return prim, nil }
// NewSelect returns a new select instruction based on the given selection // condition, and operands. // // Pre-condition: cond is of boolean or boolean vector type. x and y are of // identical types. func NewSelect(cond, x, y value.Value) (*Select, error) { // Validate that cond is of boolean or boolean vector type. if !types.IsBools(cond.Type()) { return nil, errutil.Newf("invalid selection condition type; expected boolean or boolean vector, got %v", cond.Type()) } // Validate that x and y are of identical types. if !types.Equal(x.Type(), y.Type()) { return nil, errutil.Newf("type mismatch between x (%v) and y (%v)", x.Type(), y.Type()) } return &Select{cond: cond, x: x, y: y}, nil }
// unquote interprets s as a double-quoted LLVM IR string literal, returning the // string value that s quotes. func unquote(s string) (string, error) { if !strings.HasPrefix(s, `"`) { return "", errutil.Newf(`invalid prefix of quoted string %q; expected '"'`, s) } s = s[1:] if !strings.HasSuffix(s, `"`) { return "", errutil.Newf(`invalid suffix of quoted string %q; expected '"'`, s) } s = s[:len(s)-1] return enc.Unescape(s), nil }
// NewArrayDecl returns a new array declaration node, based on the following // production rule. // // ArrayDecl // : BasicType ident "[" int_lit "]" // ; func NewArrayDecl(elem, name, lbracket, length, rbracket interface{}) (*ast.VarDecl, error) { typ, err := NewArrayType(elem, lbracket, length, rbracket) if err != nil { return nil, errutil.Newf("invalid array type; %v", err) } ident, err := NewIdent(name) if err != nil { return nil, errutil.Newf("invalid array declaration identifier; %v", err) } return &ast.VarDecl{VarType: typ, VarName: ident}, nil }
// NewScalarDecl returns a new scalar declaration node, based on the following // production rule. // // ScalarDecl // : TypeName ident // ; func NewScalarDecl(typ, name interface{}) (*ast.VarDecl, error) { scalarType, err := NewType(typ) if err != nil { return nil, errutil.Newf("invalid scalar type; %v", err) } ident, err := NewIdent(name) if err != nil { return nil, errutil.Newf("invalid scalar declaration identifier; %v", err) } return &ast.VarDecl{VarType: scalarType, VarName: ident}, nil }
// NewFuncDef returns a new function definition based on the given function // header and body. func NewFuncDef(fn, blocks interface{}) (*ir.Function, error) { if fn, ok := fn.(*ir.Function); ok { if blocks, ok := blocks.([]*ir.BasicBlock); ok { if err := fn.SetBlocks(blocks); err != nil { return nil, errutil.Err(err) } return fn, nil } return nil, errutil.Newf("invalid function body type; expected []*ir.BasicBlock, got %T", blocks) } return nil, errutil.Newf("invalid function header type; expected *ir.Function, got %T", fn) }
// NewStore returns a new store instruction based on the given source value and // destination address. // // Pre-condition: // 1. dstAddr is of pointer type // 2. src is of identical type as the element type of dstAddr func NewStore(src, dstAddr value.Value) (*Store, error) { // Validate that dstAddr is of pointer type. dstAddrType, ok := dstAddr.Type().(*types.Pointer) if !ok { return nil, errutil.Newf("invalid destination address pointer type; expected *types.Pointer, got %T", dstAddr.Type()) } // Validate that src is of identical type as the element type of dstAddr. if !types.Equal(src.Type(), dstAddrType.Elem()) { return nil, errutil.Newf("type mismatch between source value (%v) and destination address element type (%v)", src.Type(), dstAddrType.Elem()) } return &Store{src: src, dstAddr: dstAddr}, nil }