createBinOp creates a LLVM binary operation (add, sub, mul, etc) for a Go binary operation. This is almost a direct mapping, but there are some subtle differences such as the requirement in LLVM IR that both sides must have the same type, even for bitshifts. Also, signedness in Go is encoded in the
(op token.Token, typ, ytyp types.Type, x, y llvm.Value, pos token.Pos)
| 2640 | // and is encoded in the operation in LLVM IR: this is important for some |
| 2641 | // operations such as divide. |
| 2642 | func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Value, pos token.Pos) (llvm.Value, error) { |
| 2643 | switch typ := typ.Underlying().(type) { |
| 2644 | case *types.Basic: |
| 2645 | if typ.Info()&types.IsInteger != 0 { |
| 2646 | // Operations on integers |
| 2647 | signed := typ.Info()&types.IsUnsigned == 0 |
| 2648 | switch op { |
| 2649 | case token.ADD: // + |
| 2650 | return b.CreateAdd(x, y, ""), nil |
| 2651 | case token.SUB: // - |
| 2652 | return b.CreateSub(x, y, ""), nil |
| 2653 | case token.MUL: // * |
| 2654 | return b.CreateMul(x, y, ""), nil |
| 2655 | case token.QUO, token.REM: // /, % |
| 2656 | // Check for a divide by zero. If y is zero, the Go |
| 2657 | // specification says that a runtime error must be triggered. |
| 2658 | b.createDivideByZeroCheck(y) |
| 2659 | |
| 2660 | if signed { |
| 2661 | // Deal with signed division overflow. |
| 2662 | // The LLVM LangRef says: |
| 2663 | // |
| 2664 | // Overflow also leads to undefined behavior; this is a |
| 2665 | // rare case, but can occur, for example, by doing a |
| 2666 | // 32-bit division of -2147483648 by -1. |
| 2667 | // |
| 2668 | // The Go specification however says this about division: |
| 2669 | // |
| 2670 | // The one exception to this rule is that if the dividend |
| 2671 | // x is the most negative value for the int type of x, the |
| 2672 | // quotient q = x / -1 is equal to x (and r = 0) due to |
| 2673 | // two's-complement integer overflow. |
| 2674 | // |
| 2675 | // In other words, in the special case that the lowest |
| 2676 | // possible signed integer is divided by -1, the result of |
| 2677 | // the division is the same as x (the dividend). |
| 2678 | // This is implemented by checking for this condition and |
| 2679 | // changing y to 1 if it occurs, for example for 32-bit |
| 2680 | // ints: |
| 2681 | // |
| 2682 | // if x == -2147483648 && y == -1 { |
| 2683 | // y = 1 |
| 2684 | // } |
| 2685 | // |
| 2686 | // Dividing x by 1 obviously returns x, therefore satisfying |
| 2687 | // the Go specification without a branch. |
| 2688 | llvmType := x.Type() |
| 2689 | minusOne := llvm.ConstSub(llvm.ConstInt(llvmType, 0, false), llvm.ConstInt(llvmType, 1, false)) |
| 2690 | lowestInteger := llvm.ConstInt(x.Type(), 1<<(llvmType.IntTypeWidth()-1), false) |
| 2691 | yIsMinusOne := b.CreateICmp(llvm.IntEQ, y, minusOne, "") |
| 2692 | xIsLowestInteger := b.CreateICmp(llvm.IntEQ, x, lowestInteger, "") |
| 2693 | hasOverflow := b.CreateAnd(yIsMinusOne, xIsLowestInteger, "") |
| 2694 | y = b.CreateSelect(hasOverflow, llvm.ConstInt(llvmType, 1, true), y, "") |
| 2695 | |
| 2696 | if op == token.QUO { |
| 2697 | return b.CreateSDiv(x, y, ""), nil |
| 2698 | } else { |
| 2699 | return b.CreateSRem(x, y, ""), nil |
no test coverage detected