From 094336f07cdf42f2f79df5b190ccc0139412cfbc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 5 May 2016 17:19:01 -0700 Subject: [PATCH] add integer wrapping see #46 --- doc/langref.md | 28 ++++--- doc/vim/syntax/zig.vim | 4 +- src/all_types.hpp | 5 +- src/analyze.cpp | 10 +-- src/analyze.hpp | 4 +- src/codegen.cpp | 175 ++++++++++++++++++++++++++++------------- std/rand.zig | 7 +- 7 files changed, 155 insertions(+), 78 deletions(-) diff --git a/doc/langref.md b/doc/langref.md index 754b81c44d..d17317b4f9 100644 --- a/doc/langref.md +++ b/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 diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index 355d065d91..5ce2dbea02 100644 --- a/doc/vim/syntax/zig.vim +++ b/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 diff --git a/src/all_types.hpp b/src/all_types.hpp index c07fc8f0fb..de99f78551 100644 --- a/src/all_types.hpp +++ b/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; diff --git a/src/analyze.cpp b/src/analyze.cpp index 556c905e43..7f02cc9aff 100644 --- a/src/analyze.cpp +++ b/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) { diff --git a/src/analyze.hpp b/src/analyze.hpp index 84ce17fa7b..7bf7844faf 100644 --- a/src/analyze.hpp +++ b/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); diff --git a/src/codegen.cpp b/src/codegen.cpp index a626ba7bfa..ab45109312 100644 --- a/src/codegen.cpp +++ b/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,12 +3962,22 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_bool = 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, "isize"); - entry->data.integral.is_signed = 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, "%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); @@ -3908,26 +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_signed()); - g->builtin_types.entry_isize = entry; + is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned()); 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, "usize"); - entry->data.integral.is_signed = false; - 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_unsigned()); - g->builtin_types.entry_usize = entry; - 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); diff --git a/std/rand.zig b/std/rand.zig index 7650e9e990..b7bf8be04b 100644 --- a/std/rand.zig +++ b/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; }