authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-02-16 17:08:55-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-02-16 17:08:55-05:00
logb6e7a0dadd86ce401e82ec53d6874e9934826c2d
tree27171640bb593bdc66d47b213f73f2b40ca68df3
parentfc5d47b9b960aa08d65bf0dfe3d6395c811f793b

support arithmetic for non byte aligned integer types

see #261

6 files changed, 161 insertions(+), 66 deletions(-)

src/all_types.hpp+34-2
...@@ -1219,6 +1219,39 @@ struct TypeId {...@@ -1219,6 +1219,39 @@ struct TypeId {
1219uint32_t type_id_hash(TypeId);1219uint32_t type_id_hash(TypeId);
1220bool type_id_eql(TypeId a, TypeId b);1220bool type_id_eql(TypeId a, TypeId b);
12211221
1222enum ZigLLVMFnId {
1223 ZigLLVMFnIdCtz,
1224 ZigLLVMFnIdClz,
1225 ZigLLVMFnIdOverflowArithmetic,
1226};
1227
1228enum AddSubMul {
1229 AddSubMulAdd = 0,
1230 AddSubMulSub = 1,
1231 AddSubMulMul = 2,
1232};
1233
1234struct ZigLLVMFnKey {
1235 ZigLLVMFnId id;
1236
1237 union {
1238 struct {
1239 uint32_t bit_count;
1240 } ctz;
1241 struct {
1242 uint32_t bit_count;
1243 } clz;
1244 struct {
1245 AddSubMul add_sub_mul;
1246 uint32_t bit_count;
1247 bool is_signed;
1248 } overflow_arithmetic;
1249 } data;
1250};
1251
1252uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey);
1253bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b);
1254
1222struct CodeGen {1255struct CodeGen {
1223 LLVMModuleRef module;1256 LLVMModuleRef module;
1224 ZigList<ErrorMsg*> errors;1257 ZigList<ErrorMsg*> errors;
...@@ -1239,6 +1272,7 @@ struct CodeGen {...@@ -1239,6 +1272,7 @@ struct CodeGen {
1239 HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;1272 HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
1240 HashMap<GenericFnTypeId *, FnTableEntry *, generic_fn_type_id_hash, generic_fn_type_id_eql> generic_table;1273 HashMap<GenericFnTypeId *, FnTableEntry *, generic_fn_type_id_hash, generic_fn_type_id_eql> generic_table;
1241 HashMap<Scope *, IrInstruction *, fn_eval_hash, fn_eval_eql> memoized_fn_eval_table;1274 HashMap<Scope *, IrInstruction *, fn_eval_hash, fn_eval_eql> memoized_fn_eval_table;
1275 HashMap<ZigLLVMFnKey, LLVMValueRef, zig_llvm_fn_key_hash, zig_llvm_fn_key_eql> llvm_fn_table;
12421276
1243 ZigList<ImportTableEntry *> import_queue;1277 ZigList<ImportTableEntry *> import_queue;
1244 size_t import_queue_index;1278 size_t import_queue_index;
...@@ -1363,8 +1397,6 @@ struct CodeGen {...@@ -1363,8 +1397,6 @@ struct CodeGen {
1363 bool error_during_imports;1397 bool error_during_imports;
1364 uint32_t next_node_index;1398 uint32_t next_node_index;
1365 TypeTableEntry *err_tag_type;1399 TypeTableEntry *err_tag_type;
1366 LLVMValueRef int_overflow_fns[2][3][4]; // [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64]
1367 LLVMValueRef int_builtin_fns[2][4]; // [0-ctz,1-clz][0-8,1-16,2-32,3-64]
13681400
1369 const char **clang_argv;1401 const char **clang_argv;
1370 size_t clang_argv_len;1402 size_t clang_argv_len;
src/analyze.cpp+30
...@@ -3997,3 +3997,33 @@ bool type_id_eql(TypeId a, TypeId b) {...@@ -3997,3 +3997,33 @@ bool type_id_eql(TypeId a, TypeId b) {
3997 }3997 }
3998 zig_unreachable();3998 zig_unreachable();
3999}3999}
4000
4001uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
4002 switch (x.id) {
4003 case ZigLLVMFnIdCtz:
4004 return x.data.ctz.bit_count * 810453934;
4005 case ZigLLVMFnIdClz:
4006 return x.data.clz.bit_count * 2428952817;
4007 case ZigLLVMFnIdOverflowArithmetic:
4008 return (x.data.overflow_arithmetic.bit_count * 87135777) +
4009 (x.data.overflow_arithmetic.add_sub_mul * 31640542) +
4010 (x.data.overflow_arithmetic.is_signed ? 1062315172 : 314955820);
4011 }
4012 zig_unreachable();
4013}
4014
4015bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
4016 if (a.id != b.id)
4017 return false;
4018 switch (a.id) {
4019 case ZigLLVMFnIdCtz:
4020 return a.data.ctz.bit_count == b.data.ctz.bit_count;
4021 case ZigLLVMFnIdClz:
4022 return a.data.clz.bit_count == b.data.clz.bit_count;
4023 case ZigLLVMFnIdOverflowArithmetic:
4024 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
4025 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
4026 (a.data.overflow_arithmetic.is_signed == b.data.overflow_arithmetic.is_signed);
4027 }
4028 zig_unreachable();
4029}
src/codegen.cpp+66-57
...@@ -62,6 +62,7 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) {...@@ -62,6 +62,7 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) {
62 g->fn_type_table.init(32);62 g->fn_type_table.init(32);
63 g->error_table.init(16);63 g->error_table.init(16);
64 g->generic_table.init(16);64 g->generic_table.init(16);
65 g->llvm_fn_table.init(16);
65 g->memoized_fn_eval_table.init(16);66 g->memoized_fn_eval_table.init(16);
66 g->is_release_build = false;67 g->is_release_build = false;
67 g->is_test_build = false;68 g->is_test_build = false;
...@@ -352,33 +353,14 @@ static void clear_debug_source_node(CodeGen *g) {...@@ -352,33 +353,14 @@ static void clear_debug_source_node(CodeGen *g) {
352 ZigLLVMClearCurrentDebugLocation(g->builder);353 ZigLLVMClearCurrentDebugLocation(g->builder);
353}354}
354355
355enum AddSubMul {
356 AddSubMulAdd = 0,
357 AddSubMulSub = 1,
358 AddSubMulMul = 2,
359};
360
361static size_t bits_index(size_t size_in_bits) {
362 switch (size_in_bits) {
363 case 8:
364 return 0;
365 case 16:
366 return 1;
367 case 32:
368 return 2;
369 case 64:
370 return 3;
371 default:
372 zig_unreachable();
373 }
374}
375
376static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,356static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
377 const char *signed_name, const char *unsigned_name)357 const char *signed_name, const char *unsigned_name)
378{358{
359 char fn_name[64];
360
379 assert(type_entry->id == TypeTableEntryIdInt);361 assert(type_entry->id == TypeTableEntryIdInt);
380 const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name;362 const char *signed_str = type_entry->data.integral.is_signed ? signed_name : unsigned_name;
381 Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%zu", signed_str, type_entry->data.integral.bit_count);363 sprintf(fn_name, "llvm.%s.with.overflow.i%zu", signed_str, type_entry->data.integral.bit_count);
382364
383 LLVMTypeRef return_elem_types[] = {365 LLVMTypeRef return_elem_types[] = {
384 type_entry->type_ref,366 type_entry->type_ref,
...@@ -390,34 +372,39 @@ static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_...@@ -390,34 +372,39 @@ static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_
390 };372 };
391 LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);373 LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
392 LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);374 LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
393 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);375 LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
394 assert(LLVMGetIntrinsicID(fn_val));376 assert(LLVMGetIntrinsicID(fn_val));
395 return fn_val;377 return fn_val;
396}378}
397379
398static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, AddSubMul add_sub_mul) {380static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, AddSubMul add_sub_mul) {
399 assert(type_entry->id == TypeTableEntryIdInt);381 assert(type_entry->id == TypeTableEntryIdInt);
400 // [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64]382
401 size_t index0 = type_entry->data.integral.is_signed ? 0 : 1;383 ZigLLVMFnKey key = {};
402 size_t index1 = add_sub_mul;384 key.id = ZigLLVMFnIdOverflowArithmetic;
403 size_t index2 = bits_index(type_entry->data.integral.bit_count);385 key.data.overflow_arithmetic.is_signed = type_entry->data.integral.is_signed;
404 LLVMValueRef *fn = &g->int_overflow_fns[index0][index1][index2];386 key.data.overflow_arithmetic.add_sub_mul = add_sub_mul;
405 if (*fn) {387 key.data.overflow_arithmetic.bit_count = type_entry->data.integral.bit_count;
406 return *fn;388
407 }389 auto existing_entry = g->llvm_fn_table.maybe_get(key);
390 if (existing_entry)
391 return existing_entry->value;
392
393 LLVMValueRef fn_val;
408 switch (add_sub_mul) {394 switch (add_sub_mul) {
409 case AddSubMulAdd:395 case AddSubMulAdd:
410 *fn = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");396 fn_val = get_arithmetic_overflow_fn(g, type_entry, "sadd", "uadd");
411 break;397 break;
412 case AddSubMulSub:398 case AddSubMulSub:
413 *fn = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");399 fn_val = get_arithmetic_overflow_fn(g, type_entry, "ssub", "usub");
414 break;400 break;
415 case AddSubMulMul:401 case AddSubMulMul:
416 *fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");402 fn_val = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
417 break;403 break;
418
419 }404 }
420 return *fn;405
406 g->llvm_fn_table.put(key, fn_val);
407 return fn_val;
421}408}
422409
423static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, bool is_volatile) {410static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, bool is_volatile) {
...@@ -1388,13 +1375,22 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI...@@ -1388,13 +1375,22 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
1388 bool is_volatile = ptr_type->data.pointer.is_volatile;1375 bool is_volatile = ptr_type->data.pointer.is_volatile;
13891376
1390 uint32_t bit_offset = ptr_type->data.pointer.bit_offset;1377 uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
1391 if (bit_offset == 0)1378 LLVMValueRef containing_int;
1392 return get_handle_value(g, ptr, child_type, is_volatile);1379 if (bit_offset == 0) {
13931380 LLVMValueRef result_val = get_handle_value(g, ptr, child_type, is_volatile);
1394 assert(!handle_is_ptr(child_type));1381 if (LLVMGetTypeKind(LLVMTypeOf(result_val)) == LLVMIntegerTypeKind &&
13951382 LLVMGetTypeKind(child_type->type_ref) == LLVMIntegerTypeKind &&
1396 LLVMValueRef containing_int = LLVMBuildLoad(g->builder, ptr, "");1383 LLVMGetIntTypeWidth(child_type->type_ref) < LLVMGetIntTypeWidth(LLVMTypeOf(result_val)))
1397 LLVMSetVolatile(containing_int, is_volatile);1384 {
1385 containing_int = result_val;
1386 } else {
1387 return result_val;
1388 }
1389 } else {
1390 assert(!handle_is_ptr(child_type));
1391 containing_int = LLVMBuildLoad(g->builder, ptr, "");
1392 LLVMSetVolatile(containing_int, is_volatile);
1393 }
13981394
1399 uint32_t child_bit_count = type_size_bits(g, child_type);1395 uint32_t child_bit_count = type_size_bits(g, child_type);
1400 uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));1396 uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
...@@ -1748,21 +1744,34 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,...@@ -1748,21 +1744,34 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
1748}1744}
17491745
1750static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {1746static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {
1751 // [0-ctz,1-clz][0-8,1-16,2-32,3-64]1747 ZigLLVMFnKey key = {};
1752 size_t index0 = (fn_id == BuiltinFnIdCtz) ? 0 : 1;1748 const char *fn_name;
1753 size_t index1 = bits_index(int_type->data.integral.bit_count);1749 if (fn_id == BuiltinFnIdCtz) {
1754 LLVMValueRef *fn = &g->int_builtin_fns[index0][index1];1750 fn_name = "cttz";
1755 if (!*fn) {1751 key.id = ZigLLVMFnIdCtz;
1756 const char *fn_name = (fn_id == BuiltinFnIdCtz) ? "cttz" : "ctlz";1752 key.data.ctz.bit_count = int_type->data.integral.bit_count;
1757 Buf *llvm_name = buf_sprintf("llvm.%s.i%zu", fn_name, int_type->data.integral.bit_count);1753 } else {
1758 LLVMTypeRef param_types[] = {1754 fn_name = "ctlz";
1759 int_type->type_ref,1755 key.id = ZigLLVMFnIdClz;
1760 LLVMInt1Type(),1756 key.data.clz.bit_count = int_type->data.integral.bit_count;
1761 };
1762 LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false);
1763 *fn = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
1764 }1757 }
1765 return *fn;1758
1759 auto existing_entry = g->llvm_fn_table.maybe_get(key);
1760 if (existing_entry)
1761 return existing_entry->value;
1762
1763 char llvm_name[64];
1764 sprintf(llvm_name, "llvm.%s.i%zu", fn_name, int_type->data.integral.bit_count);
1765 LLVMTypeRef param_types[] = {
1766 int_type->type_ref,
1767 LLVMInt1Type(),
1768 };
1769 LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false);
1770 LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type);
1771
1772 g->llvm_fn_table.put(key, fn_val);
1773
1774 return fn_val;
1766}1775}
17671776
1768static LLVMValueRef ir_render_clz(CodeGen *g, IrExecutable *executable, IrInstructionClz *instruction) {1777static LLVMValueRef ir_render_clz(CodeGen *g, IrExecutable *executable, IrInstructionClz *instruction) {
std/math.zig+5-7
...@@ -71,15 +71,13 @@ fn getReturnTypeForAbs(comptime T: type) -> type {...@@ -71,15 +71,13 @@ fn getReturnTypeForAbs(comptime T: type) -> type {
71fn testMath() {71fn testMath() {
72 @setFnTest(this);72 @setFnTest(this);
7373
74 testMathImpl();
75 comptime testMathImpl();
76}
77
78fn testMathImpl() {
74 assert(%%mulOverflow(i32, 3, 4) == 12);79 assert(%%mulOverflow(i32, 3, 4) == 12);
75 assert(%%addOverflow(i32, 3, 4) == 7);80 assert(%%addOverflow(i32, 3, 4) == 7);
76 assert(%%subOverflow(i32, 3, 4) == -1);81 assert(%%subOverflow(i32, 3, 4) == -1);
77 assert(%%shlOverflow(i32, 0b11, 4) == 0b110000);82 assert(%%shlOverflow(i32, 0b11, 4) == 0b110000);
78
79 comptime {
80 assert(%%mulOverflow(i32, 3, 4) == 12);
81 assert(%%addOverflow(i32, 3, 4) == 7);
82 assert(%%subOverflow(i32, 3, 4) == -1);
83 assert(%%shlOverflow(i32, 0b11, 4) == 0b110000);
84 }
85}83}
test/cases/math.zig+21
...@@ -180,3 +180,24 @@ fn binaryNot() {...@@ -180,3 +180,24 @@ fn binaryNot() {
180fn testBinaryNot(x: u16) {180fn testBinaryNot(x: u16) {
181 assert(~x == 0b0101010101010101);181 assert(~x == 0b0101010101010101);
182}182}
183
184fn smallIntAddition() {
185 @setFnTest(this);
186
187 var x: @intType(false, 2) = 0;
188 assert(x == 0);
189
190 x += 1;
191 assert(x == 1);
192
193 x += 1;
194 assert(x == 2);
195
196 x += 1;
197 assert(x == 3);
198
199 var result: @typeOf(x) = 3;
200 assert(@addWithOverflow(@typeOf(x), x, 1, &result));
201
202 assert(result == 0);
203}
test/cases/struct.zig+5
...@@ -244,11 +244,16 @@ fn bitFieldAccess() {...@@ -244,11 +244,16 @@ fn bitFieldAccess() {
244 .b = 2,244 .b = 2,
245 .c = 3,245 .c = 3,
246 };246 };
247 assert(getA(&data) == 1);
247 assert(getB(&data) == 2);248 assert(getB(&data) == 2);
248 assert(getC(&data) == 3);249 assert(getC(&data) == 3);
249 comptime assert(@sizeOf(BitField1) == 1);250 comptime assert(@sizeOf(BitField1) == 1);
250}251}
251252
253fn getA(data: &const BitField1) -> u3 {
254 return data.a;
255}
256
252fn getB(data: &const BitField1) -> u3 {257fn getB(data: &const BitField1) -> u3 {
253 return data.b;258 return data.b;
254}259}