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....@@ -502,15 +502,6 @@ This function performs an atomic compare exchange operation.
502502
503The `fence` function is used to introduce happens-before edges between operations.503The `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
514### @truncate(comptime T: type, integer) -> T505### @truncate(comptime T: type, integer) -> T
515506
516This function truncates bits from an integer type, resulting in a smaller507This 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:...@@ -621,3 +612,68 @@ Converts an enum tag name to a slice of bytes. Example:
621### @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, field_ptr: &T) -> &ParentType612### @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, field_ptr: &T) -> &ParentType
622613
623Given a pointer to a field, returns the base pointer of a struct.614Given 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 {...@@ -1195,6 +1195,10 @@ enum BuiltinFnId {
1195 BuiltinFnIdCmpExchange,1195 BuiltinFnIdCmpExchange,
1196 BuiltinFnIdFence,1196 BuiltinFnIdFence,
1197 BuiltinFnIdDivExact,1197 BuiltinFnIdDivExact,
1198 BuiltinFnIdDivTrunc,
1199 BuiltinFnIdDivFloor,
1200 BuiltinFnIdRem,
1201 BuiltinFnIdMod,
1198 BuiltinFnIdTruncate,1202 BuiltinFnIdTruncate,
1199 BuiltinFnIdIntType,1203 BuiltinFnIdIntType,
1200 BuiltinFnIdSetDebugSafety,1204 BuiltinFnIdSetDebugSafety,
...@@ -1270,6 +1274,8 @@ enum ZigLLVMFnId {...@@ -1270,6 +1274,8 @@ enum ZigLLVMFnId {
1270 ZigLLVMFnIdCtz,1274 ZigLLVMFnIdCtz,
1271 ZigLLVMFnIdClz,1275 ZigLLVMFnIdClz,
1272 ZigLLVMFnIdOverflowArithmetic,1276 ZigLLVMFnIdOverflowArithmetic,
1277 ZigLLVMFnIdFloor,
1278 ZigLLVMFnIdCeil,
1273};1279};
12741280
1275enum AddSubMul {1281enum AddSubMul {
...@@ -1288,6 +1294,9 @@ struct ZigLLVMFnKey {...@@ -1288,6 +1294,9 @@ struct ZigLLVMFnKey {
1288 struct {1294 struct {
1289 uint32_t bit_count;1295 uint32_t bit_count;
1290 } clz;1296 } clz;
1297 struct {
1298 uint32_t bit_count;
1299 } floor_ceil;
1291 struct {1300 struct {
1292 AddSubMul add_sub_mul;1301 AddSubMul add_sub_mul;
1293 uint32_t bit_count;1302 uint32_t bit_count;
...@@ -1746,7 +1755,6 @@ enum IrInstructionId {...@@ -1746,7 +1755,6 @@ enum IrInstructionId {
1746 IrInstructionIdEmbedFile,1755 IrInstructionIdEmbedFile,
1747 IrInstructionIdCmpxchg,1756 IrInstructionIdCmpxchg,
1748 IrInstructionIdFence,1757 IrInstructionIdFence,
1749 IrInstructionIdDivExact,
1750 IrInstructionIdTruncate,1758 IrInstructionIdTruncate,
1751 IrInstructionIdIntType,1759 IrInstructionIdIntType,
1752 IrInstructionIdBoolNot,1760 IrInstructionIdBoolNot,
...@@ -1897,8 +1905,13 @@ enum IrBinOp {...@@ -1897,8 +1905,13 @@ enum IrBinOp {
1897 IrBinOpSubWrap,1905 IrBinOpSubWrap,
1898 IrBinOpMult,1906 IrBinOpMult,
1899 IrBinOpMultWrap,1907 IrBinOpMultWrap,
1900 IrBinOpDiv,1908 IrBinOpDivUnspecified,
1901 IrBinOpRem,1909 IrBinOpDivExact,
1910 IrBinOpDivTrunc,
1911 IrBinOpDivFloor,
1912 IrBinOpRemUnspecified,
1913 IrBinOpRemRem,
1914 IrBinOpRemMod,
1902 IrBinOpArrayCat,1915 IrBinOpArrayCat,
1903 IrBinOpArrayMult,1916 IrBinOpArrayMult,
1904};1917};
...@@ -2250,13 +2263,6 @@ struct IrInstructionFence {...@@ -2250,13 +2263,6 @@ struct IrInstructionFence {
2250 AtomicOrder order;2263 AtomicOrder order;
2251};2264};
22522265
2253struct IrInstructionDivExact {
2254 IrInstruction base;
2255
2256 IrInstruction *op1;
2257 IrInstruction *op2;
2258};
2259
2260struct IrInstructionTruncate {2266struct IrInstructionTruncate {
2261 IrInstruction base;2267 IrInstruction base;
22622268
src/analyze.cpp+7
...@@ -4228,6 +4228,10 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {...@@ -4228,6 +4228,10 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
4228 return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;4228 return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;
4229 case ZigLLVMFnIdClz:4229 case ZigLLVMFnIdClz:
4230 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;4230 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;
4231 case ZigLLVMFnIdOverflowArithmetic:4235 case ZigLLVMFnIdOverflowArithmetic:
4232 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +4236 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
4233 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +4237 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
...@@ -4244,6 +4248,9 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {...@@ -4244,6 +4248,9 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
4244 return a.data.ctz.bit_count == b.data.ctz.bit_count;4248 return a.data.ctz.bit_count == b.data.ctz.bit_count;
4245 case ZigLLVMFnIdClz:4249 case ZigLLVMFnIdClz:
4246 return a.data.clz.bit_count == b.data.clz.bit_count;4250 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;
4247 case ZigLLVMFnIdOverflowArithmetic:4254 case ZigLLVMFnIdOverflowArithmetic:
4248 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&4255 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
4249 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&4256 (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) {...@@ -204,6 +204,23 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
204204
205 if (dest->kind == BigNumKindFloat) {205 if (dest->kind == BigNumKindFloat) {
206 dest->data.x_float = op1->data.x_float / op2->data.x_float;206 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 }
207 } else {224 } else {
208 dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;225 dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
209 dest->is_negative = op1->is_negative != op2->is_negative;226 dest->is_negative = op1->is_negative != op2->is_negative;
...@@ -212,6 +229,29 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {...@@ -212,6 +229,29 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
212 return false;229 return false;
213}230}
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
215bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {255bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
216 assert(op1->kind == op2->kind);256 assert(op1->kind == op2->kind);
217 dest->kind = op1->kind;257 dest->kind = op1->kind;
...@@ -219,10 +259,28 @@ bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {...@@ -219,10 +259,28 @@ bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) {
219 if (dest->kind == BigNumKindFloat) {259 if (dest->kind == BigNumKindFloat) {
220 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);260 dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
221 } else {261 } else {
222 if (op1->is_negative || op2->is_negative) {262 assert(!op2->is_negative);
223 zig_panic("TODO handle remainder division with negative numbers");
224 }
225 dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;263 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;
226 bignum_normalize(dest);284 bignum_normalize(dest);
227 }285 }
228 return false;286 return false;
src/bignum.hpp+3
...@@ -37,7 +37,10 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);...@@ -37,7 +37,10 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);
37bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);37bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);
38bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);38bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);
39bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2);39bool 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);
40bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2);42bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2);
43bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2);
4144
42bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);45bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);
43bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2);46bool 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,...@@ -538,6 +538,35 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
538 return fn_val;538 return fn_val;
539}539}
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
541static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, bool is_volatile) {570static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, bool is_volatile) {
542 if (type_has_bits(type)) {571 if (type_has_bits(type)) {
543 if (handle_is_ptr(type)) {572 if (handle_is_ptr(type)) {
...@@ -618,7 +647,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -618,7 +647,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
618 case PanicMsgIdDivisionByZero:647 case PanicMsgIdDivisionByZero:
619 return buf_create_from_str("division by zero");648 return buf_create_from_str("division by zero");
620 case PanicMsgIdRemainderDivisionByZero:649 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");
622 case PanicMsgIdExactDivisionRemainder:651 case PanicMsgIdExactDivisionRemainder:
623 return buf_create_from_str("exact division produced remainder");652 return buf_create_from_str("exact division produced remainder");
624 case PanicMsgIdSliceWidenRemainder:653 case PanicMsgIdSliceWidenRemainder:
...@@ -1099,12 +1128,34 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,...@@ -1099,12 +1128,34 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry,
1099 return result;1128 return result;
1100}1129}
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
1102static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,1154static 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)
1104{1156{
11051157 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
1106 if (want_debug_safety) {1158 if (want_debug_safety) {
1107 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
1108 LLVMValueRef is_zero_bit;1159 LLVMValueRef is_zero_bit;
1109 if (type_entry->id == TypeTableEntryIdInt) {1160 if (type_entry->id == TypeTableEntryIdInt) {
1110 is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, "");1161 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...@@ -1140,55 +1191,111 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val
1140 }1191 }
11411192
1142 if (type_entry->id == TypeTableEntryIdFloat) {1193 if (type_entry->id == TypeTableEntryIdFloat) {
1143 assert(!exact);1194 LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, "");
1144 return 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();
1145 }1224 }
11461225
1147 assert(type_entry->id == TypeTableEntryIdInt);1226 assert(type_entry->id == TypeTableEntryIdInt);
11481227
1149 if (exact) {1228 switch (div_kind) {
1150 if (want_debug_safety) {1229 case DivKindFloat:
1151 LLVMValueRef remainder_val;1230 zig_unreachable();
1231 case DivKindTrunc:
1152 if (type_entry->data.integral.is_signed) {1232 if (type_entry->data.integral.is_signed) {
1153 remainder_val = LLVMBuildSRem(g->builder, val1, val2, "");1233 return LLVMBuildSDiv(g->builder, val1, val2, "");
1154 } else {1234 } else {
1155 remainder_val = LLVMBuildURem(g->builder, val1, val2, "");1235 return LLVMBuildUDiv(g->builder, val1, val2, "");
1156 }1236 }
1157 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);1237 case DivKindExact:
1158 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");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");1247 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
1161 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");1248 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
1162 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);1249 LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
11631250
1164 LLVMPositionBuilderAtEnd(g->builder, fail_block);1251 LLVMPositionBuilderAtEnd(g->builder, fail_block);
1165 gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder);1252 gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder);
11661253
1167 LLVMPositionBuilderAtEnd(g->builder, ok_block);1254 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1168 }1255 }
1169 if (type_entry->data.integral.is_signed) {1256 if (type_entry->data.integral.is_signed) {
1170 return LLVMBuildExactSDiv(g->builder, val1, val2, "");1257 return LLVMBuildExactSDiv(g->builder, val1, val2, "");
1171 } else {1258 } else {
1172 return LLVMBuildExactUDiv(g->builder, val1, val2, "");1259 return LLVMBuildExactUDiv(g->builder, val1, val2, "");
1173 }1260 }
1174 } else {1261 case DivKindFloor:
1175 if (type_entry->data.integral.is_signed) {1262 {
1176 return LLVMBuildSDiv(g->builder, val1, val2, "");1263 if (!type_entry->data.integral.is_signed) {
1177 } else {1264 return LLVMBuildUDiv(g->builder, val1, val2, "");
1178 return LLVMBuildUDiv(g->builder, val1, val2, "");1265 }
1179 }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 }
1180 }1281 }
1282 zig_unreachable();
1181}1283}
11821284
1285enum RemKind {
1286 RemKindRem,
1287 RemKindMod,
1288};
1289
1183static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,1290static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,
1184 TypeTableEntry *type_entry)1291 TypeTableEntry *type_entry, RemKind rem_kind)
1185{1292{
11861293 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
1187 if (want_debug_safety) {1294 if (want_debug_safety) {
1188 LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
1189 LLVMValueRef is_zero_bit;1295 LLVMValueRef is_zero_bit;
1190 if (type_entry->id == TypeTableEntryIdInt) {1296 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, "");
1192 } else if (type_entry->id == TypeTableEntryIdFloat) {1299 } else if (type_entry->id == TypeTableEntryIdFloat) {
1193 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");1300 is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, "");
1194 } else {1301 } else {
...@@ -1202,30 +1309,30 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val...@@ -1202,30 +1309,30 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val
1202 gen_debug_safety_crash(g, PanicMsgIdRemainderDivisionByZero);1309 gen_debug_safety_crash(g, PanicMsgIdRemainderDivisionByZero);
12031310
1204 LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block);1311 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 }
1221 }1312 }
12221313
1223 if (type_entry->id == TypeTableEntryIdFloat) {1314 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 }
1225 } else {1324 } else {
1226 assert(type_entry->id == TypeTableEntryIdInt);1325 assert(type_entry->id == TypeTableEntryIdInt);
1227 if (type_entry->data.integral.is_signed) {1326 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 }
1229 } else {1336 } else {
1230 return LLVMBuildURem(g->builder, val1, val2, "");1337 return LLVMBuildURem(g->builder, val1, val2, "");
1231 }1338 }
...@@ -1252,6 +1359,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -1252,6 +1359,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
1252 case IrBinOpInvalid:1359 case IrBinOpInvalid:
1253 case IrBinOpArrayCat:1360 case IrBinOpArrayCat:
1254 case IrBinOpArrayMult:1361 case IrBinOpArrayMult:
1362 case IrBinOpRemUnspecified:
1255 zig_unreachable();1363 zig_unreachable();
1256 case IrBinOpBoolOr:1364 case IrBinOpBoolOr:
1257 return LLVMBuildOr(g->builder, op1_value, op2_value, "");1365 return LLVMBuildOr(g->builder, op1_value, op2_value, "");
...@@ -1367,10 +1475,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -1367,10 +1475,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
1367 } else {1475 } else {
1368 zig_unreachable();1476 zig_unreachable();
1369 }1477 }
1370 case IrBinOpDiv:1478 case IrBinOpDivUnspecified:
1371 return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, false);1479 return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloat);
1372 case IrBinOpRem:1480 case IrBinOpDivExact:
1373 return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry);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);
1374 }1490 }
1375 zig_unreachable();1491 zig_unreachable();
1376}1492}
...@@ -2353,14 +2469,6 @@ static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInst...@@ -2353,14 +2469,6 @@ static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInst
2353 return nullptr;2469 return nullptr;
2354}2470}
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
2364static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) {2472static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) {
2365 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);2473 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
2366 TypeTableEntry *dest_type = instruction->base.value.type;2474 TypeTableEntry *dest_type = instruction->base.value.type;
...@@ -2965,8 +3073,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -2965,8 +3073,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
2965 return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction);3073 return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction);
2966 case IrInstructionIdFence:3074 case IrInstructionIdFence:
2967 return ir_render_fence(g, executable, (IrInstructionFence *)instruction);3075 return ir_render_fence(g, executable, (IrInstructionFence *)instruction);
2968 case IrInstructionIdDivExact:
2969 return ir_render_div_exact(g, executable, (IrInstructionDivExact *)instruction);
2970 case IrInstructionIdTruncate:3076 case IrInstructionIdTruncate:
2971 return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);3077 return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
2972 case IrInstructionIdBoolNot:3078 case IrInstructionIdBoolNot:
...@@ -4320,7 +4426,6 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4320,7 +4426,6 @@ static void define_builtin_fns(CodeGen *g) {
4320 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);4426 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
4321 create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);4427 create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
4322 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);4428 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);
4323 create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2);
4324 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);4429 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);
4325 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);4430 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
4326 create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);4431 create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
...@@ -4335,6 +4440,11 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4335,6 +4440,11 @@ static void define_builtin_fns(CodeGen *g) {
4335 create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1);4440 create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1);
4336 create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3);4441 create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3);
4337 create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2);4442 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);
4338}4448}
43394449
4340static const char *bool_to_str(bool b) {4450static const char *bool_to_str(bool b) {
src/error.cpp+2
...@@ -23,6 +23,8 @@ const char *err_str(int err) {...@@ -23,6 +23,8 @@ const char *err_str(int err) {
23 case ErrorOverflow: return "overflow";23 case ErrorOverflow: return "overflow";
24 case ErrorPathAlreadyExists: return "path already exists";24 case ErrorPathAlreadyExists: return "path already exists";
25 case ErrorUnexpected: return "unexpected error";25 case ErrorUnexpected: return "unexpected error";
26 case ErrorExactDivRemainder: return "exact division had a remainder";
27 case ErrorNegativeDenominator: return "negative denominator";
26 }28 }
27 return "(invalid error)";29 return "(invalid error)";
28}30}
src/error.hpp+2
...@@ -23,6 +23,8 @@ enum Error {...@@ -23,6 +23,8 @@ enum Error {
23 ErrorOverflow,23 ErrorOverflow,
24 ErrorPathAlreadyExists,24 ErrorPathAlreadyExists,
25 ErrorUnexpected,25 ErrorUnexpected,
26 ErrorExactDivRemainder,
27 ErrorNegativeDenominator,
26};28};
2729
28const char *err_str(int err);30const char *err_str(int err);
src/ir.cpp+201-142
...@@ -400,10 +400,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) {...@@ -400,10 +400,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) {
400 return IrInstructionIdFence;400 return IrInstructionIdFence;
401}401}
402402
403static constexpr IrInstructionId ir_instruction_id(IrInstructionDivExact *) {
404 return IrInstructionIdDivExact;
405}
406
407static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) {403static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) {
408 return IrInstructionIdTruncate;404 return IrInstructionIdTruncate;
409}405}
...@@ -1628,23 +1624,6 @@ static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_ins...@@ -1628,23 +1624,6 @@ static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_ins
1628 return new_instruction;1624 return new_instruction;
1629}1625}
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
1648static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {1627static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
1649 IrInstructionTruncate *instruction = ir_build_instruction<IrInstructionTruncate>(irb, scope, source_node);1628 IrInstructionTruncate *instruction = ir_build_instruction<IrInstructionTruncate>(irb, scope, source_node);
1650 instruction->dest_type = dest_type;1629 instruction->dest_type = dest_type;
...@@ -2597,14 +2576,6 @@ static IrInstruction *ir_instruction_fence_get_dep(IrInstructionFence *instructi...@@ -2597,14 +2576,6 @@ static IrInstruction *ir_instruction_fence_get_dep(IrInstructionFence *instructi
2597 }2576 }
2598}2577}
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
2608static IrInstruction *ir_instruction_truncate_get_dep(IrInstructionTruncate *instruction, size_t index) {2579static IrInstruction *ir_instruction_truncate_get_dep(IrInstructionTruncate *instruction, size_t index) {
2609 switch (index) {2580 switch (index) {
2610 case 0: return instruction->dest_type;2581 case 0: return instruction->dest_type;
...@@ -3022,8 +2993,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t...@@ -3022,8 +2993,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
3022 return ir_instruction_cmpxchg_get_dep((IrInstructionCmpxchg *) instruction, index);2993 return ir_instruction_cmpxchg_get_dep((IrInstructionCmpxchg *) instruction, index);
3023 case IrInstructionIdFence:2994 case IrInstructionIdFence:
3024 return ir_instruction_fence_get_dep((IrInstructionFence *) instruction, index);2995 return ir_instruction_fence_get_dep((IrInstructionFence *) instruction, index);
3025 case IrInstructionIdDivExact:
3026 return ir_instruction_divexact_get_dep((IrInstructionDivExact *) instruction, index);
3027 case IrInstructionIdTruncate:2996 case IrInstructionIdTruncate:
3028 return ir_instruction_truncate_get_dep((IrInstructionTruncate *) instruction, index);2997 return ir_instruction_truncate_get_dep((IrInstructionTruncate *) instruction, index);
3029 case IrInstructionIdIntType:2998 case IrInstructionIdIntType:
...@@ -3644,9 +3613,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -3644,9 +3613,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
3644 case BinOpTypeAssignTimesWrap:3613 case BinOpTypeAssignTimesWrap:
3645 return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap);3614 return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap);
3646 case BinOpTypeAssignDiv:3615 case BinOpTypeAssignDiv:
3647 return ir_gen_assign_op(irb, scope, node, IrBinOpDiv);3616 return ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified);
3648 case BinOpTypeAssignMod:3617 case BinOpTypeAssignMod:
3649 return ir_gen_assign_op(irb, scope, node, IrBinOpRem);3618 return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified);
3650 case BinOpTypeAssignPlus:3619 case BinOpTypeAssignPlus:
3651 return ir_gen_assign_op(irb, scope, node, IrBinOpAdd);3620 return ir_gen_assign_op(irb, scope, node, IrBinOpAdd);
3652 case BinOpTypeAssignPlusWrap:3621 case BinOpTypeAssignPlusWrap:
...@@ -3712,9 +3681,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -3712,9 +3681,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
3712 case BinOpTypeMultWrap:3681 case BinOpTypeMultWrap:
3713 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap);3682 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap);
3714 case BinOpTypeDiv:3683 case BinOpTypeDiv:
3715 return ir_gen_bin_op_id(irb, scope, node, IrBinOpDiv);3684 return ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified);
3716 case BinOpTypeMod:3685 case BinOpTypeMod:
3717 return ir_gen_bin_op_id(irb, scope, node, IrBinOpRem);3686 return ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified);
3718 case BinOpTypeArrayCat:3687 case BinOpTypeArrayCat:
3719 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);3688 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
3720 case BinOpTypeArrayMult:3689 case BinOpTypeArrayMult:
...@@ -4138,7 +4107,63 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4138,7 +4107,63 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4138 if (arg1_value == irb->codegen->invalid_instruction)4107 if (arg1_value == irb->codegen->invalid_instruction)
4139 return arg1_value;4108 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);
4142 }4167 }
4143 case BuiltinFnIdTruncate:4168 case BuiltinFnIdTruncate:
4144 {4169 {
...@@ -8024,32 +8049,70 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -8024,32 +8049,70 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
8024 return ira->codegen->builtin_types.entry_bool;8049 return ira->codegen->builtin_types.entry_bool;
8025}8050}
80268051
8052enum EvalBigNumSpecial {
8053 EvalBigNumSpecialNone,
8054 EvalBigNumSpecialWrapping,
8055 EvalBigNumSpecialExact,
8056};
8057
8027static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,8058static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
8028 ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *),8059 ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *),
8029 TypeTableEntry *type, bool wrapping_op)8060 TypeTableEntry *type, EvalBigNumSpecial special)
8030{8061{
8031 bool is_int = false;8062 bool is_int = false;
8032 bool is_float = false;8063 bool is_float = false;
8033 if (bignum_fn == bignum_div || bignum_fn == bignum_rem) {8064 if (type->id == TypeTableEntryIdInt ||
8034 if (type->id == TypeTableEntryIdInt ||8065 type->id == TypeTableEntryIdNumLitInt)
8035 type->id == TypeTableEntryIdNumLitInt)8066 {
8036 {8067 is_int = true;
8037 is_int = true;8068 } else if (type->id == TypeTableEntryIdFloat ||
8038 } else if (type->id == TypeTableEntryIdFloat ||8069 type->id == TypeTableEntryIdNumLitFloat)
8039 type->id == TypeTableEntryIdNumLitFloat)8070 {
8040 {8071 is_float = true;
8041 is_float = true;8072 } else {
8042 }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 {
8043 if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||8078 if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
8044 (is_float && op2_val->data.x_bignum.data.x_float == 0.0))8079 (is_float && op2_val->data.x_bignum.data.x_float == 0.0))
8045 {8080 {
8046 return ErrorDivByZero;8081 return ErrorDivByZero;
8047 }8082 }
8048 }8083 }
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
8050 bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);8113 bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
8051 if (overflow) {8114 if (overflow) {
8052 if (wrapping_op) {8115 if (special == EvalBigNumSpecialWrapping) {
8053 zig_panic("TODO compiler bug, implement compile-time wrapping arithmetic for >= 64 bit ints");8116 zig_panic("TODO compiler bug, implement compile-time wrapping arithmetic for >= 64 bit ints");
8054 } else {8117 } else {
8055 return ErrorOverflow;8118 return ErrorOverflow;
...@@ -8059,7 +8122,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,...@@ -8059,7 +8122,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
8059 if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum,8122 if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum,
8060 type->data.integral.bit_count, type->data.integral.is_signed))8123 type->data.integral.bit_count, type->data.integral.is_signed))
8061 {8124 {
8062 if (wrapping_op) {8125 if (special == EvalBigNumSpecialWrapping) {
8063 if (type->data.integral.is_signed) {8126 if (type->data.integral.is_signed) {
8064 out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;8127 out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1;
8065 out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative;8128 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,...@@ -8093,35 +8156,44 @@ static int ir_eval_math_op(TypeTableEntry *canon_type, ConstExprValue *op1_val,
8093 case IrBinOpCmpGreaterOrEq:8156 case IrBinOpCmpGreaterOrEq:
8094 case IrBinOpArrayCat:8157 case IrBinOpArrayCat:
8095 case IrBinOpArrayMult:8158 case IrBinOpArrayMult:
8159 case IrBinOpRemUnspecified:
8096 zig_unreachable();8160 zig_unreachable();
8097 case IrBinOpBinOr:8161 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);
8099 case IrBinOpBinXor:8163 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);
8101 case IrBinOpBinAnd:8165 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);
8103 case IrBinOpBitShiftLeft:8167 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);
8105 case IrBinOpBitShiftLeftWrap:8169 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);
8107 case IrBinOpBitShiftRight:8171 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);
8109 case IrBinOpAdd:8173 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);
8111 case IrBinOpAddWrap:8175 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);
8113 case IrBinOpSub:8177 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);
8115 case IrBinOpSubWrap:8179 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);
8117 case IrBinOpMult:8181 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);
8119 case IrBinOpMultWrap:8183 case IrBinOpMultWrap:
8120 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, true);8184 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialWrapping);
8121 case IrBinOpDiv:8185 case IrBinOpDivUnspecified:
8122 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, false);8186 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialNone);
8123 case IrBinOpRem:8187 case IrBinOpDivTrunc:
8124 return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, false);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);
8125 }8197 }
8126 zig_unreachable();8198 zig_unreachable();
8127}8199}
...@@ -8135,6 +8207,31 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp...@@ -8135,6 +8207,31 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
8135 return resolved_type;8207 return resolved_type;
8136 IrBinOp op_id = bin_op_instruction->op_id;8208 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
8138 if (resolved_type->id == TypeTableEntryIdInt ||8235 if (resolved_type->id == TypeTableEntryIdInt ||
8139 resolved_type->id == TypeTableEntryIdNumLitInt)8236 resolved_type->id == TypeTableEntryIdNumLitInt)
8140 {8237 {
...@@ -8144,8 +8241,12 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp...@@ -8144,8 +8241,12 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
8144 (op_id == IrBinOpAdd ||8241 (op_id == IrBinOpAdd ||
8145 op_id == IrBinOpSub ||8242 op_id == IrBinOpSub ||
8146 op_id == IrBinOpMult ||8243 op_id == IrBinOpMult ||
8147 op_id == IrBinOpDiv ||8244 op_id == IrBinOpDivUnspecified ||
8148 op_id == IrBinOpRem))8245 op_id == IrBinOpDivTrunc ||
8246 op_id == IrBinOpDivFloor ||
8247 op_id == IrBinOpDivExact ||
8248 op_id == IrBinOpRemRem ||
8249 op_id == IrBinOpRemMod))
8149 {8250 {
8150 // float8251 // float
8151 } else {8252 } else {
...@@ -8176,20 +8277,25 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp...@@ -8176,20 +8277,25 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
8176 int err;8277 int err;
8177 if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, out_val))) {8278 if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, out_val))) {
8178 if (err == ErrorDivByZero) {8279 if (err == ErrorDivByZero) {
8179 ir_add_error_node(ira, bin_op_instruction->base.source_node,8280 ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("division by zero is undefined"));
8180 buf_sprintf("division by zero is undefined"));
8181 return ira->codegen->builtin_types.entry_invalid;8281 return ira->codegen->builtin_types.entry_invalid;
8182 } else if (err == ErrorOverflow) {8282 } else if (err == ErrorOverflow) {
8183 ir_add_error_node(ira, bin_op_instruction->base.source_node,8283 ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow"));
8184 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"));
8185 return ira->codegen->builtin_types.entry_invalid;8290 return ira->codegen->builtin_types.entry_invalid;
8291 } else {
8292 zig_unreachable();
8186 }8293 }
8187 return ira->codegen->builtin_types.entry_invalid;8294 return ira->codegen->builtin_types.entry_invalid;
8188 }8295 }
81898296
8190 ir_num_lit_fits_in_other_type(ira, &bin_op_instruction->base, resolved_type);8297 ir_num_lit_fits_in_other_type(ira, &bin_op_instruction->base, resolved_type);
8191 return resolved_type;8298 return resolved_type;
8192
8193 }8299 }
81948300
8195 ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id,8301 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...@@ -8197,6 +8303,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
8197 return resolved_type;8303 return resolved_type;
8198}8304}
81998305
8306
8200static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) {8307static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) {
8201 IrInstruction *op1 = instruction->op1->other;8308 IrInstruction *op1 = instruction->op1->other;
8202 TypeTableEntry *op1_type = op1->value.type;8309 TypeTableEntry *op1_type = op1->value.type;
...@@ -8416,8 +8523,13 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi...@@ -8416,8 +8523,13 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
8416 case IrBinOpSubWrap:8523 case IrBinOpSubWrap:
8417 case IrBinOpMult:8524 case IrBinOpMult:
8418 case IrBinOpMultWrap:8525 case IrBinOpMultWrap:
8419 case IrBinOpDiv:8526 case IrBinOpDivUnspecified:
8420 case IrBinOpRem:8527 case IrBinOpDivTrunc:
8528 case IrBinOpDivFloor:
8529 case IrBinOpDivExact:
8530 case IrBinOpRemUnspecified:
8531 case IrBinOpRemRem:
8532 case IrBinOpRemMod:
8421 return ir_analyze_bin_op_math(ira, bin_op_instruction);8533 return ir_analyze_bin_op_math(ira, bin_op_instruction);
8422 case IrBinOpArrayCat:8534 case IrBinOpArrayCat:
8423 return ir_analyze_array_cat(ira, bin_op_instruction);8535 return ir_analyze_array_cat(ira, bin_op_instruction);
...@@ -10007,6 +10119,21 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -10007,6 +10119,21 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
10007 buf_ptr(&child_type->name), buf_ptr(field_name)));10119 buf_ptr(&child_type->name), buf_ptr(field_name)));
10008 return ira->codegen->builtin_types.entry_invalid;10120 return ira->codegen->builtin_types.entry_invalid;
10009 }10121 }
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 }
10010 } else {10137 } else {
10011 ir_add_error(ira, &field_ptr_instruction->base,10138 ir_add_error(ira, &field_ptr_instruction->base,
10012 buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name)));10139 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...@@ -12030,71 +12157,6 @@ static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructio
12030 return ira->codegen->builtin_types.entry_void;12157 return ira->codegen->builtin_types.entry_void;
12031}12158}
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
12098static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) {12160static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) {
12099 IrInstruction *dest_type_value = instruction->dest_type->other;12161 IrInstruction *dest_type_value = instruction->dest_type->other;
12100 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);12162 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
...@@ -13261,8 +13323,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -13261,8 +13323,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
13261 return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction);13323 return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction);
13262 case IrInstructionIdFence:13324 case IrInstructionIdFence:
13263 return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);13325 return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);
13264 case IrInstructionIdDivExact:
13265 return ir_analyze_instruction_div_exact(ira, (IrInstructionDivExact *)instruction);
13266 case IrInstructionIdTruncate:13326 case IrInstructionIdTruncate:
13267 return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction);13327 return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction);
13268 case IrInstructionIdIntType:13328 case IrInstructionIdIntType:
...@@ -13469,7 +13529,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -13469,7 +13529,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
13469 case IrInstructionIdMinValue:13529 case IrInstructionIdMinValue:
13470 case IrInstructionIdMaxValue:13530 case IrInstructionIdMaxValue:
13471 case IrInstructionIdEmbedFile:13531 case IrInstructionIdEmbedFile:
13472 case IrInstructionIdDivExact:
13473 case IrInstructionIdTruncate:13532 case IrInstructionIdTruncate:
13474 case IrInstructionIdIntType:13533 case IrInstructionIdIntType:
13475 case IrInstructionIdBoolNot:13534 case IrInstructionIdBoolNot:
src/ir_print.cpp+12-13
...@@ -109,10 +109,20 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {...@@ -109,10 +109,20 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
109 return "*";109 return "*";
110 case IrBinOpMultWrap:110 case IrBinOpMultWrap:
111 return "*%";111 return "*%";
112 case IrBinOpDiv:112 case IrBinOpDivUnspecified:
113 return "/";113 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:
115 return "%";121 return "%";
122 case IrBinOpRemRem:
123 return "@rem";
124 case IrBinOpRemMod:
125 return "@mod";
116 case IrBinOpArrayCat:126 case IrBinOpArrayCat:
117 return "++";127 return "++";
118 case IrBinOpArrayMult:128 case IrBinOpArrayMult:
...@@ -580,14 +590,6 @@ static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) {...@@ -580,14 +590,6 @@ static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) {
580 fprintf(irp->f, ")");590 fprintf(irp->f, ")");
581}591}
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
591static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) {593static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) {
592 fprintf(irp->f, "@truncate(");594 fprintf(irp->f, "@truncate(");
593 ir_print_other_instruction(irp, instruction->dest_type);595 ir_print_other_instruction(irp, instruction->dest_type);
...@@ -1056,9 +1058,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1056,9 +1058,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1056 case IrInstructionIdFence:1058 case IrInstructionIdFence:
1057 ir_print_fence(irp, (IrInstructionFence *)instruction);1059 ir_print_fence(irp, (IrInstructionFence *)instruction);
1058 break;1060 break;
1059 case IrInstructionIdDivExact:
1060 ir_print_div_exact(irp, (IrInstructionDivExact *)instruction);
1061 break;
1062 case IrInstructionIdTruncate:1061 case IrInstructionIdTruncate:
1063 ir_print_truncate(irp, (IrInstructionTruncate *)instruction);1062 ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
1064 break;1063 break;
src/link.cpp+2
...@@ -297,6 +297,7 @@ static void construct_linker_job_elf(LinkJob *lj) {...@@ -297,6 +297,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
297 lj->args.append("-lgcc");297 lj->args.append("-lgcc");
298 lj->args.append("-lgcc_eh");298 lj->args.append("-lgcc_eh");
299 lj->args.append("-lc");299 lj->args.append("-lc");
300 lj->args.append("-lm");
300 lj->args.append("--end-group");301 lj->args.append("--end-group");
301 } else {302 } else {
302 lj->args.append("-lgcc");303 lj->args.append("-lgcc");
...@@ -304,6 +305,7 @@ static void construct_linker_job_elf(LinkJob *lj) {...@@ -304,6 +305,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
304 lj->args.append("-lgcc_s");305 lj->args.append("-lgcc_s");
305 lj->args.append("--no-as-needed");306 lj->args.append("--no-as-needed");
306 lj->args.append("-lc");307 lj->args.append("-lc");
308 lj->args.append("-lm");
307 lj->args.append("-lgcc");309 lj->args.append("-lgcc");
308 lj->args.append("--as-needed");310 lj->args.append("--as-needed");
309 lj->args.append("-lgcc_s");311 lj->args.append("-lgcc_s");
std/elf.zig+3-4
...@@ -165,9 +165,9 @@ pub const Elf = struct {...@@ -165,9 +165,9 @@ pub const Elf = struct {
165 if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;165 if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
166166
167 const sh_byte_count = u64(sh_entry_size) * u64(sh_entry_count);167 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);
169 const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count);169 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
172 const stream_end = %return elf.in_stream.getEndPos();172 const stream_end = %return elf.in_stream.getEndPos();
173 if (stream_end < end_sh or stream_end < end_ph) {173 if (stream_end < end_sh or stream_end < end_ph) {
...@@ -214,8 +214,7 @@ pub const Elf = struct {...@@ -214,8 +214,7 @@ pub const Elf = struct {
214214
215 for (elf.section_headers) |*section| {215 for (elf.section_headers) |*section| {
216 if (section.sh_type != SHT_NOBITS) {216 if (section.sh_type != SHT_NOBITS) {
217 const file_end_offset = %return math.addOverflow(u64,217 const file_end_offset = %return math.add(u64, section.offset, section.size);
218 section.offset, section.size);
219 if (stream_end < file_end_offset) return error.InvalidFormat;218 if (stream_end < file_end_offset) return error.InvalidFormat;
220 }219 }
221 }220 }
std/fmt.zig+2-2
...@@ -305,8 +305,8 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {...@@ -305,8 +305,8 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
305305
306 for (buf) |c| {306 for (buf) |c| {
307 const digit = %return charToDigit(c, radix);307 const digit = %return charToDigit(c, radix);
308 x = %return math.mulOverflow(T, x, radix);308 x = %return math.mul(T, x, radix);
309 x = %return math.addOverflow(T, x, digit);309 x = %return math.add(T, x, digit);
310 }310 }
311311
312 return x;312 return x;
std/math.zig+211-28
...@@ -1,37 +1,64 @@...@@ -1,37 +1,64 @@
1const assert = @import("debug.zig").assert;1const assert = @import("debug.zig").assert;
22
3pub const Cmp = enum {3pub const Cmp = enum {
4 Less,
4 Equal,5 Equal,
5 Greater,6 Greater,
6 Less,
7};7};
88
9pub fn min(x: var, y: var) -> @typeOf(x + y) {9pub fn min(x: var, y: var) -> @typeOf(x + y) {
10 if (x < y) x else y10 if (x < y) x else y
11}11}
1212
13test "math.min" {
14 assert(min(i32(-1), i32(2)) == -1);
15}
16
13pub fn max(x: var, y: var) -> @typeOf(x + y) {17pub fn max(x: var, y: var) -> @typeOf(x + y) {
14 if (x > y) x else y18 if (x > y) x else y
15}19}
1620
21test "math.max" {
22 assert(max(i32(-1), i32(2)) == 2);
23}
24
17error Overflow;25error Overflow;
18pub fn mulOverflow(comptime T: type, a: T, b: T) -> %T {26pub fn mul(comptime T: type, a: T, b: T) -> %T {
19 var answer: T = undefined;27 var answer: T = undefined;
20 if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer28 if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer
21}29}
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 {
23 var answer: T = undefined;33 var answer: T = undefined;
24 if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer34 if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer
25}35}
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 {
27 var answer: T = undefined;39 var answer: T = undefined;
28 if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer40 if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer
29}41}
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 {
31 var answer: T = undefined;45 var answer: T = undefined;
32 if (@shlWithOverflow(T, a, b, &answer)) error.Overflow else answer46 if (@shlWithOverflow(T, a, b, &answer)) error.Overflow else answer
33}47}
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
35pub fn log(comptime base: usize, value: var) -> @typeOf(value) {62pub fn log(comptime base: usize, value: var) -> @typeOf(value) {
36 const T = @typeOf(value);63 const T = @typeOf(value);
37 if (@isInteger(T)) {64 if (@isInteger(T)) {
...@@ -47,35 +74,191 @@ pub fn log(comptime base: usize, value: var) -> @typeOf(value) {...@@ -47,35 +74,191 @@ pub fn log(comptime base: usize, value: var) -> @typeOf(value) {
47 }74 }
48}75}
4976
50/// x must be an integer or a float77error Overflow;
51/// Note that this causes undefined behavior if78pub fn absInt(x: var) -> %@typeOf(x) {
52/// @typeOf(x).is_signed and x == @minValue(@typeOf(x)).
53pub fn abs(x: var) -> @typeOf(x) {
54 const T = @typeOf(x);79 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);
56 return if (x < 0) -x else x;86 return if (x < 0) -x else x;
57 } else if (@isFloat(T)) {
58 @compileError("TODO implement abs for floats");
59 } else {
60 unreachable;
61 }87 }
62}88}
63fn getReturnTypeForAbs(comptime T: type) -> type {89
64 if (@isInteger(T)) {90test "math.absInt" {
65 return @IntType(false, T.bit_count);91 testAbsInt();
66 } else {92 comptime testAbsInt();
67 return T;93}
68 }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();
69}181}
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" {189 assert(%%divExact(f32, 10.0, 5.0) == 2.0);
72 testMathImpl();190 assert(%%divExact(f32, -10.0, 5.0) == -2.0);
73 comptime testMathImpl();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 }
74}258}
75259
76fn testMathImpl() {260fn floatBits(comptime T: type, x: T) -> @IntType(false, T.bit_count) {
77 assert(%%mulOverflow(i32, 3, 4) == 12);261 assert(@isFloat(T));
78 assert(%%addOverflow(i32, 3, 4) == 7);262 const uint = @IntType(false, T.bit_count);
79 assert(%%subOverflow(i32, 3, 4) == -1);263 return *@intToPtr(&const uint, &x);
80 assert(%%shlOverflow(i32, 0b11, 4) == 0b110000);
81}264}
std/mem.zig+28-2
...@@ -35,12 +35,12 @@ pub const Allocator = struct {...@@ -35,12 +35,12 @@ pub const Allocator = struct {
35 }35 }
3636
37 fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {37 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);
39 ([]T)(%return self.allocFn(self, byte_count))39 ([]T)(%return self.allocFn(self, byte_count))
40 }40 }
4141
42 fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {42 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);
44 ([]T)(%return self.reallocFn(self, ([]u8)(old_mem), byte_count))44 ([]T)(%return self.reallocFn(self, ([]u8)(old_mem), byte_count))
45 }45 }
4646
...@@ -333,3 +333,29 @@ fn testWriteIntImpl() {...@@ -333,3 +333,29 @@ fn testWriteIntImpl() {
333 assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 }));333 assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 }));
334}334}
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() {...@@ -29,3 +29,95 @@ export fn __stack_chk_fail() {
29 }29 }
30 @panic("stack smashing detected");30 @panic("stack smashing detected");
31}31}
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 @@...@@ -1,5 +1,5 @@
1// This file contains functions that zig depends on to coordinate between1// This file contains functions that zig depends on to coordinate between
2// multiple .o files. The symbols are defined Weak so that multiple2// multiple .o files. The symbols are defined LinkOnce so that multiple
3// instances of zig_rt.zig do not conflict with each other.3// instances of zig_rt.zig do not conflict with each other.
44
5const builtin = @import("builtin");5const builtin = @import("builtin");
test/cases/math.zig+64-26
...@@ -1,47 +1,76 @@...@@ -1,47 +1,76 @@
1const assert = @import("std").debug.assert;1const assert = @import("std").debug.assert;
22
3test "exactDivision" {3test "division" {
4 assert(divExact(55, 11) == 5);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
5}32}
6fn divExact(a: u32, b: u32) -> u32 {33fn divExact(comptime T: type, a: T, b: T) -> T {
7 @divExact(a, b)34 @divExact(a, b)
8}35}
936fn divFloor(comptime T: type, a: T, b: T) -> T {
10test "floatDivision" {37 @divFloor(a, b)
11 assert(fdiv32(12.0, 3.0) == 4.0);
12}38}
13fn fdiv32(a: f32, b: f32) -> f32 {39fn divTrunc(comptime T: type, a: T, b: T) -> T {
14 a / b40 @divTrunc(a, b)
15}41}
1642
17test "overflowIntrinsics" {43test "@addWithOverflow" {
18 var result: u8 = undefined;44 var result: u8 = undefined;
19 assert(@addWithOverflow(u8, 250, 100, &result));45 assert(@addWithOverflow(u8, 250, 100, &result));
20 assert(!@addWithOverflow(u8, 100, 150, &result));46 assert(!@addWithOverflow(u8, 100, 150, &result));
21 assert(result == 250);47 assert(result == 250);
22}48}
2349
24test "shlWithOverflow" {50// TODO test mulWithOverflow
51// TODO test subWithOverflow
52
53test "@shlWithOverflow" {
25 var result: u16 = undefined;54 var result: u16 = undefined;
26 assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));55 assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
27 assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));56 assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
28 assert(result == 0b1011111111111100);57 assert(result == 0b1011111111111100);
29}58}
3059
31test "countLeadingZeroes" {60test "@clz" {
32 assert(@clz(u8(0b00001010)) == 4);61 assert(@clz(u8(0b00001010)) == 4);
33 assert(@clz(u8(0b10001010)) == 0);62 assert(@clz(u8(0b10001010)) == 0);
34 assert(@clz(u8(0b00000000)) == 8);63 assert(@clz(u8(0b00000000)) == 8);
35}64}
3665
37test "countTrailingZeroes" {66test "@ctz" {
38 assert(@ctz(u8(0b10100000)) == 5);67 assert(@ctz(u8(0b10100000)) == 5);
39 assert(@ctz(u8(0b10001010)) == 1);68 assert(@ctz(u8(0b10001010)) == 1);
40 assert(@ctz(u8(0b00000000)) == 8);69 assert(@ctz(u8(0b00000000)) == 8);
41}70}
4271
43test "modifyOperators" {72test "assignment operators" {
44 var i : i32 = 0;73 var i: u32 = 0;
45 i += 5; assert(i == 5);74 i += 5; assert(i == 5);
46 i -= 2; assert(i == 3);75 i -= 2; assert(i == 3);
47 i *= 20; assert(i == 60);76 i *= 20; assert(i == 60);
...@@ -57,6 +86,8 @@ test "modifyOperators" {...@@ -57,6 +86,8 @@ test "modifyOperators" {
57}86}
5887
59test "threeExprInARow" {88test "threeExprInARow" {
89 testThreeExprInARow(false, true);
90 comptime testThreeExprInARow(false, true);
60}91}
61fn testThreeExprInARow(f: bool, t: bool) {92fn testThreeExprInARow(f: bool, t: bool) {
62 assertFalse(f or f or f);93 assertFalse(f or f or f);
...@@ -72,13 +103,12 @@ fn testThreeExprInARow(f: bool, t: bool) {...@@ -72,13 +103,12 @@ fn testThreeExprInARow(f: bool, t: bool) {
72 assertFalse(!!false);103 assertFalse(!!false);
73 assertFalse(i32(7) != --(i32(7)));104 assertFalse(i32(7) != --(i32(7)));
74}105}
75
76fn assertFalse(b: bool) {106fn assertFalse(b: bool) {
77 assert(!b);107 assert(!b);
78}108}
79109
80110
81test "constNumberLiteral" {111test "const number literal" {
82 const one = 1;112 const one = 1;
83 const eleven = ten + one;113 const eleven = ten + one;
84114
...@@ -88,8 +118,9 @@ const ten = 10;...@@ -88,8 +118,9 @@ const ten = 10;
88118
89119
90120
91test "unsignedWrapping" {121test "unsigned wrapping" {
92 testUnsignedWrappingEval(@maxValue(u32));122 testUnsignedWrappingEval(@maxValue(u32));
123 comptime testUnsignedWrappingEval(@maxValue(u32));
93}124}
94fn testUnsignedWrappingEval(x: u32) {125fn testUnsignedWrappingEval(x: u32) {
95 const zero = x +% 1;126 const zero = x +% 1;
...@@ -98,8 +129,9 @@ fn testUnsignedWrappingEval(x: u32) {...@@ -98,8 +129,9 @@ fn testUnsignedWrappingEval(x: u32) {
98 assert(orig == @maxValue(u32));129 assert(orig == @maxValue(u32));
99}130}
100131
101test "signedWrapping" {132test "signed wrapping" {
102 testSignedWrappingEval(@maxValue(i32));133 testSignedWrappingEval(@maxValue(i32));
134 comptime testSignedWrappingEval(@maxValue(i32));
103}135}
104fn testSignedWrappingEval(x: i32) {136fn testSignedWrappingEval(x: i32) {
105 const min_val = x +% 1;137 const min_val = x +% 1;
...@@ -108,8 +140,9 @@ fn testSignedWrappingEval(x: i32) {...@@ -108,8 +140,9 @@ fn testSignedWrappingEval(x: i32) {
108 assert(max_val == @maxValue(i32));140 assert(max_val == @maxValue(i32));
109}141}
110142
111test "negationWrapping" {143test "negation wrapping" {
112 testNegationWrappingEval(@minValue(i16));144 testNegationWrappingEval(@minValue(i16));
145 comptime testNegationWrappingEval(@minValue(i16));
113}146}
114fn testNegationWrappingEval(x: i16) {147fn testNegationWrappingEval(x: i16) {
115 assert(x == -32768);148 assert(x == -32768);
...@@ -117,20 +150,25 @@ fn testNegationWrappingEval(x: i16) {...@@ -117,20 +150,25 @@ fn testNegationWrappingEval(x: i16) {
117 assert(neg == -32768);150 assert(neg == -32768);
118}151}
119152
120test "shlWrapping" {153test "shift left wrapping" {
121 testShlWrappingEval(@maxValue(u16));154 testShlWrappingEval(@maxValue(u16));
155 comptime testShlWrappingEval(@maxValue(u16));
122}156}
123fn testShlWrappingEval(x: u16) {157fn testShlWrappingEval(x: u16) {
124 const shifted = x <<% 1;158 const shifted = x <<% 1;
125 assert(shifted == 65534);159 assert(shifted == 65534);
126}160}
127161
128test "unsigned64BitDivision" {162test "unsigned 64-bit division" {
129 const result = div(1152921504606846976, 34359738365);163 test_u64_div();
164 comptime test_u64_div();
165}
166fn test_u64_div() {
167 const result = divWithResult(1152921504606846976, 34359738365);
130 assert(result.quotient == 33554432);168 assert(result.quotient == 33554432);
131 assert(result.remainder == 100663296);169 assert(result.remainder == 100663296);
132}170}
133fn div(a: u64, b: u64) -> DivResult {171fn divWithResult(a: u64, b: u64) -> DivResult {
134 DivResult {172 DivResult {
135 .quotient = a / b,173 .quotient = a / b,
136 .remainder = a % b,174 .remainder = a % b,
...@@ -141,7 +179,7 @@ const DivResult = struct {...@@ -141,7 +179,7 @@ const DivResult = struct {
141 remainder: u64,179 remainder: u64,
142};180};
143181
144test "binaryNot" {182test "binary not" {
145 assert(comptime {~u16(0b1010101010101010) == 0b0101010101010101});183 assert(comptime {~u16(0b1010101010101010) == 0b0101010101010101});
146 assert(comptime {~u64(2147483647) == 18446744071562067968});184 assert(comptime {~u64(2147483647) == 18446744071562067968});
147 testBinaryNot(0b1010101010101010);185 testBinaryNot(0b1010101010101010);
...@@ -151,7 +189,7 @@ fn testBinaryNot(x: u16) {...@@ -151,7 +189,7 @@ fn testBinaryNot(x: u16) {
151 assert(~x == 0b0101010101010101);189 assert(~x == 0b0101010101010101);
152}190}
153191
154test "smallIntAddition" {192test "small int addition" {
155 var x: @IntType(false, 2) = 0;193 var x: @IntType(false, 2) = 0;
156 assert(x == 0);194 assert(x == 0);
157195
...@@ -170,7 +208,7 @@ test "smallIntAddition" {...@@ -170,7 +208,7 @@ test "smallIntAddition" {
170 assert(result == 0);208 assert(result == 0);
171}209}
172210
173test "testFloatEquality" {211test "float equality" {
174 const x: f64 = 0.012;212 const x: f64 = 0.012;
175 const y: f64 = x + 1.0;213 const y: f64 = x + 1.0;
176214
test/cases/misc.zig+5
...@@ -49,6 +49,11 @@ test "@IntType builtin" {...@@ -49,6 +49,11 @@ test "@IntType builtin" {
49 assert(!usize.is_signed);49 assert(!usize.is_signed);
50}50}
5151
52test "floating point primitive bit counts" {
53 assert(f32.bit_count == 32);
54 assert(f64.bit_count == 64);
55}
56
52const u1 = @IntType(false, 1);57const u1 = @IntType(false, 1);
53const u63 = @IntType(false, 63);58const u63 = @IntType(false, 63);
54const i1 = @IntType(true, 1);59const i1 = @IntType(true, 1);
test/compile_errors.zig+16-2
...@@ -702,7 +702,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -702,7 +702,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
702 cases.add("division by zero",702 cases.add("division by zero",
703 \\const lit_int_x = 1 / 0;703 \\const lit_int_x = 1 / 0;
704 \\const lit_float_x = 1.0 / 0.0;704 \\const lit_float_x = 1.0 / 0.0;
705 \\const int_x = i32(1) / i32(0);705 \\const int_x = u32(1) / u32(0);
706 \\const float_x = f32(1.0) / f32(0.0);706 \\const float_x = f32(1.0) / f32(0.0);
707 \\707 \\
708 \\export fn entry1() -> usize { @sizeOf(@typeOf(lit_int_x)) }708 \\export fn entry1() -> usize { @sizeOf(@typeOf(lit_int_x)) }
...@@ -792,7 +792,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -792,7 +792,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
792792
793 cases.add("compile time division by zero",793 cases.add("compile time division by zero",
794 \\const y = foo(0);794 \\const y = foo(0);
795 \\fn foo(x: i32) -> i32 {795 \\fn foo(x: u32) -> u32 {
796 \\ 1 / x796 \\ 1 / x
797 \\}797 \\}
798 \\798 \\
...@@ -1709,4 +1709,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) {...@@ -1709,4 +1709,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
1709 \\extern fn quux(usize);1709 \\extern fn quux(usize);
1710 ,1710 ,
1711 ".tmp_source.zig:4:8: error: unable to inline function");1711 ".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");
1712}1726}
test/debug_safety.zig+2-2
...@@ -97,7 +97,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -97,7 +97,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
97 \\ if (x == 32767) return error.Whatever;97 \\ if (x == 32767) return error.Whatever;
98 \\}98 \\}
99 \\fn div(a: i16, b: i16) -> i16 {99 \\fn div(a: i16, b: i16) -> i16 {
100 \\ a / b100 \\ @divTrunc(a, b)
101 \\}101 \\}
102 );102 );
103103
...@@ -141,7 +141,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -141,7 +141,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
141 \\ const x = div0(999, 0);141 \\ const x = div0(999, 0);
142 \\}142 \\}
143 \\fn div0(a: i32, b: i32) -> i32 {143 \\fn div0(a: i32, b: i32) -> i32 {
144 \\ a / b144 \\ @divTrunc(a, b)
145 \\}145 \\}
146 );146 );
147147