// parseInst converts the provided LLVM IR instruction into an equivalent Go AST // node (a statement). func parseInst(inst llvm.Value) (ast.Stmt, error) { // TODO: Remove debug output. if flagVerbose { fmt.Println("parseInst:") fmt.Println(" nops:", inst.OperandsCount()) inst.Dump() fmt.Println() } // Assignment operation. // %foo = ... opcode := inst.InstructionOpcode() if _, err := getResult(inst); err == nil { // Binary Operations switch opcode { case llvm.Add, llvm.FAdd: return parseBinOp(inst, token.ADD) case llvm.Sub, llvm.FSub: return parseBinOp(inst, token.SUB) case llvm.Mul, llvm.FMul: return parseBinOp(inst, token.MUL) case llvm.UDiv, llvm.SDiv, llvm.FDiv: // TODO: Handle signed and unsigned div separately. return parseBinOp(inst, token.QUO) case llvm.URem, llvm.SRem, llvm.FRem: // TODO: Handle signed and unsigned mod separately. return parseBinOp(inst, token.REM) // Bitwise Binary Operations case llvm.Shl: return parseBinOp(inst, token.SHL) case llvm.LShr, llvm.AShr: // TODO: Handle logical and arithmetic shift right separately. return parseBinOp(inst, token.SHR) case llvm.And: return parseBinOp(inst, token.AND) case llvm.Or: return parseBinOp(inst, token.OR) case llvm.Xor: return parseBinOp(inst, token.XOR) // Other Operators case llvm.ICmp, llvm.FCmp: pred, err := getCmpPred(inst) if err != nil { return nil, errutil.Err(err) } return parseBinOp(inst, pred) } } return nil, errutil.Newf("support for LLVM IR instruction %q not yet implemented", prettyOpcode(opcode)) }
// parseOperand converts the provided LLVM IR operand into an equivalent Go AST // expression node (a basic literal, a composite literal or an identifier). // // Syntax: // i32 1 // %foo = ... func parseOperand(op llvm.Value) (ast.Expr, error) { // TODO: Support *BasicLit, *CompositeLit. // Parse and validate tokens. tokens, err := getTokens(op) if err != nil { return nil, err } if len(tokens) < 2 { // TODO: Remove debug output. op.Dump() return nil, errutil.Newf("unable to parse operand; expected 2 >= tokens, got %d", len(tokens)) } // TODO: Add support for operand of other types than int. // TODO: Parse type. // Create and return a constant operand. // i32 42 if tokens[0].Kind == lltoken.Type { switch tok := tokens[1]; tok.Kind { case lltoken.Int: return &ast.BasicLit{Kind: token.INT, Value: tok.Val}, nil case lltoken.LocalVar: return getIdent(tok) default: return nil, errutil.Newf("support for LLVM IR token kind %v not yet implemented", tok.Kind) } } // Create and return a variable operand. // %foo = ... if tokens[1].Kind == lltoken.Equal { switch tok := tokens[0]; tok.Kind { case lltoken.LocalVar, lltoken.LocalID: // %foo // %42 return getIdent(tok) default: return nil, errutil.Newf("support for LLVM IR token kind %v not yet implemented", tok.Kind) } } return nil, errutil.New("support for LLVM IR operand not yet implemented") }
// hackDump returns the value dump as a string. func hackDump(v llvm.Value) (string, error) { // Open temp file. // TODO: Use an in-memory file instead of /tmp/x. fd, err := unix.Open("/tmp/x", unix.O_WRONLY|unix.O_TRUNC|unix.O_CREAT, 0644) if err != nil { return "", errutil.Err(err) } // Store original stderr. stderr, err := unix.Dup(2) if err != nil { return "", errutil.Err(err) } // Capture stderr and redirect its output to the temp file. err = unix.Dup2(fd, 2) if err != nil { return "", errutil.Err(err) } err = unix.Close(fd) if err != nil { return "", errutil.Err(err) } // Dump value. v.Dump() C.fflush_stderr() // Restore stderr. err = unix.Dup2(stderr, 2) if err != nil { return "", errutil.Err(err) } err = unix.Close(stderr) if err != nil { return "", errutil.Err(err) } // Return content of temp file. buf, err := ioutil.ReadFile("/tmp/x") if err != nil { return "", errutil.Err(err) } return string(buf), nil }
// getBrCond parses the provided branch instruction and returns its condition. // // Syntax: // br i1 <cond>, label <target_true>, label <target_false> func getBrCond(term llvm.Value) (cond ast.Expr, targetTrue, targetFalse string, err error) { // Parse and validate tokens. tokens, err := getTokens(term) if err != nil { return nil, "", "", err } if len(tokens) != 10 { // TODO: Remove debug output. term.Dump() return nil, "", "", errutil.Newf("unable to parse conditional branch instruction; expected 10 tokens, got %d", len(tokens)) } // Create and return the condition. switch tok := tokens[2]; tok.Kind { case lltoken.KwTrue, lltoken.KwFalse, lltoken.LocalVar, lltoken.LocalID: // true // false // %foo // %42 ident, err := getIdent(tok) if err != nil { return nil, "", "", errutil.Err(err) } return ident, tokens[5].Val, tokens[8].Val, nil case lltoken.Int: // 1 // 0 switch tok.Val { case "0": return newIdent("false"), tokens[5].Val, tokens[8].Val, nil case "1": return newIdent("true"), tokens[5].Val, tokens[8].Val, nil default: return nil, "", "", errutil.Newf("invalid integer value; expected boolean, got %q", tok.Val) } default: return nil, "", "", errutil.Newf("support for LLVM IR token kind %v not yet implemented", tok.Kind) } }
// parseRetInst converts the provided LLVM IR ret instruction into an equivalent // Go return statement. // // Syntax: // ret void // ret <type> <val> func parseRetInst(inst llvm.Value) (*ast.ReturnStmt, error) { // TODO: Make more robust by using proper parsing instead of relying on // tokens. The current approach is used for a proof of concept and would fail // for composite literals. This TODO applies to the use of tokens in all // functions. // Parse and validate tokens. tokens, err := getTokens(inst) if err != nil { return nil, err } if len(tokens) < 4 { // TODO: Remove debug output. inst.Dump() return nil, errutil.Newf("unable to parse return instruction; expected >= 4 tokens, got %d", len(tokens)) } typ := tokens[1] if typ.Kind != lltoken.Type { return nil, errutil.Newf(`invalid return instruction; expected type token, got %q`, typ) } // Create and return a void return statement. if typ.Val == "void" { return &ast.ReturnStmt{}, nil } // Create and return a return statement. val, err := parseOperand(inst.Operand(0)) if err != nil { return nil, errutil.Err(err) } ret := &ast.ReturnStmt{ Results: []ast.Expr{val}, } return ret, nil }