Пример #1
0
// 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))
}
Пример #2
0
// 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")
}
Пример #3
0
// 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
}
Пример #4
0
// 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)
	}
}
Пример #5
0
// 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
}