| ... | ... | @@ -100,6 +100,74 @@ static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_no |
| 100 | 100 | } |
| 101 | 101 | } |
| 102 | 102 | |
| 103 | enum AddSubMul { |
| 104 | AddSubMulAdd = 0, |
| 105 | AddSubMulSub = 1, |
| 106 | AddSubMulMul = 2, |
| 107 | }; |
| 108 | |
| 109 | static int bits_index(int size_in_bits) { |
| 110 | switch (size_in_bits) { |
| 111 | case 8: |
| 112 | return 0; |
| 113 | case 16: |
| 114 | return 1; |
| 115 | case 32: |
| 116 | return 2; |
| 117 | case 64: |
| 118 | return 3; |
| 119 | default: |
| 120 | zig_unreachable(); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, |
| 125 | const char *signed_name, const char *unsigned_name) |
| 126 | { |
| 127 | const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name; |
| 128 | Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits); |
| 129 | |
| 130 | LLVMTypeRef return_elem_types[] = { |
| 131 | type_entry->type_ref, |
| 132 | LLVMInt1Type(), |
| 133 | }; |
| 134 | LLVMTypeRef param_types[] = { |
| 135 | type_entry->type_ref, |
| 136 | type_entry->type_ref, |
| 137 | }; |
| 138 | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); |
| 139 | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); |
| 140 | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type); |
| 141 | assert(LLVMGetIntrinsicID(fn_val)); |
| 142 | return fn_val; |
| 143 | } |
| 144 | |
| 145 | static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, AddSubMul add_sub_mul) { |
| 146 | assert(type_entry->id == TypeTableEntryIdInt); |
| 147 | // [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64] |
| 148 | int index0 = type_entry->data.integral.is_signed ? 0 : 1; |
| 149 | int index1 = add_sub_mul; |
| 150 | int index2 = bits_index(type_entry->size_in_bits); |
| 151 | LLVMValueRef *fn = &g->int_overflow_fns[index0][index1][index2]; |
| 152 | if (*fn) { |
| 153 | return *fn; |
| 154 | } |
| 155 | switch (add_sub_mul) { |
| 156 | case AddSubMulAdd: |
| 157 | *fn = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd"); |
| 158 | break; |
| 159 | case AddSubMulSub: |
| 160 | *fn = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub"); |
| 161 | break; |
| 162 | case AddSubMulMul: |
| 163 | *fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul"); |
| 164 | break; |
| 165 | |
| 166 | } |
| 167 | return *fn; |
| 168 | } |
| 169 | |
| 170 | |
| 103 | 171 | static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 104 | 172 | assert(node->type == NodeTypeFnCallExpr); |
| 105 | 173 | AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; |
| ... | ... | @@ -118,16 +186,17 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 118 | 186 | assert(fn_call_param_count == 4); |
| 119 | 187 | |
| 120 | 188 | TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0)); |
| 121 | | LLVMValueRef fn_val; |
| 189 | AddSubMul add_sub_mul; |
| 122 | 190 | if (builtin_fn->id == BuiltinFnIdAddWithOverflow) { |
| 123 | | fn_val = int_type->data.integral.add_with_overflow_fn; |
| 191 | add_sub_mul = AddSubMulAdd; |
| 124 | 192 | } else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) { |
| 125 | | fn_val = int_type->data.integral.sub_with_overflow_fn; |
| 193 | add_sub_mul = AddSubMulSub; |
| 126 | 194 | } else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) { |
| 127 | | fn_val = int_type->data.integral.mul_with_overflow_fn; |
| 195 | add_sub_mul = AddSubMulMul; |
| 128 | 196 | } else { |
| 129 | 197 | zig_unreachable(); |
| 130 | 198 | } |
| 199 | LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul); |
| 131 | 200 | |
| 132 | 201 | LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1)); |
| 133 | 202 | LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2)); |
| ... | ... | @@ -2559,35 +2628,6 @@ static void do_code_gen(CodeGen *g) { |
| 2559 | 2628 | #endif |
| 2560 | 2629 | } |
| 2561 | 2630 | |
| 2562 | | static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, |
| 2563 | | const char *signed_name, const char *unsigned_name) |
| 2564 | | { |
| 2565 | | const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name; |
| 2566 | | Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits); |
| 2567 | | |
| 2568 | | LLVMTypeRef return_elem_types[] = { |
| 2569 | | type_entry->type_ref, |
| 2570 | | LLVMInt1Type(), |
| 2571 | | }; |
| 2572 | | LLVMTypeRef param_types[] = { |
| 2573 | | type_entry->type_ref, |
| 2574 | | type_entry->type_ref, |
| 2575 | | }; |
| 2576 | | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); |
| 2577 | | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); |
| 2578 | | LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type); |
| 2579 | | assert(LLVMGetIntrinsicID(fn_val)); |
| 2580 | | return fn_val; |
| 2581 | | } |
| 2582 | | |
| 2583 | | static void add_int_overflow_fns(CodeGen *g, TypeTableEntry *type_entry) { |
| 2584 | | assert(type_entry->id == TypeTableEntryIdInt); |
| 2585 | | |
| 2586 | | type_entry->data.integral.add_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd"); |
| 2587 | | type_entry->data.integral.sub_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub"); |
| 2588 | | type_entry->data.integral.mul_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul"); |
| 2589 | | } |
| 2590 | | |
| 2591 | 2631 | static const int int_sizes_in_bits[] = { |
| 2592 | 2632 | 8, |
| 2593 | 2633 | 16, |
| ... | ... | @@ -2595,6 +2635,52 @@ static const int int_sizes_in_bits[] = { |
| 2595 | 2635 | 64, |
| 2596 | 2636 | }; |
| 2597 | 2637 | |
| 2638 | enum CIntType { |
| 2639 | CIntTypeShort, |
| 2640 | CIntTypeUShort, |
| 2641 | CIntTypeInt, |
| 2642 | CIntTypeUInt, |
| 2643 | CIntTypeLong, |
| 2644 | CIntTypeULong, |
| 2645 | CIntTypeLongLong, |
| 2646 | CIntTypeULongLong, |
| 2647 | }; |
| 2648 | |
| 2649 | struct CIntTypeInfo { |
| 2650 | CIntType id; |
| 2651 | const char *name; |
| 2652 | bool is_signed; |
| 2653 | }; |
| 2654 | |
| 2655 | static const CIntTypeInfo c_int_type_infos[] = { |
| 2656 | {CIntTypeShort, "c_short", true}, |
| 2657 | {CIntTypeUShort, "c_ushort", false}, |
| 2658 | {CIntTypeInt, "c_int", true}, |
| 2659 | {CIntTypeUInt, "c_uint", false}, |
| 2660 | {CIntTypeLong, "c_long", true}, |
| 2661 | {CIntTypeULong, "c_ulong", false}, |
| 2662 | {CIntTypeLongLong, "c_longlong", true}, |
| 2663 | {CIntTypeULongLong, "c_ulonglong", false}, |
| 2664 | }; |
| 2665 | |
| 2666 | static int get_c_type_size_in_bits(CodeGen *g, CIntType id) { |
| 2667 | // TODO other architectures besides x86_64 |
| 2668 | switch (id) { |
| 2669 | case CIntTypeShort: |
| 2670 | case CIntTypeUShort: |
| 2671 | return 16; |
| 2672 | case CIntTypeInt: |
| 2673 | case CIntTypeUInt: |
| 2674 | return 32; |
| 2675 | case CIntTypeLong: |
| 2676 | case CIntTypeULong: |
| 2677 | case CIntTypeLongLong: |
| 2678 | case CIntTypeULongLong: |
| 2679 | return 64; |
| 2680 | } |
| 2681 | zig_unreachable(); |
| 2682 | } |
| 2683 | |
| 2598 | 2684 | static void define_builtin_types(CodeGen *g) { |
| 2599 | 2685 | { |
| 2600 | 2686 | // if this type is anywhere in the AST, we should never hit codegen. |
| ... | ... | @@ -2639,8 +2725,6 @@ static void define_builtin_types(CodeGen *g) { |
| 2639 | 2725 | |
| 2640 | 2726 | get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry; |
| 2641 | 2727 | |
| 2642 | | add_int_overflow_fns(g, entry); |
| 2643 | | |
| 2644 | 2728 | if (!is_signed) { |
| 2645 | 2729 | break; |
| 2646 | 2730 | } else { |
| ... | ... | @@ -2649,6 +2733,26 @@ static void define_builtin_types(CodeGen *g) { |
| 2649 | 2733 | } |
| 2650 | 2734 | } |
| 2651 | 2735 | |
| 2736 | for (int i = 0; i < array_length(c_int_type_infos); i += 1) { |
| 2737 | const CIntTypeInfo *info = &c_int_type_infos[i]; |
| 2738 | uint64_t size_in_bits = get_c_type_size_in_bits(g, info->id); |
| 2739 | bool is_signed = info->is_signed; |
| 2740 | |
| 2741 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| 2742 | entry->type_ref = LLVMIntType(size_in_bits); |
| 2743 | |
| 2744 | buf_init_from_str(&entry->name, info->name); |
| 2745 | |
| 2746 | entry->size_in_bits = size_in_bits; |
| 2747 | entry->align_in_bits = size_in_bits; |
| 2748 | |
| 2749 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), |
| 2750 | entry->size_in_bits, entry->align_in_bits, |
| 2751 | is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned()); |
| 2752 | entry->data.integral.is_signed = is_signed; |
| 2753 | g->primitive_type_table.put(&entry->name, entry); |
| 2754 | } |
| 2755 | |
| 2652 | 2756 | { |
| 2653 | 2757 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); |
| 2654 | 2758 | entry->type_ref = LLVMInt1Type(); |
| ... | ... | @@ -2669,11 +2773,6 @@ static void define_builtin_types(CodeGen *g) { |
| 2669 | 2773 | entry->align_in_bits = g->pointer_size_bytes * 8; |
| 2670 | 2774 | entry->data.integral.is_signed = true; |
| 2671 | 2775 | |
| 2672 | | TypeTableEntry *fixed_width_entry = get_int_type(g, entry->data.integral.is_signed, entry->size_in_bits); |
| 2673 | | entry->data.integral.add_with_overflow_fn = fixed_width_entry->data.integral.add_with_overflow_fn; |
| 2674 | | entry->data.integral.sub_with_overflow_fn = fixed_width_entry->data.integral.sub_with_overflow_fn; |
| 2675 | | entry->data.integral.mul_with_overflow_fn = fixed_width_entry->data.integral.mul_with_overflow_fn; |
| 2676 | | |
| 2677 | 2776 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), |
| 2678 | 2777 | entry->size_in_bits, entry->align_in_bits, |
| 2679 | 2778 | LLVMZigEncoding_DW_ATE_signed()); |
| ... | ... | @@ -2688,11 +2787,6 @@ static void define_builtin_types(CodeGen *g) { |
| 2688 | 2787 | entry->align_in_bits = g->pointer_size_bytes * 8; |
| 2689 | 2788 | entry->data.integral.is_signed = false; |
| 2690 | 2789 | |
| 2691 | | TypeTableEntry *fixed_width_entry = get_int_type(g, entry->data.integral.is_signed, entry->size_in_bits); |
| 2692 | | entry->data.integral.add_with_overflow_fn = fixed_width_entry->data.integral.add_with_overflow_fn; |
| 2693 | | entry->data.integral.sub_with_overflow_fn = fixed_width_entry->data.integral.sub_with_overflow_fn; |
| 2694 | | entry->data.integral.mul_with_overflow_fn = fixed_width_entry->data.integral.mul_with_overflow_fn; |
| 2695 | | |
| 2696 | 2790 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), |
| 2697 | 2791 | entry->size_in_bits, entry->align_in_bits, |
| 2698 | 2792 | LLVMZigEncoding_DW_ATE_unsigned()); |