Commit 0c9afede9e

Andrew Kelley <superjoe30@gmail.com>
2016-01-15 01:04:35
overflow intrinsics take type as first argument
1 parent 68c4f61
src/all_types.hpp
@@ -630,6 +630,9 @@ struct TypeTableEntryPointer {
 
 struct TypeTableEntryInt {
     bool is_signed;
+    LLVMValueRef add_with_overflow_fn;
+    LLVMValueRef sub_with_overflow_fn;
+    LLVMValueRef mul_with_overflow_fn;
 };
 
 struct TypeTableEntryArray {
@@ -791,7 +794,6 @@ struct FnTableEntry {
 
 enum BuiltinFnId {
     BuiltinFnIdInvalid,
-    BuiltinFnIdArithmeticWithOverflow,
     BuiltinFnIdMemcpy,
     BuiltinFnIdMemset,
     BuiltinFnIdSizeof,
@@ -799,6 +801,9 @@ enum BuiltinFnId {
     BuiltinFnIdMinValue,
     BuiltinFnIdValueCount,
     BuiltinFnIdTypeof,
+    BuiltinFnIdAddWithOverflow,
+    BuiltinFnIdSubWithOverflow,
+    BuiltinFnIdMulWithOverflow,
 };
 
 struct BuiltinFnEntry {
@@ -831,6 +836,7 @@ struct CodeGen {
 
     struct {
         TypeTableEntry *entry_bool;
+        TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64]
         TypeTableEntry *entry_u8;
         TypeTableEntry *entry_u16;
         TypeTableEntry *entry_u32;
src/analyze.cpp
@@ -155,18 +155,7 @@ static TypeTableEntry *get_number_literal_type_unsigned(CodeGen *g, uint64_t x)
 }
 
 static TypeTableEntry *get_int_type_unsigned(CodeGen *g, uint64_t x) {
-    switch (get_number_literal_kind_unsigned(x)) {
-        case NumLitU8:
-            return g->builtin_types.entry_u8;
-        case NumLitU16:
-            return g->builtin_types.entry_u16;
-        case NumLitU32:
-            return g->builtin_types.entry_u32;
-        case NumLitU64:
-            return g->builtin_types.entry_u64;
-        default:
-            zig_unreachable();
-    }
+    return get_int_type(g, false, num_lit_bit_count(get_number_literal_kind_unsigned(x)));
 }
 
 static TypeTableEntry *get_meta_type(CodeGen *g, TypeTableEntry *child_type) {
@@ -464,7 +453,9 @@ static void eval_const_expr_builtin(CodeGen *g, BlockContext *context, AstNode *
     switch (node->data.fn_call_expr.builtin_fn->id) {
         case BuiltinFnIdInvalid:
             zig_unreachable();
-        case BuiltinFnIdArithmeticWithOverflow:
+        case BuiltinFnIdAddWithOverflow:
+        case BuiltinFnIdSubWithOverflow:
+        case BuiltinFnIdMulWithOverflow:
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
             break;
@@ -2476,18 +2467,36 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
             add_node_error(g, node,
                     buf_sprintf("expected %d arguments, got %d",
                         builtin_fn->param_count, actual_param_count));
+            return g->builtin_types.entry_invalid;
         }
 
         switch (builtin_fn->id) {
             case BuiltinFnIdInvalid:
                 zig_unreachable();
-            case BuiltinFnIdArithmeticWithOverflow:
-                for (int i = 0; i < actual_param_count; i += 1) {
-                    AstNode *child = node->data.fn_call_expr.params.at(i);
-                    TypeTableEntry *expected_param_type = builtin_fn->param_types[i];
-                    analyze_expression(g, import, context, expected_param_type, child);
+            case BuiltinFnIdAddWithOverflow:
+            case BuiltinFnIdSubWithOverflow:
+            case BuiltinFnIdMulWithOverflow:
+                {
+                    AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                    TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
+                    if (int_type->id == TypeTableEntryIdInvalid) {
+                        return g->builtin_types.entry_bool;
+                    } else if (int_type->id == TypeTableEntryIdInt) {
+                        AstNode *op1_node = node->data.fn_call_expr.params.at(1);
+                        AstNode *op2_node = node->data.fn_call_expr.params.at(2);
+                        AstNode *result_node = node->data.fn_call_expr.params.at(3);
+
+                        analyze_expression(g, import, context, int_type, op1_node);
+                        analyze_expression(g, import, context, int_type, op2_node);
+                        analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false),
+                                result_node);
+                    } else {
+                        add_node_error(g, type_node,
+                            buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
+                    }
+
+                    return g->builtin_types.entry_bool;
                 }
-                return builtin_fn->return_type;
             case BuiltinFnIdMemcpy:
                 {
                     AstNode *dest_node = node->data.fn_call_expr.params.at(0);
@@ -2796,7 +2805,8 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                 } else if (expr_type->id == TypeTableEntryIdNumberLiteral) {
                     return expr_type;
                 } else {
-                    add_node_error(g, operand_node, buf_sprintf("invalid negation type: '%s'",
+                    BREAKPOINT;
+                    add_node_error(g, node, buf_sprintf("invalid negation type: '%s'",
                             buf_ptr(&expr_type->name)));
                     return g->builtin_types.entry_invalid;
                 }
@@ -3842,3 +3852,22 @@ bool is_node_void_expr(AstNode *node) {
     return false;
 }
 
+TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits) {
+    int index;
+    if (size_in_bits == 8) {
+        index = 0;
+    } else if (size_in_bits == 16) {
+        index = 1;
+    } else if (size_in_bits == 32) {
+        index = 2;
+    } else if (size_in_bits == 64) {
+        index = 3;
+    } else {
+        zig_unreachable();
+    }
+    return &g->builtin_types.entry_int[is_signed ? 0 : 1][index];
+}
+
+TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) {
+    return *get_int_type_ptr(g, is_signed, size_in_bits);
+}
src/analyze.hpp
@@ -21,5 +21,7 @@ Expr *get_resolved_expr(AstNode *node);
 NumLitCodeGen *get_resolved_num_lit(AstNode *node);
 TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
 bool is_node_void_expr(AstNode *node);
+TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits);
+TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits);
 
 #endif
src/codegen.cpp
@@ -184,14 +184,28 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
         case BuiltinFnIdInvalid:
         case BuiltinFnIdTypeof:
             zig_unreachable();
-        case BuiltinFnIdArithmeticWithOverflow:
+        case BuiltinFnIdAddWithOverflow:
+        case BuiltinFnIdSubWithOverflow:
+        case BuiltinFnIdMulWithOverflow:
             {
                 int fn_call_param_count = node->data.fn_call_expr.params.length;
-                assert(fn_call_param_count == 3);
+                assert(fn_call_param_count == 4);
+
+                TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
+                LLVMValueRef fn_val;
+                if (builtin_fn->id == BuiltinFnIdAddWithOverflow) {
+                    fn_val = int_type->data.integral.add_with_overflow_fn;
+                } else if (builtin_fn->id == BuiltinFnIdSubWithOverflow) {
+                    fn_val = int_type->data.integral.sub_with_overflow_fn;
+                } else if (builtin_fn->id == BuiltinFnIdMulWithOverflow) {
+                    fn_val = int_type->data.integral.mul_with_overflow_fn;
+                } else {
+                    zig_unreachable();
+                }
 
-                LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(0));
-                LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(1));
-                LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(2));
+                LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(1));
+                LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(2));
+                LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(3));
 
                 LLVMValueRef params[] = {
                     op1,
@@ -199,7 +213,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
                 };
 
                 add_debug_source_node(g, node);
-                LLVMValueRef result_struct = LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 2, "");
+                LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, "");
                 LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
                 LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
                 LLVMBuildStore(g->builder, result, ptr_result);
@@ -2184,6 +2198,35 @@ static void do_code_gen(CodeGen *g) {
 #endif
 }
 
+static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
+        const char *signed_name, const char *unsigned_name)
+{
+    const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name;
+    Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits);
+
+    LLVMTypeRef return_elem_types[] = {
+        type_entry->type_ref,
+        LLVMInt1Type(),
+    };
+    LLVMTypeRef param_types[] = {
+        type_entry->type_ref,
+        type_entry->type_ref,
+    };
+    LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
+    LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
+    LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
+    assert(LLVMGetIntrinsicID(fn_val));
+    return fn_val;
+}
+
+static void add_int_overflow_fns(CodeGen *g, TypeTableEntry *type_entry) {
+    assert(type_entry->id == TypeTableEntryIdInt);
+
+    type_entry->data.integral.add_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");
+    type_entry->data.integral.sub_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");
+    type_entry->data.integral.mul_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
+}
+
 static const NumLit num_lit_kinds[] = {
     NumLitF32,
     NumLitF64,
@@ -2198,6 +2241,13 @@ static const NumLit num_lit_kinds[] = {
     NumLitI64,
 };
 
+static const int int_sizes_in_bits[] = {
+    8,
+    16,
+    32,
+    64,
+};
+
 static void define_builtin_types(CodeGen *g) {
     {
         // if this type is anywhere in the AST, we should never hit codegen.
@@ -2219,6 +2269,37 @@ static void define_builtin_types(CodeGen *g) {
         g->num_lit_types[i] = entry;
     }
 
+    for (int i = 0; i < array_length(int_sizes_in_bits); i += 1) {
+        int size_in_bits = int_sizes_in_bits[i];
+        bool is_signed = true;
+        for (;;) {
+            TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
+            entry->type_ref = LLVMIntType(size_in_bits);
+
+            const char u_or_i = is_signed ? 'i' : 'u';
+            buf_resize(&entry->name, 0);
+            buf_appendf(&entry->name, "%c%d", u_or_i, size_in_bits);
+
+            entry->size_in_bits = size_in_bits;
+            entry->align_in_bits = size_in_bits;
+            entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+                    entry->size_in_bits, entry->align_in_bits,
+                    is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned());
+            entry->data.integral.is_signed = is_signed;
+            g->primitive_type_table.put(&entry->name, entry);
+
+            get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry;
+
+            add_int_overflow_fns(g, entry);
+
+            if (!is_signed) {
+                break;
+            } else {
+                is_signed = false;
+            }
+        }
+    }
+
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
         entry->type_ref = LLVMInt1Type();
@@ -2231,110 +2312,6 @@ static void define_builtin_types(CodeGen *g) {
         g->builtin_types.entry_bool = entry;
         g->primitive_type_table.put(&entry->name, entry);
     }
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt8Type();
-        buf_init_from_str(&entry->name, "u8");
-        entry->size_in_bits = 8;
-        entry->align_in_bits = 8;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_unsigned());
-        g->builtin_types.entry_u8 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt16Type();
-        buf_init_from_str(&entry->name, "u16");
-        entry->size_in_bits = 16;
-        entry->align_in_bits = 16;
-        entry->data.integral.is_signed = false;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_unsigned());
-        g->builtin_types.entry_u16 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt32Type();
-        buf_init_from_str(&entry->name, "u32");
-        entry->size_in_bits = 32;
-        entry->align_in_bits = 32;
-        entry->data.integral.is_signed = false;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_unsigned());
-        g->builtin_types.entry_u32 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt64Type();
-        buf_init_from_str(&entry->name, "u64");
-        entry->size_in_bits = 64;
-        entry->align_in_bits = 64;
-        entry->data.integral.is_signed = false;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_unsigned());
-        g->builtin_types.entry_u64 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt8Type();
-        buf_init_from_str(&entry->name, "i8");
-        entry->size_in_bits = 8;
-        entry->align_in_bits = 8;
-        entry->data.integral.is_signed = true;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_signed());
-        g->builtin_types.entry_i8 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt16Type();
-        buf_init_from_str(&entry->name, "i16");
-        entry->size_in_bits = 16;
-        entry->align_in_bits = 16;
-        entry->data.integral.is_signed = true;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_signed());
-        g->builtin_types.entry_i16 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt32Type();
-        buf_init_from_str(&entry->name, "i32");
-        entry->size_in_bits = 32;
-        entry->align_in_bits = 32;
-        entry->data.integral.is_signed = true;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_signed());
-        g->builtin_types.entry_i32 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
-        entry->type_ref = LLVMInt64Type();
-        buf_init_from_str(&entry->name, "i64");
-        entry->size_in_bits = 64;
-        entry->align_in_bits = 64;
-        entry->data.integral.is_signed = true;
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                entry->size_in_bits, entry->align_in_bits,
-                LLVMZigEncoding_DW_ATE_signed());
-        g->builtin_types.entry_i64 = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
@@ -2342,6 +2319,12 @@ static void define_builtin_types(CodeGen *g) {
         entry->size_in_bits = g->pointer_size_bytes * 8;
         entry->align_in_bits = g->pointer_size_bytes * 8;
         entry->data.integral.is_signed = true;
+
+        TypeTableEntry *fixed_width_entry = get_int_type(g, entry->data.integral.is_signed, entry->size_in_bits);
+        entry->data.integral.add_with_overflow_fn = fixed_width_entry->data.integral.add_with_overflow_fn;
+        entry->data.integral.sub_with_overflow_fn = fixed_width_entry->data.integral.sub_with_overflow_fn;
+        entry->data.integral.mul_with_overflow_fn = fixed_width_entry->data.integral.mul_with_overflow_fn;
+
         entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
                 entry->size_in_bits, entry->align_in_bits,
                 LLVMZigEncoding_DW_ATE_signed());
@@ -2355,6 +2338,12 @@ static void define_builtin_types(CodeGen *g) {
         entry->size_in_bits = g->pointer_size_bytes * 8;
         entry->align_in_bits = g->pointer_size_bytes * 8;
         entry->data.integral.is_signed = false;
+
+        TypeTableEntry *fixed_width_entry = get_int_type(g, entry->data.integral.is_signed, entry->size_in_bits);
+        entry->data.integral.add_with_overflow_fn = fixed_width_entry->data.integral.add_with_overflow_fn;
+        entry->data.integral.sub_with_overflow_fn = fixed_width_entry->data.integral.sub_with_overflow_fn;
+        entry->data.integral.mul_with_overflow_fn = fixed_width_entry->data.integral.mul_with_overflow_fn;
+
         entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
                 entry->size_in_bits, entry->align_in_bits,
                 LLVMZigEncoding_DW_ATE_unsigned());
@@ -2403,55 +2392,20 @@ static void define_builtin_types(CodeGen *g) {
         g->builtin_types.entry_unreachable = entry;
         g->primitive_type_table.put(&entry->name, entry);
     }
-}
-
-static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) {
-    assert(type_entry->id == TypeTableEntryIdInt);
-    struct OverflowFn {
-        const char *bare_name;
-        const char *signed_name;
-        const char *unsigned_name;
-    };
-    OverflowFn overflow_fns[] = {
-        {"add", "sadd", "uadd"},
-        {"sub", "ssub", "usub"},
-        {"mul", "smul", "umul"},
-    };
-    for (size_t i = 0; i < sizeof(overflow_fns)/sizeof(overflow_fns[0]); i += 1) {
-        OverflowFn *overflow_fn = &overflow_fns[i];
-        BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
-        buf_resize(&builtin_fn->name, 0);
-        buf_appendf(&builtin_fn->name, "%s_with_overflow_%s", overflow_fn->bare_name, buf_ptr(&type_entry->name));
-        builtin_fn->id = BuiltinFnIdArithmeticWithOverflow;
-        builtin_fn->return_type = g->builtin_types.entry_bool;
-        builtin_fn->param_count = 3;
-        builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
-        builtin_fn->param_types[0] = type_entry;
-        builtin_fn->param_types[1] = type_entry;
-        builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false);
 
+    g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, get_int_type(g, false, 8), true);
 
-        const char *signed_str = type_entry->data.integral.is_signed ?
-            overflow_fn->signed_name : overflow_fn->unsigned_name;
-        Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits);
-
-        LLVMTypeRef return_elem_types[] = {
-            type_entry->type_ref,
-            LLVMInt1Type(),
-        };
-        LLVMTypeRef param_types[] = {
-            type_entry->type_ref,
-            type_entry->type_ref,
-        };
-        LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
-        LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
-        builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
-        assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
-
-        g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
-    }
+    g->builtin_types.entry_u8 = get_int_type(g, false, 8);
+    g->builtin_types.entry_u16 = get_int_type(g, false, 16);
+    g->builtin_types.entry_u32 = get_int_type(g, false, 32);
+    g->builtin_types.entry_u64 = get_int_type(g, false, 64);
+    g->builtin_types.entry_i8 = get_int_type(g, true, 8);
+    g->builtin_types.entry_i16 = get_int_type(g, true, 16);
+    g->builtin_types.entry_i32 = get_int_type(g, true, 32);
+    g->builtin_types.entry_i64 = get_int_type(g, true, 64);
 }
 
+
 static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) {
     BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
     buf_init_from_str(&builtin_fn->name, name);
@@ -2460,24 +2414,14 @@ static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char
     return builtin_fn;
 }
 
-static BuiltinFnEntry *create_one_arg_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) {
+static BuiltinFnEntry *create_builtin_fn_with_arg_count(CodeGen *g, BuiltinFnId id, const char *name, int count) {
     BuiltinFnEntry *builtin_fn = create_builtin_fn(g, id, name);
-    builtin_fn->return_type = nullptr; // manually determined later
-    builtin_fn->param_count = 1;
-    builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
-    builtin_fn->param_types[0] = nullptr; // manually checked later
+    builtin_fn->param_count = count;
+    builtin_fn->param_types = allocate<TypeTableEntry *>(count);
     return builtin_fn;
 }
 
 static void define_builtin_fns(CodeGen *g) {
-    define_builtin_fns_int(g, g->builtin_types.entry_u8);
-    define_builtin_fns_int(g, g->builtin_types.entry_u16);
-    define_builtin_fns_int(g, g->builtin_types.entry_u32);
-    define_builtin_fns_int(g, g->builtin_types.entry_u64);
-    define_builtin_fns_int(g, g->builtin_types.entry_i8);
-    define_builtin_fns_int(g, g->builtin_types.entry_i16);
-    define_builtin_fns_int(g, g->builtin_types.entry_i32);
-    define_builtin_fns_int(g, g->builtin_types.entry_i64);
     {
         BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy");
         builtin_fn->return_type = g->builtin_types.entry_void;
@@ -2524,11 +2468,14 @@ static void define_builtin_fns(CodeGen *g) {
 
         g->memset_fn_val = builtin_fn->fn_val;
     }
-    create_one_arg_builtin_fn(g, BuiltinFnIdSizeof, "sizeof");
-    create_one_arg_builtin_fn(g, BuiltinFnIdMaxValue, "max_value");
-    create_one_arg_builtin_fn(g, BuiltinFnIdMinValue, "min_value");
-    create_one_arg_builtin_fn(g, BuiltinFnIdValueCount, "value_count");
-    create_one_arg_builtin_fn(g, BuiltinFnIdTypeof, "typeof");
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdSizeof, "sizeof", 1);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdMaxValue, "max_value", 1);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdMinValue, "min_value", 1);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdValueCount, "value_count", 1);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdTypeof, "typeof", 1);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdAddWithOverflow, "add_with_overflow", 4);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdSubWithOverflow, "sub_with_overflow", 4);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdMulWithOverflow, "mul_with_overflow", 4);
 }
 
 
std/std.zig
@@ -61,12 +61,12 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => {
         }
 
         // x *= radix
-        if (@mul_with_overflow_u64(x, radix, &x)) {
+        if (@mul_with_overflow(u64, x, radix, &x)) {
             return true;
         }
 
         // x += digit
-        if (@add_with_overflow_u64(x, digit, &x)) {
+        if (@add_with_overflow(u64, x, digit, &x)) {
             return true;
         }
 
test/run_tests.cpp
@@ -986,10 +986,10 @@ fn f(c: u8) u8 => {
 use "std.zig";
 pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var result: u8;
-    if (!@add_with_overflow_u8(250, 100, &result)) {
+    if (!@add_with_overflow(u8, 250, 100, &result)) {
         print_str("BAD\n");
     }
-    if (@add_with_overflow_u8(100, 150, &result)) {
+    if (@add_with_overflow(u8, 100, 150, &result)) {
         print_str("BAD\n");
     }
     if (result != 250) {