add integer wrapping

see #46
This commit is contained in:
Andrew Kelley 2016-05-05 17:19:01 -07:00
parent dedde0d790
commit 094336f07c
7 changed files with 155 additions and 78 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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;
}