Commit 094336f07c

Andrew Kelley <superjoe30@gmail.com>
2016-05-06 02:19:01
add integer wrapping
see #46
1 parent dedde0d
doc/vim/syntax/zig.vim
@@ -15,7 +15,9 @@ syn keyword zigRepeat while for
 
 syn keyword zigConstant null undefined
 syn keyword zigKeyword fn use
-syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 void unreachable type error
+syn keyword zigType bool f32 f64 void unreachable type error
+syn keyword zigType i8  u8  i16  u16  i32  u32  i64  u64  isize  usize
+syn keyword zigType i8w u8w i16w u16w i32w u32w i64w u64w isizew usizew
 syn keyword zigType c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong
 
 syn keyword zigBoolean true false
doc/langref.md
@@ -181,19 +181,26 @@ x{}
 Type name       C equivalent        Description
 
 i8              int8_t              signed 8-bit integer
-u8              uint8_t             unsigned 8-bit integer
+u8              (none)              unsigned 8-bit integer
 i16             int16_t             signed 16-bit integer
-u16             uint16_t            unsigned 16-bit integer
+u16             (none)              unsigned 16-bit integer
 i32             int32_t             signed 32-bit integer
-u32             uint32_t            unsigned 32-bit integer
+u32             (none)              unsigned 32-bit integer
 i64             int64_t             signed 64-bit integer
-u64             uint64_t            unsigned 64-bit integer
-
-f32             float               32-bit IEE754 floating point
-f64             double              64-bit IEE754 floating point
-
+u64             (none)              unsigned 64-bit integer
 isize           intptr_t            signed pointer sized integer
-usize           uintptr_t           unsigned pointer sized integer
+usize           (none)              unsigned pointer sized integer
+
+i8w             (none)              wrapping signed 8-bit integer
+u8w             uint8_t             wrapping unsigned 8-bit integer
+i16w            (none)              wrapping signed 16-bit integer
+u16w            uint16_t            wrapping unsigned 16-bit integer
+i32w            (none)              wrapping signed 32-bit integer
+u32w            uint32_t            wrapping unsigned 32-bit integer
+i64w            (none)              wrapping signed 64-bit integer
+u64w            uint64_t            wrapping unsigned 64-bit integer
+isizew          (none)              wrapping signed pointer sized integer
+usizew          uintptr_t           wrapping unsigned pointer sized integer
 
 c_short         short               for ABI compatibility with C
 c_ushort        unsigned short      for ABI compatibility with C
@@ -205,6 +212,9 @@ c_longlong      long long           for ABI compatibility with C
 c_ulonglong     unsigned long long  for ABI compatibility with C
 c_long_double   long double         for ABI compatibility with C
 c_void          void                for ABI compatibility with C
+
+f32             float               32-bit IEE754 floating point
+f64             double              64-bit IEE754 floating point
 ```
 
 ### Boolean Type
src/all_types.hpp
@@ -846,8 +846,9 @@ struct TypeTableEntryPointer {
 };
 
 struct TypeTableEntryInt {
-    bool is_signed;
     int bit_count;
+    bool is_signed;
+    bool is_wrapping;
 };
 
 struct TypeTableEntryFloat {
@@ -1157,7 +1158,7 @@ struct CodeGen {
 
     struct {
         TypeTableEntry *entry_bool;
-        TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64]
+        TypeTableEntry *entry_int[2][2][4]; // [signed,unsigned][wrapping,nonwrapping][8,16,32,64]
         TypeTableEntry *entry_c_int[CIntTypeCount];
         TypeTableEntry *entry_c_long_double;
         TypeTableEntry *entry_c_void;
src/analyze.cpp
@@ -206,7 +206,7 @@ static bool type_is_complete(TypeTableEntry *type_entry) {
 }
 
 TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
-    return get_int_type(g, false, bits_needed_for_unsigned(x));
+    return get_int_type(g, false, false, bits_needed_for_unsigned(x));
 }
 
 static TypeTableEntry *get_generic_fn_type(CodeGen *g, AstNode *decl_node) {
@@ -6453,7 +6453,7 @@ bool is_node_void_expr(AstNode *node) {
     return false;
 }
 
-TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits) {
+TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits) {
     int index;
     if (size_in_bits == 8) {
         index = 0;
@@ -6466,11 +6466,11 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits)
     } else {
         zig_unreachable();
     }
-    return &g->builtin_types.entry_int[is_signed ? 0 : 1][index];
+    return &g->builtin_types.entry_int[is_signed ? 0 : 1][is_wrapping ? 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);
+TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits) {
+    return *get_int_type_ptr(g, is_signed, is_wrapping, size_in_bits);
 }
 
 TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
src/analyze.hpp
@@ -18,8 +18,8 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
 BlockContext *new_block_context(AstNode *node, BlockContext *parent);
 Expr *get_resolved_expr(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);
+TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits);
+TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits);
 TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
 TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type);
 TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type);
src/codegen.cpp
@@ -338,6 +338,11 @@ static bool want_debug_safety(CodeGen *g, AstNode *node) {
     return !g->is_release_build && !node->block_context->safety_off;
 }
 
+static void gen_debug_safety_crash(CodeGen *g) {
+    LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
+    LLVMBuildUnreachable(g->builder);
+}
+
 static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef target_val,
         LLVMIntPredicate lower_pred, LLVMValueRef lower_value,
         LLVMIntPredicate upper_pred, LLVMValueRef upper_value)
@@ -362,8 +367,7 @@ static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef targ
     LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block);
 
     LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block);
-    LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
-    LLVMBuildUnreachable(g->builder);
+    gen_debug_safety_crash(g);
 
     if (upper_value) {
         LLVMPositionBuilderAtEnd(g->builder, lower_ok_block);
@@ -1369,8 +1373,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                     LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
 
                     LLVMPositionBuilderAtEnd(g->builder, err_block);
-                    LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
-                    LLVMBuildUnreachable(g->builder);
+                    gen_debug_safety_crash(g);
 
                     LLVMPositionBuilderAtEnd(g->builder, ok_block);
                 }
@@ -1408,8 +1411,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                     LLVMBuildCondBr(g->builder, cond_val, ok_block, null_block);
 
                     LLVMPositionBuilderAtEnd(g->builder, null_block);
-                    LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
-                    LLVMBuildUnreachable(g->builder);
+                    gen_debug_safety_crash(g);
 
                     LLVMPositionBuilderAtEnd(g->builder, ok_block);
                 }
@@ -1429,6 +1431,28 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
     zig_unreachable();
 }
 
+static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddSubMul op,
+        LLVMValueRef val1, LLVMValueRef val2)
+{
+    LLVMValueRef fn_val = get_int_overflow_fn(g, type_entry, op);
+    LLVMValueRef params[] = {
+        val1,
+        val2,
+    };
+    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, "");
+    LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail");
+    LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk");
+    LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block);
+
+    LLVMPositionBuilderAtEnd(g->builder, fail_block);
+    gen_debug_safety_crash(g);
+
+    LLVMPositionBuilderAtEnd(g->builder, ok_block);
+    return result;
+}
+
 static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
     LLVMValueRef val1, LLVMValueRef val2,
     TypeTableEntry *op1_type, TypeTableEntry *op2_type,
@@ -1469,24 +1493,54 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
             set_debug_source_node(g, source_node);
             if (op1_type->id == TypeTableEntryIdFloat) {
                 return LLVMBuildFAdd(g->builder, val1, val2, "");
+            } else if (op1_type->id == TypeTableEntryIdInt) {
+                if (op1_type->data.integral.is_wrapping) {
+                    return LLVMBuildAdd(g->builder, val1, val2, "");
+                } else if (want_debug_safety(g, source_node)) {
+                    return gen_overflow_op(g, op1_type, AddSubMulAdd, val1, val2);
+                } else if (op1_type->data.integral.is_signed) {
+                    return LLVMBuildNSWAdd(g->builder, val1, val2, "");
+                } else {
+                    return LLVMBuildNUWAdd(g->builder, val1, val2, "");
+                }
             } else {
-                return LLVMBuildAdd(g->builder, val1, val2, "");
+                zig_unreachable();
             }
         case BinOpTypeSub:
         case BinOpTypeAssignMinus:
             set_debug_source_node(g, source_node);
             if (op1_type->id == TypeTableEntryIdFloat) {
                 return LLVMBuildFSub(g->builder, val1, val2, "");
+            } else if (op1_type->id == TypeTableEntryIdInt) {
+                if (op1_type->data.integral.is_wrapping) {
+                    return LLVMBuildSub(g->builder, val1, val2, "");
+                } else if (want_debug_safety(g, source_node)) {
+                    return gen_overflow_op(g, op1_type, AddSubMulSub, val1, val2);
+                } else if (op1_type->data.integral.is_signed) {
+                    return LLVMBuildNSWSub(g->builder, val1, val2, "");
+                } else {
+                    return LLVMBuildNUWSub(g->builder, val1, val2, "");
+                }
             } else {
-                return LLVMBuildSub(g->builder, val1, val2, "");
+                zig_unreachable();
             }
         case BinOpTypeMult:
         case BinOpTypeAssignTimes:
             set_debug_source_node(g, source_node);
             if (op1_type->id == TypeTableEntryIdFloat) {
                 return LLVMBuildFMul(g->builder, val1, val2, "");
+            } else if (op1_type->id == TypeTableEntryIdInt) {
+                if (op1_type->data.integral.is_wrapping) {
+                    return LLVMBuildMul(g->builder, val1, val2, "");
+                } else if (want_debug_safety(g, source_node)) {
+                    return gen_overflow_op(g, op1_type, AddSubMulMul, val1, val2);
+                } else if (op1_type->data.integral.is_signed) {
+                    return LLVMBuildNSWMul(g->builder, val1, val2, "");
+                } else {
+                    return LLVMBuildNUWMul(g->builder, val1, val2, "");
+                }
             } else {
-                return LLVMBuildMul(g->builder, val1, val2, "");
+                zig_unreachable();
             }
         case BinOpTypeDiv:
         case BinOpTypeAssignDiv:
@@ -2472,9 +2526,10 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
         assert(node->data.container_init_expr.entries.length == 0);
         set_debug_source_node(g, node);
         if (want_debug_safety(g, node)) {
-            LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
+            gen_debug_safety_crash(g);
+        } else {
+            LLVMBuildUnreachable(g->builder);
         }
-        LLVMBuildUnreachable(g->builder);
         return nullptr;
     } else if (type_entry->id == TypeTableEntryIdVoid) {
         assert(node->data.container_init_expr.entries.length == 0);
@@ -2983,7 +3038,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
             set_debug_source_node(g, prong_expr);
             LLVMBuildBr(g->builder, end_block);
             incoming_values.append(prong_val);
-            incoming_blocks.append(prong_block);
+            incoming_blocks.append(LLVMGetInsertBlock(g->builder));
         }
     }
 
@@ -2991,9 +3046,10 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
         LLVMPositionBuilderAtEnd(g->builder, else_block);
         set_debug_source_node(g, node);
         if (want_debug_safety(g, node)) {
-            LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
+            gen_debug_safety_crash(g);
+        } else {
+            LLVMBuildUnreachable(g->builder);
         }
-        LLVMBuildUnreachable(g->builder);
     }
 
     if (end_unreachable) {
@@ -3776,6 +3832,18 @@ static const CIntTypeInfo c_int_type_infos[] = {
     {CIntTypeULongLong, "c_ulonglong", false},
 };
 
+struct SignWrap {
+    bool is_signed;
+    bool is_wrapping;
+};
+
+static const SignWrap sign_wrap_list[] = {
+    {false, false},
+    {false, true},
+    {true, false},
+    {true, true},
+};
+
 static void define_builtin_types(CodeGen *g) {
     {
         // if this type is anywhere in the AST, we should never hit codegen.
@@ -3812,17 +3880,20 @@ static void define_builtin_types(CodeGen *g) {
         g->builtin_types.entry_undef = 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 (;;) {
+    for (int int_size_i = 0; int_size_i < array_length(int_sizes_in_bits); int_size_i += 1) {
+        int size_in_bits = int_sizes_in_bits[int_size_i];
+        for (int sign_wrap_i = 0; sign_wrap_i < array_length(sign_wrap_list); sign_wrap_i += 1) {
+            bool is_signed = sign_wrap_list[sign_wrap_i].is_signed;
+            bool is_wrapping = sign_wrap_list[sign_wrap_i].is_wrapping;
+
             TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
             entry->type_ref = LLVMIntType(size_in_bits);
             entry->deep_const = true;
 
             const char u_or_i = is_signed ? 'i' : 'u';
+            const char *w_or_none = is_wrapping ? "w" : "";
             buf_resize(&entry->name, 0);
-            buf_appendf(&entry->name, "%c%d", u_or_i, size_in_bits);
+            buf_appendf(&entry->name, "%c%d%s", u_or_i, size_in_bits, w_or_none);
 
             unsigned dwarf_tag;
             if (is_signed) {
@@ -3844,16 +3915,11 @@ static void define_builtin_types(CodeGen *g) {
             entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
                     debug_size_in_bits, debug_align_in_bits, dwarf_tag);
             entry->data.integral.is_signed = is_signed;
+            entry->data.integral.is_wrapping = is_wrapping;
             entry->data.integral.bit_count = size_in_bits;
             g->primitive_type_table.put(&entry->name, entry);
 
-            get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry;
-
-            if (!is_signed) {
-                break;
-            } else {
-                is_signed = false;
-            }
+            get_int_type_ptr(g, is_signed, is_wrapping, size_in_bits)[0] = entry;
         }
     }
 
@@ -3875,6 +3941,7 @@ static void define_builtin_types(CodeGen *g) {
                 debug_align_in_bits,
                 is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned());
         entry->data.integral.is_signed = is_signed;
+        entry->data.integral.is_wrapping = !is_signed;
         entry->data.integral.bit_count = size_in_bits;
         g->primitive_type_table.put(&entry->name, entry);
 
@@ -3895,29 +3962,22 @@ 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->deep_const = true;
-        entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
-        buf_init_from_str(&entry->name, "isize");
-        entry->data.integral.is_signed = true;
-        entry->data.integral.bit_count = g->pointer_size_bytes * 8;
 
-        uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
-        uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, entry->type_ref);
-        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
-                debug_size_in_bits,
-                debug_align_in_bits,
-                LLVMZigEncoding_DW_ATE_signed());
-        g->builtin_types.entry_isize = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
-    {
+    for (int sign_wrap_i = 0; sign_wrap_i < array_length(sign_wrap_list); sign_wrap_i += 1) {
+        bool is_signed = sign_wrap_list[sign_wrap_i].is_signed;
+        bool is_wrapping = sign_wrap_list[sign_wrap_i].is_wrapping;
+
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->deep_const = true;
         entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
-        buf_init_from_str(&entry->name, "usize");
-        entry->data.integral.is_signed = false;
+
+        const char u_or_i = is_signed ? 'i' : 'u';
+        const char *w_or_none = is_wrapping ? "w" : "";
+        buf_resize(&entry->name, 0);
+        buf_appendf(&entry->name, "%csize%s", u_or_i, w_or_none);
+
+        entry->data.integral.is_signed = is_signed;
+        entry->data.integral.is_wrapping = is_wrapping;
         entry->data.integral.bit_count = g->pointer_size_bytes * 8;
 
         uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref);
@@ -3925,9 +3985,14 @@ static void define_builtin_types(CodeGen *g) {
         entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
                 debug_size_in_bits,
                 debug_align_in_bits,
-                LLVMZigEncoding_DW_ATE_unsigned());
-        g->builtin_types.entry_usize = entry;
+                is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned());
         g->primitive_type_table.put(&entry->name, entry);
+
+        if (is_signed && !is_wrapping) {
+            g->builtin_types.entry_isize = entry;
+        } else if (!is_signed && !is_wrapping) {
+            g->builtin_types.entry_usize = entry;
+        }
     }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
@@ -4009,14 +4074,14 @@ static void define_builtin_types(CodeGen *g) {
         g->primitive_type_table.put(&entry->name, entry);
     }
 
-    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);
+    g->builtin_types.entry_u8 = get_int_type(g, false, false, 8);
+    g->builtin_types.entry_u16 = get_int_type(g, false, false, 16);
+    g->builtin_types.entry_u32 = get_int_type(g, false, false, 32);
+    g->builtin_types.entry_u64 = get_int_type(g, false, false, 64);
+    g->builtin_types.entry_i8 = get_int_type(g, true, false, 8);
+    g->builtin_types.entry_i16 = get_int_type(g, true, false, 16);
+    g->builtin_types.entry_i32 = get_int_type(g, true, false, 32);
+    g->builtin_types.entry_i64 = get_int_type(g, true, false, 64);
 
     {
         g->builtin_types.entry_c_void = get_typedecl_type(g, "c_void", g->builtin_types.entry_u8);
std/rand.zig
@@ -13,11 +13,10 @@ pub struct Rand {
         r.index = 0;
         r.array[0] = seed;
         var i : isize = 1;
-        var prev_value: u64 = seed;
-        while (i < ARRAY_SIZE) {
-            r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u32(i));
+        var prev_value: u64w = seed;
+        while (i < ARRAY_SIZE; i += 1) {
+            r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u64w(i));
             prev_value = r.array[i];
-            i += 1;
         }
         return r;
     }