authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-06 23:13:12-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-06 23:13:12-04:00
log157af4332a7b78672ff8ad76a00120455547e2fd
tree0e75d0d9bb111f8e778587a2fb547b74a17c5aa1
parent866c841dd8770bcc12af0aaf946c80819f5e0092

builtin functions for division and remainder division

* add `@divTrunc` and `@divFloor` functions * add `@rem` and `@mod` functions * add compile error for `/` and `%` with signed integers * add `.bit_count` for float primitive types closes #217

21 files changed, 973 insertions(+), 312 deletions(-)

doc/langref.md+65-9
......@@ -502,15 +502,6 @@ This function performs an atomic compare exchange operation.
502502
503503The `fence` function is used to introduce happens-before edges between operations.
504504
505### @divExact(a: T, b: T) -> T
506
507This function performs integer division `a / b` and returns the result.
508
509The caller guarantees that this operation will have no remainder.
510
511In debug mode, a remainder causes a panic. In release mode, a remainder is
512undefined behavior.
513
514505### @truncate(comptime T: type, integer) -> T
515506
516507This function truncates bits from an integer type, resulting in a smaller
......@@ -621,3 +612,68 @@ Converts an enum tag name to a slice of bytes. Example:
621612### @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, field_ptr: &T) -> &ParentType
622613
623614Given a pointer to a field, returns the base pointer of a struct.
615
616### @rem(numerator: T, denominator: T) -> T
617
618Remainder division. For unsigned integers this is the same as
619`numerator % denominator`. Caller guarantees `denominator > 0`.
620
621 * `@rem(-5, 3) == -2`
622 * `@divTrunc(a, b) + @rem(a, b) == a`
623
624See also:
625 * `std.math.rem`
626 * `@mod`
627
628### @mod(numerator: T, denominator: T) -> T
629
630Modulus division. For unsigned integers this is the same as
631`numerator % denominator`. Caller guarantees `denominator > 0`.
632
633 * `@mod(-5, 3) == 1`
634 * `@divFloor(a, b) + @mod(a, b) == a`
635
636See also:
637 * `std.math.mod`
638 * `@rem`
639
640### @divTrunc(numerator: T, denominator: T) -> T
641
642Truncated division. Rounds toward zero. For unsigned integers it is
643the same as `numerator / denominator`. Caller guarantees `denominator != 0` and
644`!(@isInteger(T) and T.is_signed and numerator == @minValue(T) and denominator == -1)`.
645
646 * `@divTrunc(-5, 3) == -1`
647 * `@divTrunc(a, b) + @rem(a, b) == a`
648
649See also:
650 * `std.math.divTrunc`
651 * `@divFloor`
652 * `@divExact`
653
654### @divFloor(numerator: T, denominator: T) -> T
655
656Floored division. Rounds toward negative infinity. For unsigned integers it is
657the same as `numerator / denominator`. Caller guarantees `denominator != 0` and
658`!(@isInteger(T) and T.is_signed and numerator == @minValue(T) and denominator == -1)`.
659
660 * `@divFloor(-5, 3) == -2`
661 * `@divFloor(a, b) + @mod(a, b) == a`
662
663See also:
664 * `std.math.divFloor`
665 * `@divTrunc`
666 * `@divExact`
667
668### @divExact(numerator: T, denominator: T) -> T
669
670Exact division. Caller guarantees `denominator != 0` and
671`@divTrunc(numerator, denominator) * denominator == numerator`.
672
673 * `@divExact(6, 3) == 2`
674 * `@divExact(a, b) * b == a`
675
676See also:
677 * `std.math.divExact`
678 * `@divTrunc`
679 * `@divFloor`
src/all_types.hpp+16-10
......@@ -1195,6 +1195,10 @@ enum BuiltinFnId {
11951195 BuiltinFnIdCmpExchange,
11961196 BuiltinFnIdFence,
11971197 BuiltinFnIdDivExact,
1198 BuiltinFnIdDivTrunc,
1199 BuiltinFnIdDivFloor,
1200 BuiltinFnIdRem,
1201 BuiltinFnIdMod,
11981202 BuiltinFnIdTruncate,
11991203 BuiltinFnIdIntType,
12001204 BuiltinFnIdSetDebugSafety,
......@@ -1270,6 +1274,8 @@ enum ZigLLVMFnId {
12701274 ZigLLVMFnIdCtz,
12711275 ZigLLVMFnIdClz,
12721276 ZigLLVMFnIdOverflowArithmetic,
1277 ZigLLVMFnIdFloor,
1278 ZigLLVMFnIdCeil,
12731279};
12741280
12751281enum AddSubMul {
......@@ -1288,6 +1294,9 @@ struct ZigLLVMFnKey {
12881294 struct {
12891295 uint32_t bit_count;
12901296 } clz;
1297 struct {
1298 uint32_t bit_count;
1299 } floor_ceil;
12911300 struct {
12921301 AddSubMul add_sub_mul;
12931302 uint32_t bit_count;
......@@ -1746,7 +1755,6 @@ enum IrInstructionId {
17461755 IrInstructionIdEmbedFile,
17471756 IrInstructionIdCmpxchg,
17481757 IrInstructionIdFence,
1749 IrInstructionIdDivExact,
17501758 IrInstructionIdTruncate,
17511759 IrInstructionIdIntType,
17521760 IrInstructionIdBoolNot,
......@@ -1897,8 +1905,13 @@ enum IrBinOp {
18971905 IrBinOpSubWrap,
18981906 IrBinOpMult,
18991907 IrBinOpMultWrap,
1900 IrBinOpDiv,
1901 IrBinOpRem,
1908 IrBinOpDivUnspecified,
1909 IrBinOpDivExact,
1910 IrBinOpDivTrunc,
1911 IrBinOpDivFloor,
1912 IrBinOpRemUnspecified,
1913 IrBinOpRemRem,
1914 IrBinOpRemMod,
19021915 IrBinOpArrayCat,
19031916 IrBinOpArrayMult,
19041917};
......@@ -2250,13 +2263,6 @@ struct IrInstructionFence {
22502263 AtomicOrder order;
22512264};
22522265
2253struct IrInstructionDivExact {
2254 IrInstruction base;
2255
2256 IrInstruction *op1;
2257 IrInstruction *op2;
2258};
2259
22602266struct IrInstructionTruncate {
22612267 IrInstruction base;
22622268
src/analyze.cpp+7
......@@ -4228,6 +4228,10 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
42284228 return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;
42294229 case ZigLLVMFnIdClz:
42304230 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
4231 case ZigLLVMFnIdFloor:
4232 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168;
4233 case ZigLLVMFnIdCeil:
4234 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089;
42314235 case ZigLLVMFnIdOverflowArithmetic:
42324236 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
42334237 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
......@@ -4244,6 +4248,9 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
42444248 return a.data.ctz.bit_count == b.data.ctz.bit_count;
42454249 case ZigLLVMFnIdClz:
42464250 return a.data.clz.bit_count == b.data.clz.bit_count;
4251 case ZigLLVMFnIdFloor:
4252 case ZigLLVMFnIdCeil:
4253 return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count;
42474254 case ZigLLVMFnIdOverflowArithmetic:
42484255 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
42494256 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
src/bignum.cpp+61-3
......@@ -204,6 +204,23 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
204204
205205 if (dest->kind == BigNumKindFloat) {
206206 dest->data.x_float = op1->data.x_float / op2->data.x_float;
207 } else {
208 return bignum_div_trunc(dest, op1, op2);
209 }
210 return false;
211}
212
213bool bignum_div_trunc(BigNum *dest, BigNum *op1, BigNum *op2) {
214 assert(op1->kind == op2->kind);
215 dest->kind = op1->kind;
216
217 if (dest->kind == BigNumKindFloat) {
218 double result = op1->data.x_float / op2->data.x_float;
219 if (result >= 0) {
220 dest->data.x_float = floor(result);
221 } else {
222 dest->data.x_float = ceil(result);
223 }
207224 } else {
208225 dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
209226 dest->is_negative = op1->is_negative != op2->is_negative;
......@@ -212,6 +229,29 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
212229 return false;
213230}
214231
232bool bignum_div_floor(BigNum *dest, BigNum *op1, BigNum *op2) {
233 assert(op1->kind == op2->kind);
234 dest->kind = op1->kind;
235
236 if (dest->kind == BigNumKindFloat) {
237 dest->data.x_float = floor(op1->data.x_float / op2->data.x_float);
238 } else {
239 if (op1->is_negative != op2->is_negative) {
240 uint64_t result = op1->data.x_uint / op2->data.x_uint;
241 if (result * op2->data.x_uint == op1->data.x_uint) {
242 dest->data.x_uint = result;
243 } else {
244 dest->data.x_uint = result + 1;
245 }
246 dest->is_negative = true;
247 } else {
248 dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
249 dest->is_negative = false;
250 }
251 }
252 return false;
253}
254
215255bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
216256 assert(op1->kind == op2->kind);
217257 dest->kind = op1->kind;
......@@ -219,10 +259,28 @@ bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
219259 if (dest->kind == BigNumKindFloat) {
220260 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
221261 } else {
222 if (op1->is_negative || op2->is_negative) {
223 zig_panic("TODO handle remainder division with negative numbers");
224 }
262 assert(!op2->is_negative);
225263 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
264 dest->is_negative = op1->is_negative;
265 bignum_normalize(dest);
266 }
267 return false;
268}
269
270bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
271 assert(op1->kind == op2->kind);
272 dest->kind = op1->kind;
273
274 if (dest->kind == BigNumKindFloat) {
275 dest->data.x_float = fmod(fmod(op1->data.x_float, op2->data.x_float) + op2->data.x_float, op2->data.x_float);
276 } else {
277 assert(!op2->is_negative);
278 if (op1->is_negative) {
279 dest->data.x_uint = (op2->data.x_uint - op1->data.x_uint % op2->data.x_uint) % op2->data.x_uint;
280 } else {
281 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
282 }
283 dest->is_negative = false;
226284 bignum_normalize(dest);
227285 }
228286 return false;
src/bignum.hpp+3
......@@ -37,7 +37,10 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);
3737bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);
3838bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);
3939bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2);
40bool bignum_div_trunc(BigNum *dest, BigNum *op1, BigNum *op2);
41bool bignum_div_floor(BigNum *dest, BigNum *op1, BigNum *op2);
4042bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2);
43bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2);
4144
4245bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);
4346bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2);
src/codegen.cpp+178-68
......@@ -538,6 +538,35 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
538538 return fn_val;
539539}
540540
541static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
542 assert(type_entry->id == TypeTableEntryIdFloat);
543
544 ZigLLVMFnKey key = {};
545 key.id = fn_id;
546 key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count;
547
548 auto existing_entry = g->llvm_fn_table.maybe_get(key);
549 if (existing_entry)
550 return existing_entry->value;
551
552 const char *name;
553 if (fn_id == ZigLLVMFnIdFloor) {
554 name = "floor";
555 } else if (fn_id == ZigLLVMFnIdCeil) {
556 name = "ceil";
557 } else {
558 zig_unreachable();
559 }
560
561 char fn_name[64];
562 sprintf(fn_name, "llvm.%s.f%zu", name, type_entry->data.floating.bit_count);
563 LLVMTypeRef fn_type = LLVMFunctionType(type_entry->type_ref, &type_entry->type_ref, 1, false);
564 LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
565
566 g->llvm_fn_table.put(key, fn_val);
567 return fn_val;
568}
569
541570static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, bool is_volatile) {
542571 if (type_has_bits(type)) {
543572 if (handle_is_ptr(type)) {
......@@ -618,7 +647,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
618647 case PanicMsgIdDivisionByZero:
619648 return buf_create_from_str("division by zero");
620649 case PanicMsgIdRemainderDivisionByZero:
621 return buf_create_from_str("remainder division by zero");
650 return buf_create_from_str("remainder division by zero or negative value");
622651 case PanicMsgIdExactDivisionRemainder:
623652 return buf_create_from_str("exact division produced remainder");
624653 case PanicMsgIdSliceWidenRemainder:
......@@ -1099,12 +1128,34 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
10991128 return result;
11001129}
11011130
1131static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_entry) {
1132 if (type_entry->id == TypeTableEntryIdInt)
1133 return val;
1134
1135 LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor);
1136 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
1137}
1138
1139static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_entry) {
1140 if (type_entry->id == TypeTableEntryIdInt)
1141 return val;
1142
1143 LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil);
1144 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
1145}
1146
1147enum DivKind {
1148 DivKindFloat,
1149 DivKindTrunc,
1150 DivKindFloor,
1151 DivKindExact,
1152};
1153
11021154static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,
1103 TypeTableEntry *type_entry, bool exact)
1155 TypeTableEntry *type_entry, DivKind div_kind)
11041156{
1105
1157 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
11061158 if (want_debug_safety) {
1107 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
11081159 LLVMValueRef is_zero_bit;
11091160 if (type_entry->id == TypeTableEntryIdInt) {
11101161 is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
......@@ -1140,55 +1191,111 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val
11401191 }
11411192
11421193 if (type_entry->id == TypeTableEntryIdFloat) {
1143 assert(!exact);
1144 return LLVMBuildFDiv(g->builder, val1, val2, "");
1194 LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, "");
1195 switch (div_kind) {
1196 case DivKindFloat:
1197 return result;
1198 case DivKindExact:
1199 if (want_debug_safety) {
1200 LLVMValueRef floored = gen_floor(g, result, type_entry);
1201 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
1202 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
1203 LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");
1204
1205 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
1206
1207 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1208 gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder);
1209
1210 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1211 }
1212 return result;
1213 case DivKindTrunc:
1214 {
1215 LLVMValueRef floored = gen_floor(g, result, type_entry);
1216 LLVMValueRef ceiled = gen_ceil(g, result, type_entry);
1217 LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, "");
1218 return LLVMBuildSelect(g->builder, ltz, ceiled, floored, "");
1219 }
1220 case DivKindFloor:
1221 return gen_floor(g, result, type_entry);
1222 }
1223 zig_unreachable();
11451224 }
11461225
11471226 assert(type_entry->id == TypeTableEntryIdInt);
11481227
1149 if (exact) {
1150 if (want_debug_safety) {
1151 LLVMValueRef remainder_val;
1228 switch (div_kind) {
1229 case DivKindFloat:
1230 zig_unreachable();
1231 case DivKindTrunc:
11521232 if (type_entry->data.integral.is_signed) {
1153 remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
1233 return LLVMBuildSDiv(g->builder, val1, val2, "");
11541234 } else {
1155 remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
1235 return LLVMBuildUDiv(g->builder, val1, val2, "");
11561236 }
1157 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
1158 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
1237 case DivKindExact:
1238 if (want_debug_safety) {
1239 LLVMValueRef remainder_val;
1240 if (type_entry->data.integral.is_signed) {
1241 remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");
1242 } else {
1243 remainder_val = LLVMBuildURem(g->builder, val1, val2, "");
1244 }
1245 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
11591246
1160 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
1161 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
1162 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
1247 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
1248 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
1249 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
11631250
1164 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1165 gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder);
1251 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1252 gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder);
11661253
1167 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1168 }
1169 if (type_entry->data.integral.is_signed) {
1170 return LLVMBuildExactSDiv(g->builder, val1, val2, "");
1171 } else {
1172 return LLVMBuildExactUDiv(g->builder, val1, val2, "");
1173 }
1174 } else {
1175 if (type_entry->data.integral.is_signed) {
1176 return LLVMBuildSDiv(g->builder, val1, val2, "");
1177 } else {
1178 return LLVMBuildUDiv(g->builder, val1, val2, "");
1179 }
1254 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1255 }
1256 if (type_entry->data.integral.is_signed) {
1257 return LLVMBuildExactSDiv(g->builder, val1, val2, "");
1258 } else {
1259 return LLVMBuildExactUDiv(g->builder, val1, val2, "");
1260 }
1261 case DivKindFloor:
1262 {
1263 if (!type_entry->data.integral.is_signed) {
1264 return LLVMBuildUDiv(g->builder, val1, val2, "");
1265 }
1266 // const result = @divTrunc(a, b);
1267 // if (result >= 0 or result * b == a)
1268 // return result;
1269 // else
1270 // return result - 1;
1271
1272 LLVMValueRef result = LLVMBuildSDiv(g->builder, val1, val2, "");
1273 LLVMValueRef is_pos = LLVMBuildICmp(g->builder, LLVMIntSGE, result, zero, "");
1274 LLVMValueRef orig_num = LLVMBuildNSWMul(g->builder, result, val2, "");
1275 LLVMValueRef orig_ok = LLVMBuildICmp(g->builder, LLVMIntEQ, orig_num, val1, "");
1276 LLVMValueRef ok_bit = LLVMBuildOr(g->builder, orig_ok, is_pos, "");
1277 LLVMValueRef one = LLVMConstInt(type_entry->type_ref, 1, true);
1278 LLVMValueRef result_minus_1 = LLVMBuildNSWSub(g->builder, result, one, "");
1279 return LLVMBuildSelect(g->builder, ok_bit, result, result_minus_1, "");
1280 }
11801281 }
1282 zig_unreachable();
11811283}
11821284
1285enum RemKind {
1286 RemKindRem,
1287 RemKindMod,
1288};
1289
11831290static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,
1184 TypeTableEntry *type_entry)
1291 TypeTableEntry *type_entry, RemKind rem_kind)
11851292{
1186
1293 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
11871294 if (want_debug_safety) {
1188 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
11891295 LLVMValueRef is_zero_bit;
11901296 if (type_entry->id == TypeTableEntryIdInt) {
1191 is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");
1297 LLVMIntPredicate pred = type_entry->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ;
1298 is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, "");
11921299 } else if (type_entry->id == TypeTableEntryIdFloat) {
11931300 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
11941301 } else {
......@@ -1202,30 +1309,30 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val
12021309 gen_debug_safety_crash(g, PanicMsgIdRemainderDivisionByZero);
12031310
12041311 LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block);
1205
1206 if (type_entry->id == TypeTableEntryIdInt && type_entry->data.integral.is_signed) {
1207 LLVMValueRef neg_1_value = LLVMConstInt(type_entry->type_ref, -1, true);
1208 LLVMValueRef int_min_value = LLVMConstInt(type_entry->type_ref, min_signed_val(type_entry), true);
1209 LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemOverflowOk");
1210 LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemOverflowFail");
1211 LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, "");
1212 LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, "");
1213 LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, "");
1214 LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block);
1215
1216 LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block);
1217 gen_debug_safety_crash(g, PanicMsgIdIntegerOverflow);
1218
1219 LLVMPositionBuilderAtEnd(g->builder, overflow_ok_block);
1220 }
12211312 }
12221313
12231314 if (type_entry->id == TypeTableEntryIdFloat) {
1224 return LLVMBuildFRem(g->builder, val1, val2, "");
1315 if (rem_kind == RemKindRem) {
1316 return LLVMBuildFRem(g->builder, val1, val2, "");
1317 } else {
1318 LLVMValueRef a = LLVMBuildFRem(g->builder, val1, val2, "");
1319 LLVMValueRef b = LLVMBuildFAdd(g->builder, a, val2, "");
1320 LLVMValueRef c = LLVMBuildFRem(g->builder, b, val2, "");
1321 LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, "");
1322 return LLVMBuildSelect(g->builder, ltz, c, a, "");
1323 }
12251324 } else {
12261325 assert(type_entry->id == TypeTableEntryIdInt);
12271326 if (type_entry->data.integral.is_signed) {
1228 return LLVMBuildSRem(g->builder, val1, val2, "");
1327 if (rem_kind == RemKindRem) {
1328 return LLVMBuildSRem(g->builder, val1, val2, "");
1329 } else {
1330 LLVMValueRef a = LLVMBuildSRem(g->builder, val1, val2, "");
1331 LLVMValueRef b = LLVMBuildNSWAdd(g->builder, a, val2, "");
1332 LLVMValueRef c = LLVMBuildSRem(g->builder, b, val2, "");
1333 LLVMValueRef ltz = LLVMBuildICmp(g->builder, LLVMIntSLT, val1, zero, "");
1334 return LLVMBuildSelect(g->builder, ltz, c, a, "");
1335 }
12291336 } else {
12301337 return LLVMBuildURem(g->builder, val1, val2, "");
12311338 }
......@@ -1252,6 +1359,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
12521359 case IrBinOpInvalid:
12531360 case IrBinOpArrayCat:
12541361 case IrBinOpArrayMult:
1362 case IrBinOpRemUnspecified:
12551363 zig_unreachable();
12561364 case IrBinOpBoolOr:
12571365 return LLVMBuildOr(g->builder, op1_value, op2_value, "");
......@@ -1367,10 +1475,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
13671475 } else {
13681476 zig_unreachable();
13691477 }
1370 case IrBinOpDiv:
1371 return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, false);
1372 case IrBinOpRem:
1373 return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry);
1478 case IrBinOpDivUnspecified:
1479 return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloat);
1480 case IrBinOpDivExact:
1481 return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindExact);
1482 case IrBinOpDivTrunc:
1483 return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindTrunc);
1484 case IrBinOpDivFloor:
1485 return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloor);
1486 case IrBinOpRemRem:
1487 return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindRem);
1488 case IrBinOpRemMod:
1489 return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindMod);
13741490 }
13751491 zig_unreachable();
13761492}
......@@ -2353,14 +2469,6 @@ static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInst
23532469 return nullptr;
23542470}
23552471
2356static LLVMValueRef ir_render_div_exact(CodeGen *g, IrExecutable *executable, IrInstructionDivExact *instruction) {
2357 LLVMValueRef op1_val = ir_llvm_value(g, instruction->op1);
2358 LLVMValueRef op2_val = ir_llvm_value(g, instruction->op2);
2359
2360 bool want_debug_safety = ir_want_debug_safety(g, &instruction->base);
2361 return gen_div(g, want_debug_safety, op1_val, op2_val, instruction->base.value.type, true);
2362}
2363
23642472static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) {
23652473 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
23662474 TypeTableEntry *dest_type = instruction->base.value.type;
......@@ -2965,8 +3073,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
29653073 return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction);
29663074 case IrInstructionIdFence:
29673075 return ir_render_fence(g, executable, (IrInstructionFence *)instruction);
2968 case IrInstructionIdDivExact:
2969 return ir_render_div_exact(g, executable, (IrInstructionDivExact *)instruction);
29703076 case IrInstructionIdTruncate:
29713077 return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
29723078 case IrInstructionIdBoolNot:
......@@ -4320,7 +4426,6 @@ static void define_builtin_fns(CodeGen *g) {
43204426 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
43214427 create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
43224428 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);
4323 create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2);
43244429 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);
43254430 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
43264431 create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
......@@ -4335,6 +4440,11 @@ static void define_builtin_fns(CodeGen *g) {
43354440 create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1);
43364441 create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3);
43374442 create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2);
4443 create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2);
4444 create_builtin_fn(g, BuiltinFnIdDivTrunc, "divTrunc", 2);
4445 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
4446 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
4447 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
43384448}
43394449
43404450static const char *bool_to_str(bool b) {
src/error.cpp+2
......@@ -23,6 +23,8 @@ const char *err_str(int err) {
2323 case ErrorOverflow: return "overflow";
2424 case ErrorPathAlreadyExists: return "path already exists";
2525 case ErrorUnexpected: return "unexpected error";
26 case ErrorExactDivRemainder: return "exact division had a remainder";
27 case ErrorNegativeDenominator: return "negative denominator";
2628 }
2729 return "(invalid error)";
2830}
src/error.hpp+2
......@@ -23,6 +23,8 @@ enum Error {
2323 ErrorOverflow,
2424 ErrorPathAlreadyExists,
2525 ErrorUnexpected,
26 ErrorExactDivRemainder,
27 ErrorNegativeDenominator,
2628};
2729
2830const char *err_str(int err);
src/ir.cpp+201-142
......@@ -400,10 +400,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) {
400400 return IrInstructionIdFence;
401401}
402402
403static constexpr IrInstructionId ir_instruction_id(IrInstructionDivExact *) {
404 return IrInstructionIdDivExact;
405}
406
407403static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) {
408404 return IrInstructionIdTruncate;
409405}
......@@ -1628,23 +1624,6 @@ static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_ins
16281624 return new_instruction;
16291625}
16301626
1631static IrInstruction *ir_build_div_exact(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *op1, IrInstruction *op2) {
1632 IrInstructionDivExact *instruction = ir_build_instruction<IrInstructionDivExact>(irb, scope, source_node);
1633 instruction->op1 = op1;
1634 instruction->op2 = op2;
1635
1636 ir_ref_instruction(op1, irb->current_basic_block);
1637 ir_ref_instruction(op2, irb->current_basic_block);
1638
1639 return &instruction->base;
1640}
1641
1642static IrInstruction *ir_build_div_exact_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *op1, IrInstruction *op2) {
1643 IrInstruction *new_instruction = ir_build_div_exact(irb, old_instruction->scope, old_instruction->source_node, op1, op2);
1644 ir_link_new_instruction(new_instruction, old_instruction);
1645 return new_instruction;
1646}
1647
16481627static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
16491628 IrInstructionTruncate *instruction = ir_build_instruction<IrInstructionTruncate>(irb, scope, source_node);
16501629 instruction->dest_type = dest_type;
......@@ -2597,14 +2576,6 @@ static IrInstruction *ir_instruction_fence_get_dep(IrInstructionFence *instructi
25972576 }
25982577}
25992578
2600static IrInstruction *ir_instruction_divexact_get_dep(IrInstructionDivExact *instruction, size_t index) {
2601 switch (index) {
2602 case 0: return instruction->op1;
2603 case 1: return instruction->op2;
2604 default: return nullptr;
2605 }
2606}
2607
26082579static IrInstruction *ir_instruction_truncate_get_dep(IrInstructionTruncate *instruction, size_t index) {
26092580 switch (index) {
26102581 case 0: return instruction->dest_type;
......@@ -3022,8 +2993,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
30222993 return ir_instruction_cmpxchg_get_dep((IrInstructionCmpxchg *) instruction, index);
30232994 case IrInstructionIdFence:
30242995 return ir_instruction_fence_get_dep((IrInstructionFence *) instruction, index);
3025 case IrInstructionIdDivExact:
3026 return ir_instruction_divexact_get_dep((IrInstructionDivExact *) instruction, index);
30272996 case IrInstructionIdTruncate:
30282997 return ir_instruction_truncate_get_dep((IrInstructionTruncate *) instruction, index);
30292998 case IrInstructionIdIntType:
......@@ -3644,9 +3613,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
36443613 case BinOpTypeAssignTimesWrap:
36453614 return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap);
36463615 case BinOpTypeAssignDiv:
3647 return ir_gen_assign_op(irb, scope, node, IrBinOpDiv);
3616 return ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified);
36483617 case BinOpTypeAssignMod:
3649 return ir_gen_assign_op(irb, scope, node, IrBinOpRem);
3618 return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified);
36503619 case BinOpTypeAssignPlus:
36513620 return ir_gen_assign_op(irb, scope, node, IrBinOpAdd);
36523621 case BinOpTypeAssignPlusWrap:
......@@ -3712,9 +3681,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
37123681 case BinOpTypeMultWrap:
37133682 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap);
37143683 case BinOpTypeDiv:
3715 return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv);
3684 return ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified);
37163685 case BinOpTypeMod:
3717 return ir_gen_bin_op_id(irb, scope, node, IrBinOpRem);
3686 return ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified);
37183687 case BinOpTypeArrayCat:
37193688 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
37203689 case BinOpTypeArrayMult:
......@@ -4138,7 +4107,63 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
41384107 if (arg1_value == irb->codegen->invalid_instruction)
41394108 return arg1_value;
41404109
4141 return ir_build_div_exact(irb, scope, node, arg0_value, arg1_value);
4110 return ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
4111 }
4112 case BuiltinFnIdDivTrunc:
4113 {
4114 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4115 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4116 if (arg0_value == irb->codegen->invalid_instruction)
4117 return arg0_value;
4118
4119 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4120 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4121 if (arg1_value == irb->codegen->invalid_instruction)
4122 return arg1_value;
4123
4124 return ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
4125 }
4126 case BuiltinFnIdDivFloor:
4127 {
4128 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4129 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4130 if (arg0_value == irb->codegen->invalid_instruction)
4131 return arg0_value;
4132
4133 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4134 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4135 if (arg1_value == irb->codegen->invalid_instruction)
4136 return arg1_value;
4137
4138 return ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
4139 }
4140 case BuiltinFnIdRem:
4141 {
4142 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4143 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4144 if (arg0_value == irb->codegen->invalid_instruction)
4145 return arg0_value;
4146
4147 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4148 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4149 if (arg1_value == irb->codegen->invalid_instruction)
4150 return arg1_value;
4151
4152 return ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
4153 }
4154 case BuiltinFnIdMod:
4155 {
4156 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4157 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4158 if (arg0_value == irb->codegen->invalid_instruction)
4159 return arg0_value;
4160
4161 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4162 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4163 if (arg1_value == irb->codegen->invalid_instruction)
4164 return arg1_value;
4165
4166 return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
41424167 }
41434168 case BuiltinFnIdTruncate:
41444169 {
......@@ -8024,32 +8049,70 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
80248049 return ira->codegen->builtin_types.entry_bool;
80258050}
80268051
8052enum EvalBigNumSpecial {
8053 EvalBigNumSpecialNone,
8054 EvalBigNumSpecialWrapping,
8055 EvalBigNumSpecialExact,
8056};
8057
80278058static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
80288059 ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *),
8029 TypeTableEntry *type, bool wrapping_op)
8060 TypeTableEntry *type, EvalBigNumSpecial special)
80308061{
80318062 bool is_int = false;
80328063 bool is_float = false;
8033 if (bignum_fn == bignum_div || bignum_fn == bignum_rem) {
8034 if (type->id == TypeTableEntryIdInt ||
8035 type->id == TypeTableEntryIdNumLitInt)
8036 {
8037 is_int = true;
8038 } else if (type->id == TypeTableEntryIdFloat ||
8039 type->id == TypeTableEntryIdNumLitFloat)
8040 {
8041 is_float = true;
8042 }
8064 if (type->id == TypeTableEntryIdInt ||
8065 type->id == TypeTableEntryIdNumLitInt)
8066 {
8067 is_int = true;
8068 } else if (type->id == TypeTableEntryIdFloat ||
8069 type->id == TypeTableEntryIdNumLitFloat)
8070 {
8071 is_float = true;
8072 } else {
8073 zig_unreachable();
8074 }
8075 if (bignum_fn == bignum_div || bignum_fn == bignum_rem || bignum_fn == bignum_mod ||
8076 bignum_fn == bignum_div_trunc || bignum_fn == bignum_div_floor)
8077 {
80438078 if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
80448079 (is_float && op2_val->data.x_bignum.data.x_float == 0.0))
80458080 {
80468081 return ErrorDivByZero;
80478082 }
80488083 }
8084 if (bignum_fn == bignum_rem || bignum_fn == bignum_mod) {
8085 BigNum zero;
8086 if (is_float) {
8087 bignum_init_float(&zero, 0.0);
8088 } else {
8089 bignum_init_unsigned(&zero, 0);
8090 }
8091 if (bignum_cmp_lt(&op2_val->data.x_bignum, &zero)) {
8092 return ErrorNegativeDenominator;
8093 }
8094 }
8095
8096 if (special == EvalBigNumSpecialExact) {
8097 assert(bignum_fn == bignum_div);
8098 BigNum remainder;
8099 if (bignum_rem(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
8100 return ErrorOverflow;
8101 }
8102 BigNum zero;
8103 if (is_float) {
8104 bignum_init_float(&zero, 0.0);
8105 } else {
8106 bignum_init_unsigned(&zero, 0);
8107 }
8108 if (bignum_cmp_neq(&remainder, &zero)) {
8109 return ErrorExactDivRemainder;
8110 }
8111 }
80498112
80508113 bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
80518114 if (overflow) {
8052 if (wrapping_op) {
8115 if (special == EvalBigNumSpecialWrapping) {
80538116 zig_panic("TODO compiler bug, implement compile-time wrapping arithmetic for >= 64 bit ints");
80548117 } else {
80558118 return ErrorOverflow;
......@@ -8059,7 +8122,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
80598122 if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum,
80608123 type->data.integral.bit_count, type->data.integral.is_signed))
80618124 {
8062 if (wrapping_op) {
8125 if (special == EvalBigNumSpecialWrapping) {
80638126 if (type->data.integral.is_signed) {
80648127 out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
80658128 out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative;
......@@ -8093,35 +8156,44 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val,
80938156 case IrBinOpCmpGreaterOrEq:
80948157 case IrBinOpArrayCat:
80958158 case IrBinOpArrayMult:
8159 case IrBinOpRemUnspecified:
80968160 zig_unreachable();
80978161 case IrBinOpBinOr:
8098 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_or, canon_type, false);
8162 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_or, canon_type, EvalBigNumSpecialNone);
80998163 case IrBinOpBinXor:
8100 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_xor, canon_type, false);
8164 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_xor, canon_type, EvalBigNumSpecialNone);
81018165 case IrBinOpBinAnd:
8102 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_and, canon_type, false);
8166 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_and, canon_type, EvalBigNumSpecialNone);
81038167 case IrBinOpBitShiftLeft:
8104 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, false);
8168 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, EvalBigNumSpecialNone);
81058169 case IrBinOpBitShiftLeftWrap:
8106 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, true);
8170 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shl, canon_type, EvalBigNumSpecialWrapping);
81078171 case IrBinOpBitShiftRight:
8108 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shr, canon_type, false);
8172 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_shr, canon_type, EvalBigNumSpecialNone);
81098173 case IrBinOpAdd:
8110 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, false);
8174 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, EvalBigNumSpecialNone);
81118175 case IrBinOpAddWrap:
8112 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, true);
8176 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_add, canon_type, EvalBigNumSpecialWrapping);
81138177 case IrBinOpSub:
8114 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, false);
8178 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, EvalBigNumSpecialNone);
81158179 case IrBinOpSubWrap:
8116 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, true);
8180 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_sub, canon_type, EvalBigNumSpecialWrapping);
81178181 case IrBinOpMult:
8118 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, false);
8182 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialNone);
81198183 case IrBinOpMultWrap:
8120 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, true);
8121 case IrBinOpDiv:
8122 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, false);
8123 case IrBinOpRem:
8124 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, false);
8184 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialWrapping);
8185 case IrBinOpDivUnspecified:
8186 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialNone);
8187 case IrBinOpDivTrunc:
8188 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_trunc, canon_type, EvalBigNumSpecialNone);
8189 case IrBinOpDivFloor:
8190 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_floor, canon_type, EvalBigNumSpecialNone);
8191 case IrBinOpDivExact:
8192 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialExact);
8193 case IrBinOpRemRem:
8194 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, EvalBigNumSpecialNone);
8195 case IrBinOpRemMod:
8196 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, canon_type, EvalBigNumSpecialNone);
81258197 }
81268198 zig_unreachable();
81278199}
......@@ -8135,6 +8207,31 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
81358207 return resolved_type;
81368208 IrBinOp op_id = bin_op_instruction->op_id;
81378209
8210 bool is_int = resolved_type->id == TypeTableEntryIdInt || resolved_type->id == TypeTableEntryIdNumLitInt;
8211 bool is_signed = ((resolved_type->id == TypeTableEntryIdInt && resolved_type->data.integral.is_signed) ||
8212 (resolved_type->id == TypeTableEntryIdNumLitInt &&
8213 (op1->value.data.x_bignum.is_negative || op2->value.data.x_bignum.is_negative)));
8214 if (op_id == IrBinOpDivUnspecified) {
8215 if (is_signed) {
8216 ir_add_error(ira, &bin_op_instruction->base,
8217 buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
8218 buf_ptr(&op1->value.type->name),
8219 buf_ptr(&op2->value.type->name)));
8220 return ira->codegen->builtin_types.entry_invalid;
8221 } else if (is_int) {
8222 op_id = IrBinOpDivTrunc;
8223 }
8224 } else if (op_id == IrBinOpRemUnspecified) {
8225 if (is_signed) {
8226 ir_add_error(ira, &bin_op_instruction->base,
8227 buf_sprintf("remainder division with '%s' and '%s': signed integers must use @rem or @mod",
8228 buf_ptr(&op1->value.type->name),
8229 buf_ptr(&op2->value.type->name)));
8230 return ira->codegen->builtin_types.entry_invalid;
8231 }
8232 op_id = IrBinOpRemRem;
8233 }
8234
81388235 if (resolved_type->id == TypeTableEntryIdInt ||
81398236 resolved_type->id == TypeTableEntryIdNumLitInt)
81408237 {
......@@ -8144,8 +8241,12 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
81448241 (op_id == IrBinOpAdd ||
81458242 op_id == IrBinOpSub ||
81468243 op_id == IrBinOpMult ||
8147 op_id == IrBinOpDiv ||
8148 op_id == IrBinOpRem))
8244 op_id == IrBinOpDivUnspecified ||
8245 op_id == IrBinOpDivTrunc ||
8246 op_id == IrBinOpDivFloor ||
8247 op_id == IrBinOpDivExact ||
8248 op_id == IrBinOpRemRem ||
8249 op_id == IrBinOpRemMod))
81498250 {
81508251 // float
81518252 } else {
......@@ -8176,20 +8277,25 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
81768277 int err;
81778278 if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, out_val))) {
81788279 if (err == ErrorDivByZero) {
8179 ir_add_error_node(ira, bin_op_instruction->base.source_node,
8180 buf_sprintf("division by zero is undefined"));
8280 ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("division by zero is undefined"));
81818281 return ira->codegen->builtin_types.entry_invalid;
81828282 } else if (err == ErrorOverflow) {
8183 ir_add_error_node(ira, bin_op_instruction->base.source_node,
8184 buf_sprintf("operation caused overflow"));
8283 ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow"));
8284 return ira->codegen->builtin_types.entry_invalid;
8285 } else if (err == ErrorExactDivRemainder) {
8286 ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact division had a remainder"));
8287 return ira->codegen->builtin_types.entry_invalid;
8288 } else if (err == ErrorNegativeDenominator) {
8289 ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("negative denominator"));
81858290 return ira->codegen->builtin_types.entry_invalid;
8291 } else {
8292 zig_unreachable();
81868293 }
81878294 return ira->codegen->builtin_types.entry_invalid;
81888295 }
81898296
81908297 ir_num_lit_fits_in_other_type(ira, &bin_op_instruction->base, resolved_type);
81918298 return resolved_type;
8192
81938299 }
81948300
81958301 ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id,
......@@ -8197,6 +8303,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
81978303 return resolved_type;
81988304}
81998305
8306
82008307static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) {
82018308 IrInstruction *op1 = instruction->op1->other;
82028309 TypeTableEntry *op1_type = op1->value.type;
......@@ -8416,8 +8523,13 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
84168523 case IrBinOpSubWrap:
84178524 case IrBinOpMult:
84188525 case IrBinOpMultWrap:
8419 case IrBinOpDiv:
8420 case IrBinOpRem:
8526 case IrBinOpDivUnspecified:
8527 case IrBinOpDivTrunc:
8528 case IrBinOpDivFloor:
8529 case IrBinOpDivExact:
8530 case IrBinOpRemUnspecified:
8531 case IrBinOpRemRem:
8532 case IrBinOpRemMod:
84218533 return ir_analyze_bin_op_math(ira, bin_op_instruction);
84228534 case IrBinOpArrayCat:
84238535 return ir_analyze_array_cat(ira, bin_op_instruction);
......@@ -10007,6 +10119,21 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
1000710119 buf_ptr(&child_type->name), buf_ptr(field_name)));
1000810120 return ira->codegen->builtin_types.entry_invalid;
1000910121 }
10122 } else if (child_type->id == TypeTableEntryIdFloat) {
10123 if (buf_eql_str(field_name, "bit_count")) {
10124 bool ptr_is_const = true;
10125 bool ptr_is_volatile = false;
10126 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
10127 create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
10128 child_type->data.floating.bit_count, false),
10129 ira->codegen->builtin_types.entry_num_lit_int,
10130 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
10131 } else {
10132 ir_add_error(ira, &field_ptr_instruction->base,
10133 buf_sprintf("type '%s' has no member called '%s'",
10134 buf_ptr(&child_type->name), buf_ptr(field_name)));
10135 return ira->codegen->builtin_types.entry_invalid;
10136 }
1001010137 } else {
1001110138 ir_add_error(ira, &field_ptr_instruction->base,
1001210139 buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name)));
......@@ -12030,71 +12157,6 @@ static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructio
1203012157 return ira->codegen->builtin_types.entry_void;
1203112158}
1203212159
12033static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstructionDivExact *instruction) {
12034 IrInstruction *op1 = instruction->op1->other;
12035 if (type_is_invalid(op1->value.type))
12036 return ira->codegen->builtin_types.entry_invalid;
12037
12038 IrInstruction *op2 = instruction->op2->other;
12039 if (type_is_invalid(op2->value.type))
12040 return ira->codegen->builtin_types.entry_invalid;
12041
12042
12043 IrInstruction *peer_instructions[] = { op1, op2 };
12044 TypeTableEntry *result_type = ir_resolve_peer_types(ira, instruction->base.source_node, peer_instructions, 2);
12045
12046 if (type_is_invalid(result_type))
12047 return ira->codegen->builtin_types.entry_invalid;
12048
12049 if (result_type->id != TypeTableEntryIdInt &&
12050 result_type->id != TypeTableEntryIdNumLitInt)
12051 {
12052 ir_add_error(ira, &instruction->base,
12053 buf_sprintf("expected integer type, found '%s'", buf_ptr(&result_type->name)));
12054 return ira->codegen->builtin_types.entry_invalid;
12055 }
12056
12057 IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, result_type);
12058 if (type_is_invalid(casted_op1->value.type))
12059 return ira->codegen->builtin_types.entry_invalid;
12060
12061 IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, result_type);
12062 if (type_is_invalid(casted_op2->value.type))
12063 return ira->codegen->builtin_types.entry_invalid;
12064
12065 if (casted_op1->value.special == ConstValSpecialStatic &&
12066 casted_op2->value.special == ConstValSpecialStatic)
12067 {
12068 ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
12069 ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
12070 assert(op1_val);
12071 assert(op2_val);
12072
12073 if (op1_val->data.x_bignum.data.x_uint == 0) {
12074 ir_add_error(ira, &instruction->base, buf_sprintf("division by zero"));
12075 return ira->codegen->builtin_types.entry_invalid;
12076 }
12077
12078 BigNum remainder;
12079 if (bignum_rem(&remainder, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
12080 ir_add_error(ira, &instruction->base, buf_sprintf("integer overflow"));
12081 return ira->codegen->builtin_types.entry_invalid;
12082 }
12083
12084 if (remainder.data.x_uint != 0) {
12085 ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder"));
12086 return ira->codegen->builtin_types.entry_invalid;
12087 }
12088
12089 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
12090 bignum_div(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
12091 return result_type;
12092 }
12093
12094 ir_build_div_exact_from(&ira->new_irb, &instruction->base, casted_op1, casted_op2);
12095 return result_type;
12096}
12097
1209812160static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) {
1209912161 IrInstruction *dest_type_value = instruction->dest_type->other;
1210012162 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
......@@ -13261,8 +13323,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1326113323 return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction);
1326213324 case IrInstructionIdFence:
1326313325 return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);
13264 case IrInstructionIdDivExact:
13265 return ir_analyze_instruction_div_exact(ira, (IrInstructionDivExact *)instruction);
1326613326 case IrInstructionIdTruncate:
1326713327 return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction);
1326813328 case IrInstructionIdIntType:
......@@ -13469,7 +13529,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1346913529 case IrInstructionIdMinValue:
1347013530 case IrInstructionIdMaxValue:
1347113531 case IrInstructionIdEmbedFile:
13472 case IrInstructionIdDivExact:
1347313532 case IrInstructionIdTruncate:
1347413533 case IrInstructionIdIntType:
1347513534 case IrInstructionIdBoolNot:
src/ir_print.cpp+12-13
......@@ -109,10 +109,20 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
109109 return "*";
110110 case IrBinOpMultWrap:
111111 return "*%";
112 case IrBinOpDiv:
112 case IrBinOpDivUnspecified:
113113 return "/";
114 case IrBinOpRem:
114 case IrBinOpDivTrunc:
115 return "@divTrunc";
116 case IrBinOpDivFloor:
117 return "@divFloor";
118 case IrBinOpDivExact:
119 return "@divExact";
120 case IrBinOpRemUnspecified:
115121 return "%";
122 case IrBinOpRemRem:
123 return "@rem";
124 case IrBinOpRemMod:
125 return "@mod";
116126 case IrBinOpArrayCat:
117127 return "++";
118128 case IrBinOpArrayMult:
......@@ -580,14 +590,6 @@ static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) {
580590 fprintf(irp->f, ")");
581591}
582592
583static void ir_print_div_exact(IrPrint *irp, IrInstructionDivExact *instruction) {
584 fprintf(irp->f, "@divExact(");
585 ir_print_other_instruction(irp, instruction->op1);
586 fprintf(irp->f, ", ");
587 ir_print_other_instruction(irp, instruction->op2);
588 fprintf(irp->f, ")");
589}
590
591593static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) {
592594 fprintf(irp->f, "@truncate(");
593595 ir_print_other_instruction(irp, instruction->dest_type);
......@@ -1056,9 +1058,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
10561058 case IrInstructionIdFence:
10571059 ir_print_fence(irp, (IrInstructionFence *)instruction);
10581060 break;
1059 case IrInstructionIdDivExact:
1060 ir_print_div_exact(irp, (IrInstructionDivExact *)instruction);
1061 break;
10621061 case IrInstructionIdTruncate:
10631062 ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
10641063 break;
src/link.cpp+2
......@@ -297,6 +297,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
297297 lj->args.append("-lgcc");
298298 lj->args.append("-lgcc_eh");
299299 lj->args.append("-lc");
300 lj->args.append("-lm");
300301 lj->args.append("--end-group");
301302 } else {
302303 lj->args.append("-lgcc");
......@@ -304,6 +305,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
304305 lj->args.append("-lgcc_s");
305306 lj->args.append("--no-as-needed");
306307 lj->args.append("-lc");
308 lj->args.append("-lm");
307309 lj->args.append("-lgcc");
308310 lj->args.append("--as-needed");
309311 lj->args.append("-lgcc_s");
std/elf.zig+3-4
......@@ -165,9 +165,9 @@ pub const Elf = struct {
165165 if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
166166
167167 const sh_byte_count = u64(sh_entry_size) * u64(sh_entry_count);
168 const end_sh = %return math.addOverflow(u64, elf.section_header_offset, sh_byte_count);
168 const end_sh = %return math.add(u64, elf.section_header_offset, sh_byte_count);
169169 const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count);
170 const end_ph = %return math.addOverflow(u64, elf.program_header_offset, ph_byte_count);
170 const end_ph = %return math.add(u64, elf.program_header_offset, ph_byte_count);
171171
172172 const stream_end = %return elf.in_stream.getEndPos();
173173 if (stream_end < end_sh or stream_end < end_ph) {
......@@ -214,8 +214,7 @@ pub const Elf = struct {
214214
215215 for (elf.section_headers) |*section| {
216216 if (section.sh_type != SHT_NOBITS) {
217 const file_end_offset = %return math.addOverflow(u64,
218 section.offset, section.size);
217 const file_end_offset = %return math.add(u64, section.offset, section.size);
219218 if (stream_end < file_end_offset) return error.InvalidFormat;
220219 }
221220 }
std/fmt.zig+2-2
......@@ -305,8 +305,8 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
305305
306306 for (buf) |c| {
307307 const digit = %return charToDigit(c, radix);
308 x = %return math.mulOverflow(T, x, radix);
309 x = %return math.addOverflow(T, x, digit);
308 x = %return math.mul(T, x, radix);
309 x = %return math.add(T, x, digit);
310310 }
311311
312312 return x;
std/math.zig+211-28
......@@ -1,37 +1,64 @@
11const assert = @import("debug.zig").assert;
22
33pub const Cmp = enum {
4 Less,
45 Equal,
56 Greater,
6 Less,
77};
88
99pub fn min(x: var, y: var) -> @typeOf(x + y) {
1010 if (x < y) x else y
1111}
1212
13test "math.min" {
14 assert(min(i32(-1), i32(2)) == -1);
15}
16
1317pub fn max(x: var, y: var) -> @typeOf(x + y) {
1418 if (x > y) x else y
1519}
1620
21test "math.max" {
22 assert(max(i32(-1), i32(2)) == 2);
23}
24
1725error Overflow;
18pub fn mulOverflow(comptime T: type, a: T, b: T) -> %T {
26pub fn mul(comptime T: type, a: T, b: T) -> %T {
1927 var answer: T = undefined;
2028 if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer
2129}
22pub fn addOverflow(comptime T: type, a: T, b: T) -> %T {
30
31error Overflow;
32pub fn add(comptime T: type, a: T, b: T) -> %T {
2333 var answer: T = undefined;
2434 if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer
2535}
26pub fn subOverflow(comptime T: type, a: T, b: T) -> %T {
36
37error Overflow;
38pub fn sub(comptime T: type, a: T, b: T) -> %T {
2739 var answer: T = undefined;
2840 if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer
2941}
30pub fn shlOverflow(comptime T: type, a: T, b: T) -> %T {
42
43error Overflow;
44pub fn shl(comptime T: type, a: T, b: T) -> %T {
3145 var answer: T = undefined;
3246 if (@shlWithOverflow(T, a, b, &answer)) error.Overflow else answer
3347}
3448
49test "math overflow functions" {
50 testOverflow();
51 comptime testOverflow();
52}
53
54fn testOverflow() {
55 assert(%%mul(i32, 3, 4) == 12);
56 assert(%%add(i32, 3, 4) == 7);
57 assert(%%sub(i32, 3, 4) == -1);
58 assert(%%shl(i32, 0b11, 4) == 0b110000);
59}
60
61
3562pub fn log(comptime base: usize, value: var) -> @typeOf(value) {
3663 const T = @typeOf(value);
3764 if (@isInteger(T)) {
......@@ -47,35 +74,191 @@ pub fn log(comptime base: usize, value: var) -> @typeOf(value) {
4774 }
4875}
4976
50/// x must be an integer or a float
51/// Note that this causes undefined behavior if
52/// @typeOf(x).is_signed and x == @minValue(@typeOf(x)).
53pub fn abs(x: var) -> @typeOf(x) {
77error Overflow;
78pub fn absInt(x: var) -> %@typeOf(x) {
5479 const T = @typeOf(x);
55 if (@isInteger(T)) {
80 comptime assert(@isInteger(T)); // must pass an integer to absInt
81 comptime assert(T.is_signed); // must pass a signed integer to absInt
82 if (x == @minValue(@typeOf(x)))
83 return error.Overflow;
84 {
85 @setDebugSafety(this, false);
5686 return if (x < 0) -x else x;
57 } else if (@isFloat(T)) {
58 @compileError("TODO implement abs for floats");
59 } else {
60 unreachable;
6187 }
6288}
63fn getReturnTypeForAbs(comptime T: type) -> type {
64 if (@isInteger(T)) {
65 return @IntType(false, T.bit_count);
66 } else {
67 return T;
68 }
89
90test "math.absInt" {
91 testAbsInt();
92 comptime testAbsInt();
93}
94fn testAbsInt() {
95 assert(%%absInt(i32(-10)) == 10);
96 assert(%%absInt(i32(10)) == 10);
97}
98
99pub fn absFloat(x: var) -> @typeOf(x) {
100 comptime assert(@isFloat(@typeOf(x)));
101 return if (x < 0) -x else x;
102}
103
104test "math.absFloat" {
105 testAbsFloat();
106 comptime testAbsFloat();
107}
108fn testAbsFloat() {
109 assert(absFloat(f32(-10.0)) == 10.0);
110 assert(absFloat(f32(10.0)) == 10.0);
111}
112
113error DivisionByZero;
114error Overflow;
115pub fn divTrunc(comptime T: type, numerator: T, denominator: T) -> %T {
116 @setDebugSafety(this, false);
117 if (denominator == 0)
118 return error.DivisionByZero;
119 if (@isInteger(T) and T.is_signed and numerator == @minValue(T) and denominator == -1)
120 return error.Overflow;
121 return @divTrunc(numerator, denominator);
122}
123
124test "math.divTrunc" {
125 testDivTrunc();
126 comptime testDivTrunc();
127}
128fn testDivTrunc() {
129 assert(%%divTrunc(i32, 5, 3) == 1);
130 assert(%%divTrunc(i32, -5, 3) == -1);
131 if (divTrunc(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
132 if (divTrunc(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow);
133
134 assert(%%divTrunc(f32, 5.0, 3.0) == 1.0);
135 assert(%%divTrunc(f32, -5.0, 3.0) == -1.0);
136}
137
138error DivisionByZero;
139error Overflow;
140pub fn divFloor(comptime T: type, numerator: T, denominator: T) -> %T {
141 @setDebugSafety(this, false);
142 if (denominator == 0)
143 return error.DivisionByZero;
144 if (@isInteger(T) and T.is_signed and numerator == @minValue(T) and denominator == -1)
145 return error.Overflow;
146 return @divFloor(numerator, denominator);
147}
148
149test "math.divFloor" {
150 testDivFloor();
151 comptime testDivFloor();
152}
153fn testDivFloor() {
154 assert(%%divFloor(i32, 5, 3) == 1);
155 assert(%%divFloor(i32, -5, 3) == -2);
156 if (divFloor(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
157 if (divFloor(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow);
158
159 assert(%%divFloor(f32, 5.0, 3.0) == 1.0);
160 assert(%%divFloor(f32, -5.0, 3.0) == -2.0);
161}
162
163error DivisionByZero;
164error Overflow;
165error UnexpectedRemainder;
166pub fn divExact(comptime T: type, numerator: T, denominator: T) -> %T {
167 @setDebugSafety(this, false);
168 if (denominator == 0)
169 return error.DivisionByZero;
170 if (@isInteger(T) and T.is_signed and numerator == @minValue(T) and denominator == -1)
171 return error.Overflow;
172 const result = @divTrunc(numerator, denominator);
173 if (result * denominator != numerator)
174 return error.UnexpectedRemainder;
175 return result;
176}
177
178test "math.divExact" {
179 testDivExact();
180 comptime testDivExact();
69181}
182fn testDivExact() {
183 assert(%%divExact(i32, 10, 5) == 2);
184 assert(%%divExact(i32, -10, 5) == -2);
185 if (divExact(i8, -5, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
186 if (divExact(i8, -128, -1)) |_| unreachable else |err| assert(err == error.Overflow);
187 if (divExact(i32, 5, 2)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder);
70188
71test "testMath" {
72 testMathImpl();
73 comptime testMathImpl();
189 assert(%%divExact(f32, 10.0, 5.0) == 2.0);
190 assert(%%divExact(f32, -10.0, 5.0) == -2.0);
191 if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder);
192}
193
194error DivisionByZero;
195error NegativeDenominator;
196pub fn mod(comptime T: type, numerator: T, denominator: T) -> %T {
197 @setDebugSafety(this, false);
198 if (denominator == 0)
199 return error.DivisionByZero;
200 if (denominator < 0)
201 return error.NegativeDenominator;
202 return @mod(numerator, denominator);
203}
204
205test "math.mod" {
206 testMod();
207 comptime testMod();
208}
209fn testMod() {
210 assert(%%mod(i32, -5, 3) == 1);
211 assert(%%mod(i32, 5, 3) == 2);
212 if (mod(i32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator);
213 if (mod(i32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
214
215 assert(%%mod(f32, -5, 3) == 1);
216 assert(%%mod(f32, 5, 3) == 2);
217 if (mod(f32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator);
218 if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
219}
220
221error DivisionByZero;
222error NegativeDenominator;
223pub fn rem(comptime T: type, numerator: T, denominator: T) -> %T {
224 @setDebugSafety(this, false);
225 if (denominator == 0)
226 return error.DivisionByZero;
227 if (denominator < 0)
228 return error.NegativeDenominator;
229 return @rem(numerator, denominator);
230}
231
232test "math.rem" {
233 testRem();
234 comptime testRem();
235}
236fn testRem() {
237 assert(%%rem(i32, -5, 3) == -2);
238 assert(%%rem(i32, 5, 3) == 2);
239 if (rem(i32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator);
240 if (rem(i32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
241
242 assert(%%rem(f32, -5, 3) == -2);
243 assert(%%rem(f32, 5, 3) == 2);
244 if (rem(f32, 10, -1)) |_| unreachable else |err| assert(err == error.NegativeDenominator);
245 if (rem(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero);
246}
247
248fn isNan(comptime T: type, x: T) -> bool {
249 assert(@isFloat(T));
250 const bits = floatBits(x);
251 if (T == f32) {
252 return (bits & 0x7fffffff) > 0x7f800000;
253 } else if (T == f64) {
254 return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52);
255 } else {
256 unreachable;
257 }
74258}
75259
76fn testMathImpl() {
77 assert(%%mulOverflow(i32, 3, 4) == 12);
78 assert(%%addOverflow(i32, 3, 4) == 7);
79 assert(%%subOverflow(i32, 3, 4) == -1);
80 assert(%%shlOverflow(i32, 0b11, 4) == 0b110000);
260fn floatBits(comptime T: type, x: T) -> @IntType(false, T.bit_count) {
261 assert(@isFloat(T));
262 const uint = @IntType(false, T.bit_count);
263 return *@intToPtr(&const uint, &x);
81264}
std/mem.zig+28-2
......@@ -35,12 +35,12 @@ pub const Allocator = struct {
3535 }
3636
3737 fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {
38 const byte_count = %return math.mulOverflow(usize, @sizeOf(T), n);
38 const byte_count = %return math.mul(usize, @sizeOf(T), n);
3939 ([]T)(%return self.allocFn(self, byte_count))
4040 }
4141
4242 fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {
43 const byte_count = %return math.mulOverflow(usize, @sizeOf(T), n);
43 const byte_count = %return math.mul(usize, @sizeOf(T), n);
4444 ([]T)(%return self.reallocFn(self, ([]u8)(old_mem), byte_count))
4545 }
4646
......@@ -333,3 +333,29 @@ fn testWriteIntImpl() {
333333 assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 }));
334334}
335335
336
337pub fn min(comptime T: type, slice: []const T) -> T {
338 var best = slice[0];
339 var i: usize = 1;
340 while (i < slice.len) : (i += 1) {
341 best = math.min(best, slice[i]);
342 }
343 return best;
344}
345
346test "mem.min" {
347 assert(min(u8, "abcdefg") == 'a');
348}
349
350pub fn max(comptime T: type, slice: []const T) -> T {
351 var best = slice[0];
352 var i: usize = 1;
353 while (i < slice.len) : (i += 1) {
354 best = math.max(best, slice[i]);
355 }
356 return best;
357}
358
359test "mem.max" {
360 assert(max(u8, "abcdefg") == 'g');
361}
std/special/builtin.zig+92
......@@ -29,3 +29,95 @@ export fn __stack_chk_fail() {
2929 }
3030 @panic("stack smashing detected");
3131}
32
33export fn fmodf(x: f32, y: f32) -> f32 { generic_fmod(f32, x, y) }
34export fn fmod(x: f64, y: f64) -> f64 { generic_fmod(f64, x, y) }
35
36fn generic_fmod(comptime T: type, x: T, y: T) -> T {
37 //@setDebugSafety(this, false);
38 const uint = @IntType(false, T.bit_count);
39 const digits = if (T == f32) 23 else 52;
40 const exp_bits = if (T == f32) 9 else 12;
41 const bits_minus_1 = T.bit_count - 1;
42 const mask = if (T == f32) 0xff else 0x7ff;
43 var ux = *@ptrCast(&const uint, &x);
44 var uy = *@ptrCast(&const uint, &y);
45 var ex = i32((ux >> digits) & mask);
46 var ey = i32((uy >> digits) & mask);
47 const sx = if (T == f32) u32(ux & 0x80000000) else i32(ux >> bits_minus_1);
48 var i: uint = undefined;
49
50 if (uy <<% 1 == 0 or isNan(uint, uy) or ex == mask)
51 return (x * y) / (x * y);
52
53 if (ux <<% 1 <= uy <<% 1) {
54 if (ux <<% 1 == uy <<% 1)
55 return 0 * x;
56 return x;
57 }
58
59 // normalize x and y
60 if (ex == 0) {
61 i = ux <<% exp_bits;
62 while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<%= 1}) {}
63 ux <<%= twosComplementCast(uint, -ex + 1);
64 } else {
65 ux &= @maxValue(uint) >> exp_bits;
66 ux |= 1 <<% digits;
67 }
68 if (ey == 0) {
69 i = uy <<% exp_bits;
70 while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<%= 1}) {}
71 uy <<= twosComplementCast(uint, -ey + 1);
72 } else {
73 uy &= @maxValue(uint) >> exp_bits;
74 uy |= 1 <<% digits;
75 }
76
77 // x mod y
78 while (ex > ey) : (ex -= 1) {
79 i = ux -% uy;
80 if (i >> bits_minus_1 == 0) {
81 if (i == 0)
82 return 0 * x;
83 ux = i;
84 }
85 ux <<%= 1;
86 }
87 i = ux -% uy;
88 if (i >> bits_minus_1 == 0) {
89 if (i == 0)
90 return 0 * x;
91 ux = i;
92 }
93 while (ux >> digits == 0) : ({ux <<%= 1; ex -= 1}) {}
94
95 // scale result up
96 if (ex > 0) {
97 ux -%= 1 <<% digits;
98 ux |= twosComplementCast(uint, ex) <<% digits;
99 } else {
100 ux >>= twosComplementCast(uint, -ex + 1);
101 }
102 if (T == f32) {
103 ux |= sx;
104 } else {
105 ux |= uint(sx) <<% bits_minus_1;
106 }
107 return *@ptrCast(&const T, &ux);
108}
109
110fn isNan(comptime T: type, bits: T) -> bool {
111 if (T == u32) {
112 return (bits & 0x7fffffff) > 0x7f800000;
113 } else if (T == u64) {
114 return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) <<% 52);
115 } else {
116 unreachable;
117 }
118}
119
120// TODO this should be a builtin function and it shouldn't do a ptr cast
121fn twosComplementCast(comptime T: type, src: var) -> T {
122 return *@ptrCast(&const @IntType(T.is_signed, @typeOf(src).bit_count), &src);
123}
std/special/zigrt.zig+1-1
......@@ -1,5 +1,5 @@
11// This file contains functions that zig depends on to coordinate between
2// multiple .o files. The symbols are defined Weak so that multiple
2// multiple .o files. The symbols are defined LinkOnce so that multiple
33// instances of zig_rt.zig do not conflict with each other.
44
55const builtin = @import("builtin");
test/cases/math.zig+64-26
......@@ -1,47 +1,76 @@
11const assert = @import("std").debug.assert;
22
3test "exactDivision" {
4 assert(divExact(55, 11) == 5);
3test "division" {
4 testDivision();
5 comptime testDivision();
6}
7fn testDivision() {
8 assert(div(u32, 13, 3) == 4);
9 assert(div(f32, 1.0, 2.0) == 0.5);
10
11 assert(divExact(u32, 55, 11) == 5);
12 assert(divExact(i32, -55, 11) == -5);
13 assert(divExact(f32, 55.0, 11.0) == 5.0);
14 assert(divExact(f32, -55.0, 11.0) == -5.0);
15
16 assert(divFloor(i32, 5, 3) == 1);
17 assert(divFloor(i32, -5, 3) == -2);
18 assert(divFloor(f32, 5.0, 3.0) == 1.0);
19 assert(divFloor(f32, -5.0, 3.0) == -2.0);
20 assert(divFloor(i32, -0x80000000, -2) == 0x40000000);
21 assert(divFloor(i32, 0, -0x80000000) == 0);
22 assert(divFloor(i32, -0x40000001, 0x40000000) == -2);
23 assert(divFloor(i32, -0x80000000, 1) == -0x80000000);
24
25 assert(divTrunc(i32, 5, 3) == 1);
26 assert(divTrunc(i32, -5, 3) == -1);
27 assert(divTrunc(f32, 5.0, 3.0) == 1.0);
28 assert(divTrunc(f32, -5.0, 3.0) == -1.0);
29}
30fn div(comptime T: type, a: T, b: T) -> T {
31 a / b
532}
6fn divExact(a: u32, b: u32) -> u32 {
33fn divExact(comptime T: type, a: T, b: T) -> T {
734 @divExact(a, b)
835}
9
10test "floatDivision" {
11 assert(fdiv32(12.0, 3.0) == 4.0);
36fn divFloor(comptime T: type, a: T, b: T) -> T {
37 @divFloor(a, b)
1238}
13fn fdiv32(a: f32, b: f32) -> f32 {
14 a / b
39fn divTrunc(comptime T: type, a: T, b: T) -> T {
40 @divTrunc(a, b)
1541}
1642
17test "overflowIntrinsics" {
43test "@addWithOverflow" {
1844 var result: u8 = undefined;
1945 assert(@addWithOverflow(u8, 250, 100, &result));
2046 assert(!@addWithOverflow(u8, 100, 150, &result));
2147 assert(result == 250);
2248}
2349
24test "shlWithOverflow" {
50// TODO test mulWithOverflow
51// TODO test subWithOverflow
52
53test "@shlWithOverflow" {
2554 var result: u16 = undefined;
2655 assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
2756 assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
2857 assert(result == 0b1011111111111100);
2958}
3059
31test "countLeadingZeroes" {
60test "@clz" {
3261 assert(@clz(u8(0b00001010)) == 4);
3362 assert(@clz(u8(0b10001010)) == 0);
3463 assert(@clz(u8(0b00000000)) == 8);
3564}
3665
37test "countTrailingZeroes" {
66test "@ctz" {
3867 assert(@ctz(u8(0b10100000)) == 5);
3968 assert(@ctz(u8(0b10001010)) == 1);
4069 assert(@ctz(u8(0b00000000)) == 8);
4170}
4271
43test "modifyOperators" {
44 var i : i32 = 0;
72test "assignment operators" {
73 var i: u32 = 0;
4574 i += 5; assert(i == 5);
4675 i -= 2; assert(i == 3);
4776 i *= 20; assert(i == 60);
......@@ -57,6 +86,8 @@ test "modifyOperators" {
5786}
5887
5988test "threeExprInARow" {
89 testThreeExprInARow(false, true);
90 comptime testThreeExprInARow(false, true);
6091}
6192fn testThreeExprInARow(f: bool, t: bool) {
6293 assertFalse(f or f or f);
......@@ -72,13 +103,12 @@ fn testThreeExprInARow(f: bool, t: bool) {
72103 assertFalse(!!false);
73104 assertFalse(i32(7) != --(i32(7)));
74105}
75
76106fn assertFalse(b: bool) {
77107 assert(!b);
78108}
79109
80110
81test "constNumberLiteral" {
111test "const number literal" {
82112 const one = 1;
83113 const eleven = ten + one;
84114
......@@ -88,8 +118,9 @@ const ten = 10;
88118
89119
90120
91test "unsignedWrapping" {
121test "unsigned wrapping" {
92122 testUnsignedWrappingEval(@maxValue(u32));
123 comptime testUnsignedWrappingEval(@maxValue(u32));
93124}
94125fn testUnsignedWrappingEval(x: u32) {
95126 const zero = x +% 1;
......@@ -98,8 +129,9 @@ fn testUnsignedWrappingEval(x: u32) {
98129 assert(orig == @maxValue(u32));
99130}
100131
101test "signedWrapping" {
132test "signed wrapping" {
102133 testSignedWrappingEval(@maxValue(i32));
134 comptime testSignedWrappingEval(@maxValue(i32));
103135}
104136fn testSignedWrappingEval(x: i32) {
105137 const min_val = x +% 1;
......@@ -108,8 +140,9 @@ fn testSignedWrappingEval(x: i32) {
108140 assert(max_val == @maxValue(i32));
109141}
110142
111test "negationWrapping" {
143test "negation wrapping" {
112144 testNegationWrappingEval(@minValue(i16));
145 comptime testNegationWrappingEval(@minValue(i16));
113146}
114147fn testNegationWrappingEval(x: i16) {
115148 assert(x == -32768);
......@@ -117,20 +150,25 @@ fn testNegationWrappingEval(x: i16) {
117150 assert(neg == -32768);
118151}
119152
120test "shlWrapping" {
153test "shift left wrapping" {
121154 testShlWrappingEval(@maxValue(u16));
155 comptime testShlWrappingEval(@maxValue(u16));
122156}
123157fn testShlWrappingEval(x: u16) {
124158 const shifted = x <<% 1;
125159 assert(shifted == 65534);
126160}
127161
128test "unsigned64BitDivision" {
129 const result = div(1152921504606846976, 34359738365);
162test "unsigned 64-bit division" {
163 test_u64_div();
164 comptime test_u64_div();
165}
166fn test_u64_div() {
167 const result = divWithResult(1152921504606846976, 34359738365);
130168 assert(result.quotient == 33554432);
131169 assert(result.remainder == 100663296);
132170}
133fn div(a: u64, b: u64) -> DivResult {
171fn divWithResult(a: u64, b: u64) -> DivResult {
134172 DivResult {
135173 .quotient = a / b,
136174 .remainder = a % b,
......@@ -141,7 +179,7 @@ const DivResult = struct {
141179 remainder: u64,
142180};
143181
144test "binaryNot" {
182test "binary not" {
145183 assert(comptime {~u16(0b1010101010101010) == 0b0101010101010101});
146184 assert(comptime {~u64(2147483647) == 18446744071562067968});
147185 testBinaryNot(0b1010101010101010);
......@@ -151,7 +189,7 @@ fn testBinaryNot(x: u16) {
151189 assert(~x == 0b0101010101010101);
152190}
153191
154test "smallIntAddition" {
192test "small int addition" {
155193 var x: @IntType(false, 2) = 0;
156194 assert(x == 0);
157195
......@@ -170,7 +208,7 @@ test "smallIntAddition" {
170208 assert(result == 0);
171209}
172210
173test "testFloatEquality" {
211test "float equality" {
174212 const x: f64 = 0.012;
175213 const y: f64 = x + 1.0;
176214
test/cases/misc.zig+5
......@@ -49,6 +49,11 @@ test "@IntType builtin" {
4949 assert(!usize.is_signed);
5050}
5151
52test "floating point primitive bit counts" {
53 assert(f32.bit_count == 32);
54 assert(f64.bit_count == 64);
55}
56
5257const u1 = @IntType(false, 1);
5358const u63 = @IntType(false, 63);
5459const i1 = @IntType(true, 1);
test/compile_errors.zig+16-2
......@@ -702,7 +702,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
702702 cases.add("division by zero",
703703 \\const lit_int_x = 1 / 0;
704704 \\const lit_float_x = 1.0 / 0.0;
705 \\const int_x = i32(1) / i32(0);
705 \\const int_x = u32(1) / u32(0);
706706 \\const float_x = f32(1.0) / f32(0.0);
707707 \\
708708 \\export fn entry1() -> usize { @sizeOf(@typeOf(lit_int_x)) }
......@@ -792,7 +792,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
792792
793793 cases.add("compile time division by zero",
794794 \\const y = foo(0);
795 \\fn foo(x: i32) -> i32 {
795 \\fn foo(x: u32) -> u32 {
796796 \\ 1 / x
797797 \\}
798798 \\
......@@ -1709,4 +1709,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
17091709 \\extern fn quux(usize);
17101710 ,
17111711 ".tmp_source.zig:4:8: error: unable to inline function");
1712
1713 cases.add("signed integer division",
1714 \\export fn foo(a: i32, b: i32) -> i32 {
1715 \\ a / b
1716 \\}
1717 ,
1718 ".tmp_source.zig:2:7: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact");
1719
1720 cases.add("signed integer remainder division",
1721 \\export fn foo(a: i32, b: i32) -> i32 {
1722 \\ a % b
1723 \\}
1724 ,
1725 ".tmp_source.zig:2:7: error: remainder division with 'i32' and 'i32': signed integers must use @rem or @mod");
17121726}
test/debug_safety.zig+2-2
......@@ -97,7 +97,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
9797 \\ if (x == 32767) return error.Whatever;
9898 \\}
9999 \\fn div(a: i16, b: i16) -> i16 {
100 \\ a / b
100 \\ @divTrunc(a, b)
101101 \\}
102102 );
103103
......@@ -141,7 +141,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
141141 \\ const x = div0(999, 0);
142142 \\}
143143 \\fn div0(a: i32, b: i32) -> i32 {
144 \\ a / b
144 \\ @divTrunc(a, b)
145145 \\}
146146 );
147147