| ... | @@ -2582,6 +2582,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -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 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | 2587 | static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2586 | IrInstructionBinOp *bin_op_instruction) | 2588 | IrInstructionBinOp *bin_op_instruction) |
| 2587 | { | 2589 | { |
| ... | @@ -2640,50 +2642,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2640,50 +2642,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2640 | } else { | 2642 | } else { |
| 2641 | zig_unreachable(); | 2643 | zig_unreachable(); |
| 2642 | } | 2644 | } |
| | 2645 | case IrBinOpMult: |
| | 2646 | case IrBinOpMultWrap: |
| 2643 | case IrBinOpAdd: | 2647 | case IrBinOpAdd: |
| 2644 | case IrBinOpAddWrap: | 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 | if (type_entry->id == ZigTypeIdPointer) { | 2672 | if (type_entry->id == ZigTypeIdPointer) { |
| 2646 | assert(type_entry->data.pointer.ptr_len == PtrLenUnknown); | 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 | // TODO runtime safety | 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 | } else if (type_entry->id == ZigTypeIdFloat) { | 2691 | } else if (type_entry->id == ZigTypeIdFloat) { |
| 2650 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | 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 | } else if (type_entry->id == ZigTypeIdInt) { | 2694 | } else if (type_entry->id == ZigTypeIdInt) { |
| 2653 | bool is_wrapping = (op_id == IrBinOpAddWrap); | | |
| 2654 | if (is_wrapping) { | 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 | } else if (want_runtime_safety) { | 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 | } else if (type_entry->data.integral.is_signed) { | 2701 | } else if (type_entry->data.integral.is_signed) { |
| 2659 | return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); | 2702 | return signed_op[add_sub_mul](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 | } | | |
| 2681 | } else { | 2703 | } else { |
| 2682 | zig_unreachable(); | 2704 | return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); |
| 2683 | } | 2705 | } |
| 2684 | } else { | 2706 | } else { |
| 2685 | zig_unreachable(); | 2707 | zig_unreachable(); |
| 2686 | } | 2708 | } |
| | 2709 | } |
| 2687 | case IrBinOpBinOr: | 2710 | case IrBinOpBinOr: |
| 2688 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); | 2711 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); |
| 2689 | case IrBinOpBinXor: | 2712 | case IrBinOpBinXor: |
| ... | @@ -2728,49 +2751,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, | ... | @@ -2728,49 +2751,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 2728 | return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); | 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 | case IrBinOpDivUnspecified: | 2754 | case IrBinOpDivUnspecified: |
| 2775 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), | 2755 | return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 2776 | op1_value, op2_value, type_entry, DivKindFloat); | 2756 | op1_value, op2_value, type_entry, DivKindFloat); |