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 {
12191219uint32_t type_id_hash(TypeId);
12201220bool 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
12221255struct CodeGen {
12231256 LLVMModuleRef module;
12241257 ZigList<ErrorMsg*> errors;
......@@ -1239,6 +1272,7 @@ struct CodeGen {
12391272 HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
12401273 HashMap<GenericFnTypeId *, FnTableEntry *, generic_fn_type_id_hash, generic_fn_type_id_eql> generic_table;
12411274 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
12431277 ZigList<ImportTableEntry *> import_queue;
12441278 size_t import_queue_index;
......@@ -1363,8 +1397,6 @@ struct CodeGen {
13631397 bool error_during_imports;
13641398 uint32_t next_node_index;
13651399 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
13691401 const char **clang_argv;
13701402 size_t clang_argv_len;
src/analyze.cpp+30
......@@ -3997,3 +3997,33 @@ bool type_id_eql(TypeId a, TypeId b) {
39973997 }
39983998 zig_unreachable();
39993999}
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) {
6262 g->fn_type_table.init(32);
6363 g->error_table.init(16);
6464 g->generic_table.init(16);
65 g->llvm_fn_table.init(16);
6566 g->memoized_fn_eval_table.init(16);
6667 g->is_release_build = false;
6768 g->is_test_build = false;
......@@ -352,33 +353,14 @@ static void clear_debug_source_node(CodeGen *g) {
352353 ZigLLVMClearCurrentDebugLocation(g->builder);
353354}
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
376356static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
377357 const char *signed_name, const char *unsigned_name)
378358{
359 char fn_name[64];
360
379361 assert(type_entry->id == TypeTableEntryIdInt);
380362 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
383365 LLVMTypeRef return_elem_types[] = {
384366 type_entry->type_ref,
......@@ -390,34 +372,39 @@ static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, TypeTableEntry *type_
390372 };
391373 LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
392374 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);
394376 assert(LLVMGetIntrinsicID(fn_val));
395377 return fn_val;
396378}
397379
398380static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, AddSubMul add_sub_mul) {
399381 assert(type_entry->id == TypeTableEntryIdInt);
400 // [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64]
401 size_t index0 = type_entry->data.integral.is_signed ? 0 : 1;
402 size_t index1 = add_sub_mul;
403 size_t index2 = bits_index(type_entry->data.integral.bit_count);
404 LLVMValueRef *fn = &g->int_overflow_fns[index0][index1][index2];
405 if (*fn) {
406 return *fn;
407 }
382
383 ZigLLVMFnKey key = {};
384 key.id = ZigLLVMFnIdOverflowArithmetic;
385 key.data.overflow_arithmetic.is_signed = type_entry->data.integral.is_signed;
386 key.data.overflow_arithmetic.add_sub_mul = add_sub_mul;
387 key.data.overflow_arithmetic.bit_count = type_entry->data.integral.bit_count;
388
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;
408394 switch (add_sub_mul) {
409395 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");
411397 break;
412398 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");
414400 break;
415401 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");
417403 break;
418
419404 }
420 return *fn;
405
406 g->llvm_fn_table.put(key, fn_val);
407 return fn_val;
421408}
422409
423410static 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
13881375 bool is_volatile = ptr_type->data.pointer.is_volatile;
13891376
13901377 uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
1391 if (bit_offset == 0)
1392 return get_handle_value(g, ptr, child_type, is_volatile);
1393
1394 assert(!handle_is_ptr(child_type));
1395
1396 LLVMValueRef containing_int = LLVMBuildLoad(g->builder, ptr, "");
1397 LLVMSetVolatile(containing_int, is_volatile);
1378 LLVMValueRef containing_int;
1379 if (bit_offset == 0) {
1380 LLVMValueRef result_val = get_handle_value(g, ptr, child_type, is_volatile);
1381 if (LLVMGetTypeKind(LLVMTypeOf(result_val)) == LLVMIntegerTypeKind &&
1382 LLVMGetTypeKind(child_type->type_ref) == LLVMIntegerTypeKind &&
1383 LLVMGetIntTypeWidth(child_type->type_ref) < LLVMGetIntTypeWidth(LLVMTypeOf(result_val)))
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
13991395 uint32_t child_bit_count = type_size_bits(g, child_type);
14001396 uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
......@@ -1748,21 +1744,34 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
17481744}
17491745
17501746static 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]
1752 size_t index0 = (fn_id == BuiltinFnIdCtz) ? 0 : 1;
1753 size_t index1 = bits_index(int_type->data.integral.bit_count);
1754 LLVMValueRef *fn = &g->int_builtin_fns[index0][index1];
1755 if (!*fn) {
1756 const char *fn_name = (fn_id == BuiltinFnIdCtz) ? "cttz" : "ctlz";
1757 Buf *llvm_name = buf_sprintf("llvm.%s.i%zu", fn_name, int_type->data.integral.bit_count);
1758 LLVMTypeRef param_types[] = {
1759 int_type->type_ref,
1760 LLVMInt1Type(),
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);
1747 ZigLLVMFnKey key = {};
1748 const char *fn_name;
1749 if (fn_id == BuiltinFnIdCtz) {
1750 fn_name = "cttz";
1751 key.id = ZigLLVMFnIdCtz;
1752 key.data.ctz.bit_count = int_type->data.integral.bit_count;
1753 } else {
1754 fn_name = "ctlz";
1755 key.id = ZigLLVMFnIdClz;
1756 key.data.clz.bit_count = int_type->data.integral.bit_count;
17641757 }
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;
17661775}
17671776
17681777static 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 {
7171fn testMath() {
7272 @setFnTest(this);
7373
74 testMathImpl();
75 comptime testMathImpl();
76}
77
78fn testMathImpl() {
7479 assert(%%mulOverflow(i32, 3, 4) == 12);
7580 assert(%%addOverflow(i32, 3, 4) == 7);
7681 assert(%%subOverflow(i32, 3, 4) == -1);
7782 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 }
8583}
test/cases/math.zig+21
......@@ -180,3 +180,24 @@ fn binaryNot() {
180180fn testBinaryNot(x: u16) {
181181 assert(~x == 0b0101010101010101);
182182}
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() {
244244 .b = 2,
245245 .c = 3,
246246 };
247 assert(getA(&data) == 1);
247248 assert(getB(&data) == 2);
248249 assert(getC(&data) == 3);
249250 comptime assert(@sizeOf(BitField1) == 1);
250251}
251252
253fn getA(data: &const BitField1) -> u3 {
254 return data.a;
255}
256
252257fn getB(data: &const BitField1) -> u3 {
253258 return data.b;
254259}