| ... | ... | @@ -2582,6 +2582,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2582 | 2582 | |
| 2583 | 2583 | } |
| 2584 | 2584 | |
| 2585 | typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *); |
| 2586 | |
| 2585 | 2587 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2586 | 2588 | IrInstructionBinOp *bin_op_instruction) |
| 2587 | 2589 | { |
| ... | ... | @@ -2640,50 +2642,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2640 | 2642 | } else { |
| 2641 | 2643 | zig_unreachable(); |
| 2642 | 2644 | } |
| 2645 | case IrBinOpMult: |
| 2646 | case IrBinOpMultWrap: |
| 2643 | 2647 | case IrBinOpAdd: |
| 2644 | 2648 | case IrBinOpAddWrap: |
| 2649 | case IrBinOpSub: |
| 2650 | case IrBinOpSubWrap: { |
| 2651 | // These are lookup table using the AddSubMul enum as the lookup. |
| 2652 | // If AddSubMul ever changes, then these tables will be out of |
| 2653 | // date. |
| 2654 | static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul }; |
| 2655 | static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul }; |
| 2656 | static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; |
| 2657 | static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; |
| 2658 | |
| 2659 | bool is_vector = type_entry->id == ZigTypeIdVector; |
| 2660 | bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); |
| 2661 | AddSubMul add_sub_mul = |
| 2662 | op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : |
| 2663 | op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : |
| 2664 | AddSubMulMul; |
| 2665 | |
| 2666 | // The code that is generated for vectors and scalars are the same, |
| 2667 | // so we can just set type_entry to the vectors elem_type an avoid |
| 2668 | // a lot of repeated code. |
| 2669 | if (is_vector) |
| 2670 | type_entry = type_entry->data.vector.elem_type; |
| 2671 | |
| 2645 | 2672 | if (type_entry->id == ZigTypeIdPointer) { |
| 2646 | 2673 | assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); |
| 2674 | LLVMValueRef subscript_value; |
| 2675 | if (is_vector) |
| 2676 | zig_panic("TODO: Implement vector operations on pointers."); |
| 2677 | |
| 2678 | switch (add_sub_mul) { |
| 2679 | case AddSubMulAdd: |
| 2680 | subscript_value = op2_value; |
| 2681 | break; |
| 2682 | case AddSubMulSub: |
| 2683 | subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); |
| 2684 | break; |
| 2685 | case AddSubMulMul: |
| 2686 | zig_unreachable(); |
| 2687 | } |
| 2688 | |
| 2647 | 2689 | // TODO runtime safety |
| 2648 | | return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, ""); |
| 2690 | return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); |
| 2649 | 2691 | } else if (type_entry->id == ZigTypeIdFloat) { |
| 2650 | 2692 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2651 | | return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); |
| 2693 | return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2652 | 2694 | } else if (type_entry->id == ZigTypeIdInt) { |
| 2653 | | bool is_wrapping = (op_id == IrBinOpAddWrap); |
| 2654 | 2695 | if (is_wrapping) { |
| 2655 | | return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); |
| 2696 | return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2656 | 2697 | } else if (want_runtime_safety) { |
| 2657 | | return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value); |
| 2698 | if (is_vector) |
| 2699 | zig_panic("TODO: Implement runtime safety vector operations."); |
| 2700 | return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value); |
| 2658 | 2701 | } else if (type_entry->data.integral.is_signed) { |
| 2659 | | return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); |
| 2660 | | } else { |
| 2661 | | return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); |
| 2662 | | } |
| 2663 | | } else if (type_entry->id == ZigTypeIdVector) { |
| 2664 | | ZigType *elem_type = type_entry->data.vector.elem_type; |
| 2665 | | if (elem_type->id == ZigTypeIdFloat) { |
| 2666 | | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2667 | | return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); |
| 2668 | | } else if (elem_type->id == ZigTypeIdPointer) { |
| 2669 | | zig_panic("TODO codegen for pointers in vectors"); |
| 2670 | | } else if (elem_type->id == ZigTypeIdInt) { |
| 2671 | | bool is_wrapping = (op_id == IrBinOpAddWrap); |
| 2672 | | if (is_wrapping) { |
| 2673 | | return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); |
| 2674 | | } else if (want_runtime_safety) { |
| 2675 | | zig_panic("TODO runtime safety for vector integer addition"); |
| 2676 | | } else if (elem_type->data.integral.is_signed) { |
| 2677 | | return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); |
| 2678 | | } else { |
| 2679 | | return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); |
| 2680 | | } |
| 2702 | return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2681 | 2703 | } else { |
| 2682 | | zig_unreachable(); |
| 2704 | return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2683 | 2705 | } |
| 2684 | 2706 | } else { |
| 2685 | 2707 | zig_unreachable(); |
| 2686 | 2708 | } |
| 2709 | } |
| 2687 | 2710 | case IrBinOpBinOr: |
| 2688 | 2711 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); |
| 2689 | 2712 | case IrBinOpBinXor: |
| ... | ... | @@ -2728,49 +2751,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2728 | 2751 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); |
| 2729 | 2752 | } |
| 2730 | 2753 | } |
| 2731 | | case IrBinOpSub: |
| 2732 | | case IrBinOpSubWrap: |
| 2733 | | if (type_entry->id == ZigTypeIdPointer) { |
| 2734 | | assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); |
| 2735 | | // TODO runtime safety |
| 2736 | | LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); |
| 2737 | | return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); |
| 2738 | | } else if (type_entry->id == ZigTypeIdFloat) { |
| 2739 | | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2740 | | return LLVMBuildFSub(g->builder, op1_value, op2_value, ""); |
| 2741 | | } else if (type_entry->id == ZigTypeIdInt) { |
| 2742 | | bool is_wrapping = (op_id == IrBinOpSubWrap); |
| 2743 | | if (is_wrapping) { |
| 2744 | | return LLVMBuildSub(g->builder, op1_value, op2_value, ""); |
| 2745 | | } else if (want_runtime_safety) { |
| 2746 | | return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value); |
| 2747 | | } else if (type_entry->data.integral.is_signed) { |
| 2748 | | return LLVMBuildNSWSub(g->builder, op1_value, op2_value, ""); |
| 2749 | | } else { |
| 2750 | | return LLVMBuildNUWSub(g->builder, op1_value, op2_value, ""); |
| 2751 | | } |
| 2752 | | } else { |
| 2753 | | zig_unreachable(); |
| 2754 | | } |
| 2755 | | case IrBinOpMult: |
| 2756 | | case IrBinOpMultWrap: |
| 2757 | | if (type_entry->id == ZigTypeIdFloat) { |
| 2758 | | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); |
| 2759 | | return LLVMBuildFMul(g->builder, op1_value, op2_value, ""); |
| 2760 | | } else if (type_entry->id == ZigTypeIdInt) { |
| 2761 | | bool is_wrapping = (op_id == IrBinOpMultWrap); |
| 2762 | | if (is_wrapping) { |
| 2763 | | return LLVMBuildMul(g->builder, op1_value, op2_value, ""); |
| 2764 | | } else if (want_runtime_safety) { |
| 2765 | | return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value); |
| 2766 | | } else if (type_entry->data.integral.is_signed) { |
| 2767 | | return LLVMBuildNSWMul(g->builder, op1_value, op2_value, ""); |
| 2768 | | } else { |
| 2769 | | return LLVMBuildNUWMul(g->builder, op1_value, op2_value, ""); |
| 2770 | | } |
| 2771 | | } else { |
| 2772 | | zig_unreachable(); |
| 2773 | | } |
| 2774 | 2754 | case IrBinOpDivUnspecified: |
| 2775 | 2755 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2776 | 2756 | op1_value, op2_value, type_entry, DivKindFloat); |