| author | |
| committer | |
| log | 157af4332a7b78672ff8ad76a00120455547e2fd |
| tree | 0e75d0d9bb111f8e778587a2fb547b74a17c5aa1 |
| parent | 866c841dd8770bcc12af0aaf946c80819f5e0092 |
* 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 #21721 files changed, 973 insertions(+), 312 deletions(-)
doc/langref.md+65-9| ... | ... | @@ -502,15 +502,6 @@ This function performs an atomic compare exchange operation. |
| 502 | 502 | |
| 503 | 503 | The `fence` function is used to introduce happens-before edges between operations. |
| 504 | 504 | |
| 505 | ### @divExact(a: T, b: T) -> T | |
| 506 | ||
| 507 | This function performs integer division `a / b` and returns the result. | |
| 508 | ||
| 509 | The caller guarantees that this operation will have no remainder. | |
| 510 | ||
| 511 | In debug mode, a remainder causes a panic. In release mode, a remainder is | |
| 512 | undefined behavior. | |
| 513 | ||
| 514 | 505 | ### @truncate(comptime T: type, integer) -> T |
| 515 | 506 | |
| 516 | 507 | This 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 | 612 | ### @fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8, field_ptr: &T) -> &ParentType |
| 622 | 613 | |
| 623 | 614 | Given a pointer to a field, returns the base pointer of a struct. |
| 615 | ||
| 616 | ### @rem(numerator: T, denominator: T) -> T | |
| 617 | ||
| 618 | Remainder 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 | ||
| 624 | See also: | |
| 625 | * `std.math.rem` | |
| 626 | * `@mod` | |
| 627 | ||
| 628 | ### @mod(numerator: T, denominator: T) -> T | |
| 629 | ||
| 630 | Modulus 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 | ||
| 636 | See also: | |
| 637 | * `std.math.mod` | |
| 638 | * `@rem` | |
| 639 | ||
| 640 | ### @divTrunc(numerator: T, denominator: T) -> T | |
| 641 | ||
| 642 | Truncated division. Rounds toward zero. For unsigned integers it is | |
| 643 | the 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 | ||
| 649 | See also: | |
| 650 | * `std.math.divTrunc` | |
| 651 | * `@divFloor` | |
| 652 | * `@divExact` | |
| 653 | ||
| 654 | ### @divFloor(numerator: T, denominator: T) -> T | |
| 655 | ||
| 656 | Floored division. Rounds toward negative infinity. For unsigned integers it is | |
| 657 | the 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 | ||
| 663 | See also: | |
| 664 | * `std.math.divFloor` | |
| 665 | * `@divTrunc` | |
| 666 | * `@divExact` | |
| 667 | ||
| 668 | ### @divExact(numerator: T, denominator: T) -> T | |
| 669 | ||
| 670 | Exact 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 | ||
| 676 | See also: | |
| 677 | * `std.math.divExact` | |
| 678 | * `@divTrunc` | |
| 679 | * `@divFloor` |
src/all_types.hpp+16-10| ... | ... | @@ -1195,6 +1195,10 @@ enum BuiltinFnId { |
| 1195 | 1195 | BuiltinFnIdCmpExchange, |
| 1196 | 1196 | BuiltinFnIdFence, |
| 1197 | 1197 | BuiltinFnIdDivExact, |
| 1198 | BuiltinFnIdDivTrunc, | |
| 1199 | BuiltinFnIdDivFloor, | |
| 1200 | BuiltinFnIdRem, | |
| 1201 | BuiltinFnIdMod, | |
| 1198 | 1202 | BuiltinFnIdTruncate, |
| 1199 | 1203 | BuiltinFnIdIntType, |
| 1200 | 1204 | BuiltinFnIdSetDebugSafety, |
| ... | ... | @@ -1270,6 +1274,8 @@ enum ZigLLVMFnId { |
| 1270 | 1274 | ZigLLVMFnIdCtz, |
| 1271 | 1275 | ZigLLVMFnIdClz, |
| 1272 | 1276 | ZigLLVMFnIdOverflowArithmetic, |
| 1277 | ZigLLVMFnIdFloor, | |
| 1278 | ZigLLVMFnIdCeil, | |
| 1273 | 1279 | }; |
| 1274 | 1280 | |
| 1275 | 1281 | enum AddSubMul { |
| ... | ... | @@ -1288,6 +1294,9 @@ struct ZigLLVMFnKey { |
| 1288 | 1294 | struct { |
| 1289 | 1295 | uint32_t bit_count; |
| 1290 | 1296 | } clz; |
| 1297 | struct { | |
| 1298 | uint32_t bit_count; | |
| 1299 | } floor_ceil; | |
| 1291 | 1300 | struct { |
| 1292 | 1301 | AddSubMul add_sub_mul; |
| 1293 | 1302 | uint32_t bit_count; |
| ... | ... | @@ -1746,7 +1755,6 @@ enum IrInstructionId { |
| 1746 | 1755 | IrInstructionIdEmbedFile, |
| 1747 | 1756 | IrInstructionIdCmpxchg, |
| 1748 | 1757 | IrInstructionIdFence, |
| 1749 | IrInstructionIdDivExact, | |
| 1750 | 1758 | IrInstructionIdTruncate, |
| 1751 | 1759 | IrInstructionIdIntType, |
| 1752 | 1760 | IrInstructionIdBoolNot, |
| ... | ... | @@ -1897,8 +1905,13 @@ enum IrBinOp { |
| 1897 | 1905 | IrBinOpSubWrap, |
| 1898 | 1906 | IrBinOpMult, |
| 1899 | 1907 | IrBinOpMultWrap, |
| 1900 | IrBinOpDiv, | |
| 1901 | IrBinOpRem, | |
| 1908 | IrBinOpDivUnspecified, | |
| 1909 | IrBinOpDivExact, | |
| 1910 | IrBinOpDivTrunc, | |
| 1911 | IrBinOpDivFloor, | |
| 1912 | IrBinOpRemUnspecified, | |
| 1913 | IrBinOpRemRem, | |
| 1914 | IrBinOpRemMod, | |
| 1902 | 1915 | IrBinOpArrayCat, |
| 1903 | 1916 | IrBinOpArrayMult, |
| 1904 | 1917 | }; |
| ... | ... | @@ -2250,13 +2263,6 @@ struct IrInstructionFence { |
| 2250 | 2263 | AtomicOrder order; |
| 2251 | 2264 | }; |
| 2252 | 2265 | |
| 2253 | struct IrInstructionDivExact { | |
| 2254 | IrInstruction base; | |
| 2255 | ||
| 2256 | IrInstruction *op1; | |
| 2257 | IrInstruction *op2; | |
| 2258 | }; | |
| 2259 | ||
| 2260 | 2266 | struct IrInstructionTruncate { |
| 2261 | 2267 | IrInstruction base; |
| 2262 | 2268 |
src/analyze.cpp+7| ... | ... | @@ -4228,6 +4228,10 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { |
| 4228 | 4228 | return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934; |
| 4229 | 4229 | case ZigLLVMFnIdClz: |
| 4230 | 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 | 4235 | case ZigLLVMFnIdOverflowArithmetic: |
| 4232 | 4236 | return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) + |
| 4233 | 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 | 4248 | return a.data.ctz.bit_count == b.data.ctz.bit_count; |
| 4245 | 4249 | case ZigLLVMFnIdClz: |
| 4246 | 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 | 4254 | case ZigLLVMFnIdOverflowArithmetic: |
| 4248 | 4255 | return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && |
| 4249 | 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 | 204 | |
| 205 | 205 | if (dest->kind == BigNumKindFloat) { |
| 206 | 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 | ||
| 213 | bool 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 | 224 | } else { |
| 208 | 225 | dest->data.x_uint = op1->data.x_uint / op2->data.x_uint; |
| 209 | 226 | dest->is_negative = op1->is_negative != op2->is_negative; |
| ... | ... | @@ -212,6 +229,29 @@ bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) { |
| 212 | 229 | return false; |
| 213 | 230 | } |
| 214 | 231 | |
| 232 | bool 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 | ||
| 215 | 255 | bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) { |
| 216 | 256 | assert(op1->kind == op2->kind); |
| 217 | 257 | dest->kind = op1->kind; |
| ... | ... | @@ -219,10 +259,28 @@ bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2) { |
| 219 | 259 | if (dest->kind == BigNumKindFloat) { |
| 220 | 260 | dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float); |
| 221 | 261 | } else { |
| 222 | if (op1->is_negative || op2->is_negative) { | |
| 223 | zig_panic("TODO handle remainder division with negative numbers"); | |
| 224 | } | |
| 262 | assert(!op2->is_negative); | |
| 225 | 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 | ||
| 270 | bool 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 | 284 | bignum_normalize(dest); |
| 227 | 285 | } |
| 228 | 286 | return false; |
src/bignum.hpp+3| ... | ... | @@ -37,7 +37,10 @@ bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2); |
| 37 | 37 | bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2); |
| 38 | 38 | bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2); |
| 39 | 39 | bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2); |
| 40 | bool bignum_div_trunc(BigNum *dest, BigNum *op1, BigNum *op2); | |
| 41 | bool bignum_div_floor(BigNum *dest, BigNum *op1, BigNum *op2); | |
| 40 | 42 | bool bignum_rem(BigNum *dest, BigNum *op1, BigNum *op2); |
| 43 | bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2); | |
| 41 | 44 | |
| 42 | 45 | bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2); |
| 43 | 46 | bool 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 | 538 | return fn_val; |
| 539 | 539 | } |
| 540 | 540 | |
| 541 | static 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 | ||
| 541 | 570 | static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntry *type, bool is_volatile) { |
| 542 | 571 | if (type_has_bits(type)) { |
| 543 | 572 | if (handle_is_ptr(type)) { |
| ... | ... | @@ -618,7 +647,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 618 | 647 | case PanicMsgIdDivisionByZero: |
| 619 | 648 | return buf_create_from_str("division by zero"); |
| 620 | 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 | 651 | case PanicMsgIdExactDivisionRemainder: |
| 623 | 652 | return buf_create_from_str("exact division produced remainder"); |
| 624 | 653 | case PanicMsgIdSliceWidenRemainder: |
| ... | ... | @@ -1099,12 +1128,34 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry, |
| 1099 | 1128 | return result; |
| 1100 | 1129 | } |
| 1101 | 1130 | |
| 1131 | static 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 | ||
| 1139 | static 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 | ||
| 1147 | enum DivKind { | |
| 1148 | DivKindFloat, | |
| 1149 | DivKindTrunc, | |
| 1150 | DivKindFloor, | |
| 1151 | DivKindExact, | |
| 1152 | }; | |
| 1153 | ||
| 1102 | 1154 | static 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 | { |
| 1105 | ||
| 1157 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); | |
| 1106 | 1158 | if (want_debug_safety) { |
| 1107 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); | |
| 1108 | 1159 | LLVMValueRef is_zero_bit; |
| 1109 | 1160 | if (type_entry->id == TypeTableEntryIdInt) { |
| 1110 | 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 | 1191 | } |
| 1141 | 1192 | |
| 1142 | 1193 | if (type_entry->id == TypeTableEntryIdFloat) { |
| 1143 | assert(!exact); | |
| 1144 | return LLVMBuildFDiv(g->builder, val1, val2, ""); | |
| 1194 | LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); | |
| 1195 | switch (div_kind) { | |
| 1196 | case DivKindFloat: | |
| 1197 | return result; | |
| 1198 | case DivKindExact: | |
| 1199 | if (want_debug_safety) { | |
| 1200 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 1201 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); | |
| 1202 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | |
| 1203 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); | |
| 1204 | ||
| 1205 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 1206 | ||
| 1207 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 1208 | gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder); | |
| 1209 | ||
| 1210 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 1211 | } | |
| 1212 | return result; | |
| 1213 | case DivKindTrunc: | |
| 1214 | { | |
| 1215 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 1216 | LLVMValueRef ceiled = gen_ceil(g, result, type_entry); | |
| 1217 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); | |
| 1218 | return LLVMBuildSelect(g->builder, ltz, ceiled, floored, ""); | |
| 1219 | } | |
| 1220 | case DivKindFloor: | |
| 1221 | return gen_floor(g, result, type_entry); | |
| 1222 | } | |
| 1223 | zig_unreachable(); | |
| 1145 | 1224 | } |
| 1146 | 1225 | |
| 1147 | 1226 | assert(type_entry->id == TypeTableEntryIdInt); |
| 1148 | 1227 | |
| 1149 | if (exact) { | |
| 1150 | if (want_debug_safety) { | |
| 1151 | LLVMValueRef remainder_val; | |
| 1228 | switch (div_kind) { | |
| 1229 | case DivKindFloat: | |
| 1230 | zig_unreachable(); | |
| 1231 | case DivKindTrunc: | |
| 1152 | 1232 | if (type_entry->data.integral.is_signed) { |
| 1153 | remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); | |
| 1233 | return LLVMBuildSDiv(g->builder, val1, val2, ""); | |
| 1154 | 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); | |
| 1158 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | |
| 1237 | case DivKindExact: | |
| 1238 | if (want_debug_safety) { | |
| 1239 | LLVMValueRef remainder_val; | |
| 1240 | if (type_entry->data.integral.is_signed) { | |
| 1241 | remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); | |
| 1242 | } else { | |
| 1243 | remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); | |
| 1244 | } | |
| 1245 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | |
| 1159 | 1246 | |
| 1160 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); | |
| 1161 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | |
| 1162 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 1247 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); | |
| 1248 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | |
| 1249 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 1163 | 1250 | |
| 1164 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 1165 | gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder); | |
| 1251 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 1252 | gen_debug_safety_crash(g, PanicMsgIdExactDivisionRemainder); | |
| 1166 | 1253 | |
| 1167 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 1168 | } | |
| 1169 | if (type_entry->data.integral.is_signed) { | |
| 1170 | return LLVMBuildExactSDiv(g->builder, val1, val2, ""); | |
| 1171 | } else { | |
| 1172 | return LLVMBuildExactUDiv(g->builder, val1, val2, ""); | |
| 1173 | } | |
| 1174 | } else { | |
| 1175 | if (type_entry->data.integral.is_signed) { | |
| 1176 | return LLVMBuildSDiv(g->builder, val1, val2, ""); | |
| 1177 | } else { | |
| 1178 | return LLVMBuildUDiv(g->builder, val1, val2, ""); | |
| 1179 | } | |
| 1254 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 1255 | } | |
| 1256 | if (type_entry->data.integral.is_signed) { | |
| 1257 | return LLVMBuildExactSDiv(g->builder, val1, val2, ""); | |
| 1258 | } else { | |
| 1259 | return LLVMBuildExactUDiv(g->builder, val1, val2, ""); | |
| 1260 | } | |
| 1261 | case DivKindFloor: | |
| 1262 | { | |
| 1263 | if (!type_entry->data.integral.is_signed) { | |
| 1264 | return LLVMBuildUDiv(g->builder, val1, val2, ""); | |
| 1265 | } | |
| 1266 | // const result = @divTrunc(a, b); | |
| 1267 | // if (result >= 0 or result * b == a) | |
| 1268 | // return result; | |
| 1269 | // else | |
| 1270 | // return result - 1; | |
| 1271 | ||
| 1272 | LLVMValueRef result = LLVMBuildSDiv(g->builder, val1, val2, ""); | |
| 1273 | LLVMValueRef is_pos = LLVMBuildICmp(g->builder, LLVMIntSGE, result, zero, ""); | |
| 1274 | LLVMValueRef orig_num = LLVMBuildNSWMul(g->builder, result, val2, ""); | |
| 1275 | LLVMValueRef orig_ok = LLVMBuildICmp(g->builder, LLVMIntEQ, orig_num, val1, ""); | |
| 1276 | LLVMValueRef ok_bit = LLVMBuildOr(g->builder, orig_ok, is_pos, ""); | |
| 1277 | LLVMValueRef one = LLVMConstInt(type_entry->type_ref, 1, true); | |
| 1278 | LLVMValueRef result_minus_1 = LLVMBuildNSWSub(g->builder, result, one, ""); | |
| 1279 | return LLVMBuildSelect(g->builder, ok_bit, result, result_minus_1, ""); | |
| 1280 | } | |
| 1180 | 1281 | } |
| 1282 | zig_unreachable(); | |
| 1181 | 1283 | } |
| 1182 | 1284 | |
| 1285 | enum RemKind { | |
| 1286 | RemKindRem, | |
| 1287 | RemKindMod, | |
| 1288 | }; | |
| 1289 | ||
| 1183 | 1290 | static 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 | { |
| 1186 | ||
| 1293 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); | |
| 1187 | 1294 | if (want_debug_safety) { |
| 1188 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); | |
| 1189 | 1295 | LLVMValueRef is_zero_bit; |
| 1190 | 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 | 1299 | } else if (type_entry->id == TypeTableEntryIdFloat) { |
| 1193 | 1300 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 1194 | 1301 | } else { |
| ... | ... | @@ -1202,30 +1309,30 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val |
| 1202 | 1309 | gen_debug_safety_crash(g, PanicMsgIdRemainderDivisionByZero); |
| 1203 | 1310 | |
| 1204 | 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 | } |
| 1222 | 1313 | |
| 1223 | 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 | 1324 | } else { |
| 1226 | 1325 | assert(type_entry->id == TypeTableEntryIdInt); |
| 1227 | 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 | 1336 | } else { |
| 1230 | 1337 | return LLVMBuildURem(g->builder, val1, val2, ""); |
| 1231 | 1338 | } |
| ... | ... | @@ -1252,6 +1359,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1252 | 1359 | case IrBinOpInvalid: |
| 1253 | 1360 | case IrBinOpArrayCat: |
| 1254 | 1361 | case IrBinOpArrayMult: |
| 1362 | case IrBinOpRemUnspecified: | |
| 1255 | 1363 | zig_unreachable(); |
| 1256 | 1364 | case IrBinOpBoolOr: |
| 1257 | 1365 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); |
| ... | ... | @@ -1367,10 +1475,18 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1367 | 1475 | } else { |
| 1368 | 1476 | zig_unreachable(); |
| 1369 | 1477 | } |
| 1370 | case IrBinOpDiv: | |
| 1371 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, false); | |
| 1372 | case IrBinOpRem: | |
| 1373 | return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry); | |
| 1478 | case IrBinOpDivUnspecified: | |
| 1479 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloat); | |
| 1480 | case IrBinOpDivExact: | |
| 1481 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindExact); | |
| 1482 | case IrBinOpDivTrunc: | |
| 1483 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindTrunc); | |
| 1484 | case IrBinOpDivFloor: | |
| 1485 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloor); | |
| 1486 | case IrBinOpRemRem: | |
| 1487 | return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindRem); | |
| 1488 | case IrBinOpRemMod: | |
| 1489 | return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindMod); | |
| 1374 | 1490 | } |
| 1375 | 1491 | zig_unreachable(); |
| 1376 | 1492 | } |
| ... | ... | @@ -2353,14 +2469,6 @@ static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInst |
| 2353 | 2469 | return nullptr; |
| 2354 | 2470 | } |
| 2355 | 2471 | |
| 2356 | static 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 | ||
| 2364 | 2472 | static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) { |
| 2365 | 2473 | LLVMValueRef target_val = ir_llvm_value(g, instruction->target); |
| 2366 | 2474 | TypeTableEntry *dest_type = instruction->base.value.type; |
| ... | ... | @@ -2965,8 +3073,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2965 | 3073 | return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction); |
| 2966 | 3074 | case IrInstructionIdFence: |
| 2967 | 3075 | return ir_render_fence(g, executable, (IrInstructionFence *)instruction); |
| 2968 | case IrInstructionIdDivExact: | |
| 2969 | return ir_render_div_exact(g, executable, (IrInstructionDivExact *)instruction); | |
| 2970 | 3076 | case IrInstructionIdTruncate: |
| 2971 | 3077 | return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction); |
| 2972 | 3078 | case IrInstructionIdBoolNot: |
| ... | ... | @@ -4320,7 +4426,6 @@ static void define_builtin_fns(CodeGen *g) { |
| 4320 | 4426 | create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); |
| 4321 | 4427 | create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); |
| 4322 | 4428 | create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); |
| 4323 | create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); | |
| 4324 | 4429 | create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); |
| 4325 | 4430 | create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); |
| 4326 | 4431 | create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); |
| ... | ... | @@ -4335,6 +4440,11 @@ static void define_builtin_fns(CodeGen *g) { |
| 4335 | 4440 | create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1); |
| 4336 | 4441 | create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3); |
| 4337 | 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 | } |
| 4339 | 4449 | |
| 4340 | 4450 | static const char *bool_to_str(bool b) { |
src/error.cpp+2| ... | ... | @@ -23,6 +23,8 @@ const char *err_str(int err) { |
| 23 | 23 | case ErrorOverflow: return "overflow"; |
| 24 | 24 | case ErrorPathAlreadyExists: return "path already exists"; |
| 25 | 25 | case ErrorUnexpected: return "unexpected error"; |
| 26 | case ErrorExactDivRemainder: return "exact division had a remainder"; | |
| 27 | case ErrorNegativeDenominator: return "negative denominator"; | |
| 26 | 28 | } |
| 27 | 29 | return "(invalid error)"; |
| 28 | 30 | } |
src/error.hpp+2| ... | ... | @@ -23,6 +23,8 @@ enum Error { |
| 23 | 23 | ErrorOverflow, |
| 24 | 24 | ErrorPathAlreadyExists, |
| 25 | 25 | ErrorUnexpected, |
| 26 | ErrorExactDivRemainder, | |
| 27 | ErrorNegativeDenominator, | |
| 26 | 28 | }; |
| 27 | 29 | |
| 28 | 30 | const char *err_str(int err); |
src/ir.cpp+201-142| ... | ... | @@ -400,10 +400,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { |
| 400 | 400 | return IrInstructionIdFence; |
| 401 | 401 | } |
| 402 | 402 | |
| 403 | static constexpr IrInstructionId ir_instruction_id(IrInstructionDivExact *) { | |
| 404 | return IrInstructionIdDivExact; | |
| 405 | } | |
| 406 | ||
| 407 | 403 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) { |
| 408 | 404 | return IrInstructionIdTruncate; |
| 409 | 405 | } |
| ... | ... | @@ -1628,23 +1624,6 @@ static IrInstruction *ir_build_fence_from(IrBuilder *irb, IrInstruction *old_ins |
| 1628 | 1624 | return new_instruction; |
| 1629 | 1625 | } |
| 1630 | 1626 | |
| 1631 | static 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 | ||
| 1642 | static 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 | ||
| 1648 | 1627 | static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) { |
| 1649 | 1628 | IrInstructionTruncate *instruction = ir_build_instruction<IrInstructionTruncate>(irb, scope, source_node); |
| 1650 | 1629 | instruction->dest_type = dest_type; |
| ... | ... | @@ -2597,14 +2576,6 @@ static IrInstruction *ir_instruction_fence_get_dep(IrInstructionFence *instructi |
| 2597 | 2576 | } |
| 2598 | 2577 | } |
| 2599 | 2578 | |
| 2600 | static 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 | ||
| 2608 | 2579 | static IrInstruction *ir_instruction_truncate_get_dep(IrInstructionTruncate *instruction, size_t index) { |
| 2609 | 2580 | switch (index) { |
| 2610 | 2581 | case 0: return instruction->dest_type; |
| ... | ... | @@ -3022,8 +2993,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 3022 | 2993 | return ir_instruction_cmpxchg_get_dep((IrInstructionCmpxchg *) instruction, index); |
| 3023 | 2994 | case IrInstructionIdFence: |
| 3024 | 2995 | return ir_instruction_fence_get_dep((IrInstructionFence *) instruction, index); |
| 3025 | case IrInstructionIdDivExact: | |
| 3026 | return ir_instruction_divexact_get_dep((IrInstructionDivExact *) instruction, index); | |
| 3027 | 2996 | case IrInstructionIdTruncate: |
| 3028 | 2997 | return ir_instruction_truncate_get_dep((IrInstructionTruncate *) instruction, index); |
| 3029 | 2998 | case IrInstructionIdIntType: |
| ... | ... | @@ -3644,9 +3613,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) |
| 3644 | 3613 | case BinOpTypeAssignTimesWrap: |
| 3645 | 3614 | return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap); |
| 3646 | 3615 | case BinOpTypeAssignDiv: |
| 3647 | return ir_gen_assign_op(irb, scope, node, IrBinOpDiv); | |
| 3616 | return ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified); | |
| 3648 | 3617 | case BinOpTypeAssignMod: |
| 3649 | return ir_gen_assign_op(irb, scope, node, IrBinOpRem); | |
| 3618 | return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified); | |
| 3650 | 3619 | case BinOpTypeAssignPlus: |
| 3651 | 3620 | return ir_gen_assign_op(irb, scope, node, IrBinOpAdd); |
| 3652 | 3621 | case BinOpTypeAssignPlusWrap: |
| ... | ... | @@ -3712,9 +3681,9 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) |
| 3712 | 3681 | case BinOpTypeMultWrap: |
| 3713 | 3682 | return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap); |
| 3714 | 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 | 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 | 3687 | case BinOpTypeArrayCat: |
| 3719 | 3688 | return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); |
| 3720 | 3689 | case BinOpTypeArrayMult: |
| ... | ... | @@ -4138,7 +4107,63 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4138 | 4107 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4139 | 4108 | return arg1_value; |
| 4140 | 4109 | |
| 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 | 4168 | case BuiltinFnIdTruncate: |
| 4144 | 4169 | { |
| ... | ... | @@ -8024,32 +8049,70 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp |
| 8024 | 8049 | return ira->codegen->builtin_types.entry_bool; |
| 8025 | 8050 | } |
| 8026 | 8051 | |
| 8052 | enum EvalBigNumSpecial { | |
| 8053 | EvalBigNumSpecialNone, | |
| 8054 | EvalBigNumSpecialWrapping, | |
| 8055 | EvalBigNumSpecialExact, | |
| 8056 | }; | |
| 8057 | ||
| 8027 | 8058 | static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, |
| 8028 | 8059 | ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), |
| 8029 | TypeTableEntry *type, bool wrapping_op) | |
| 8060 | TypeTableEntry *type, EvalBigNumSpecial special) | |
| 8030 | 8061 | { |
| 8031 | 8062 | bool is_int = false; |
| 8032 | 8063 | bool is_float = false; |
| 8033 | if (bignum_fn == bignum_div || bignum_fn == bignum_rem) { | |
| 8034 | if (type->id == TypeTableEntryIdInt || | |
| 8035 | type->id == TypeTableEntryIdNumLitInt) | |
| 8036 | { | |
| 8037 | is_int = true; | |
| 8038 | } else if (type->id == TypeTableEntryIdFloat || | |
| 8039 | type->id == TypeTableEntryIdNumLitFloat) | |
| 8040 | { | |
| 8041 | is_float = true; | |
| 8042 | } | |
| 8064 | if (type->id == TypeTableEntryIdInt || | |
| 8065 | type->id == TypeTableEntryIdNumLitInt) | |
| 8066 | { | |
| 8067 | is_int = true; | |
| 8068 | } else if (type->id == TypeTableEntryIdFloat || | |
| 8069 | type->id == TypeTableEntryIdNumLitFloat) | |
| 8070 | { | |
| 8071 | is_float = true; | |
| 8072 | } else { | |
| 8073 | zig_unreachable(); | |
| 8074 | } | |
| 8075 | if (bignum_fn == bignum_div || bignum_fn == bignum_rem || bignum_fn == bignum_mod || | |
| 8076 | bignum_fn == bignum_div_trunc || bignum_fn == bignum_div_floor) | |
| 8077 | { | |
| 8043 | 8078 | if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || |
| 8044 | 8079 | (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) |
| 8045 | 8080 | { |
| 8046 | 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 | } | |
| 8049 | 8112 | |
| 8050 | 8113 | bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); |
| 8051 | 8114 | if (overflow) { |
| 8052 | if (wrapping_op) { | |
| 8115 | if (special == EvalBigNumSpecialWrapping) { | |
| 8053 | 8116 | zig_panic("TODO compiler bug, implement compile-time wrapping arithmetic for >= 64 bit ints"); |
| 8054 | 8117 | } else { |
| 8055 | 8118 | return ErrorOverflow; |
| ... | ... | @@ -8059,7 +8122,7 @@ static int ir_eval_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, |
| 8059 | 8122 | if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum, |
| 8060 | 8123 | type->data.integral.bit_count, type->data.integral.is_signed)) |
| 8061 | 8124 | { |
| 8062 | if (wrapping_op) { | |
| 8125 | if (special == EvalBigNumSpecialWrapping) { | |
| 8063 | 8126 | if (type->data.integral.is_signed) { |
| 8064 | 8127 | out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1; |
| 8065 | 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 | 8156 | case IrBinOpCmpGreaterOrEq: |
| 8094 | 8157 | case IrBinOpArrayCat: |
| 8095 | 8158 | case IrBinOpArrayMult: |
| 8159 | case IrBinOpRemUnspecified: | |
| 8096 | 8160 | zig_unreachable(); |
| 8097 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 8183 | case IrBinOpMultWrap: |
| 8120 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, true); | |
| 8121 | case IrBinOpDiv: | |
| 8122 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, false); | |
| 8123 | case IrBinOpRem: | |
| 8124 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, false); | |
| 8184 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mul, canon_type, EvalBigNumSpecialWrapping); | |
| 8185 | case IrBinOpDivUnspecified: | |
| 8186 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialNone); | |
| 8187 | case IrBinOpDivTrunc: | |
| 8188 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_trunc, canon_type, EvalBigNumSpecialNone); | |
| 8189 | case IrBinOpDivFloor: | |
| 8190 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div_floor, canon_type, EvalBigNumSpecialNone); | |
| 8191 | case IrBinOpDivExact: | |
| 8192 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_div, canon_type, EvalBigNumSpecialExact); | |
| 8193 | case IrBinOpRemRem: | |
| 8194 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_rem, canon_type, EvalBigNumSpecialNone); | |
| 8195 | case IrBinOpRemMod: | |
| 8196 | return ir_eval_bignum(op1_val, op2_val, out_val, bignum_mod, canon_type, EvalBigNumSpecialNone); | |
| 8125 | 8197 | } |
| 8126 | 8198 | zig_unreachable(); |
| 8127 | 8199 | } |
| ... | ... | @@ -8135,6 +8207,31 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp |
| 8135 | 8207 | return resolved_type; |
| 8136 | 8208 | IrBinOp op_id = bin_op_instruction->op_id; |
| 8137 | 8209 | |
| 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 | 8235 | if (resolved_type->id == TypeTableEntryIdInt || |
| 8139 | 8236 | resolved_type->id == TypeTableEntryIdNumLitInt) |
| 8140 | 8237 | { |
| ... | ... | @@ -8144,8 +8241,12 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp |
| 8144 | 8241 | (op_id == IrBinOpAdd || |
| 8145 | 8242 | op_id == IrBinOpSub || |
| 8146 | 8243 | op_id == IrBinOpMult || |
| 8147 | op_id == IrBinOpDiv || | |
| 8148 | op_id == IrBinOpRem)) | |
| 8244 | op_id == IrBinOpDivUnspecified || | |
| 8245 | op_id == IrBinOpDivTrunc || | |
| 8246 | op_id == IrBinOpDivFloor || | |
| 8247 | op_id == IrBinOpDivExact || | |
| 8248 | op_id == IrBinOpRemRem || | |
| 8249 | op_id == IrBinOpRemMod)) | |
| 8149 | 8250 | { |
| 8150 | 8251 | // float |
| 8151 | 8252 | } else { |
| ... | ... | @@ -8176,20 +8277,25 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp |
| 8176 | 8277 | int err; |
| 8177 | 8278 | if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, out_val))) { |
| 8178 | 8279 | if (err == ErrorDivByZero) { |
| 8179 | ir_add_error_node(ira, bin_op_instruction->base.source_node, | |
| 8180 | buf_sprintf("division by zero is undefined")); | |
| 8280 | ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("division by zero is undefined")); | |
| 8181 | 8281 | return ira->codegen->builtin_types.entry_invalid; |
| 8182 | 8282 | } else if (err == ErrorOverflow) { |
| 8183 | ir_add_error_node(ira, bin_op_instruction->base.source_node, | |
| 8184 | buf_sprintf("operation caused overflow")); | |
| 8283 | ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow")); | |
| 8284 | return ira->codegen->builtin_types.entry_invalid; | |
| 8285 | } else if (err == ErrorExactDivRemainder) { | |
| 8286 | ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact division had a remainder")); | |
| 8287 | return ira->codegen->builtin_types.entry_invalid; | |
| 8288 | } else if (err == ErrorNegativeDenominator) { | |
| 8289 | ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("negative denominator")); | |
| 8185 | 8290 | return ira->codegen->builtin_types.entry_invalid; |
| 8291 | } else { | |
| 8292 | zig_unreachable(); | |
| 8186 | 8293 | } |
| 8187 | 8294 | return ira->codegen->builtin_types.entry_invalid; |
| 8188 | 8295 | } |
| 8189 | 8296 | |
| 8190 | 8297 | ir_num_lit_fits_in_other_type(ira, &bin_op_instruction->base, resolved_type); |
| 8191 | 8298 | return resolved_type; |
| 8192 | ||
| 8193 | 8299 | } |
| 8194 | 8300 | |
| 8195 | 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 | 8303 | return resolved_type; |
| 8198 | 8304 | } |
| 8199 | 8305 | |
| 8306 | ||
| 8200 | 8307 | static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) { |
| 8201 | 8308 | IrInstruction *op1 = instruction->op1->other; |
| 8202 | 8309 | TypeTableEntry *op1_type = op1->value.type; |
| ... | ... | @@ -8416,8 +8523,13 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi |
| 8416 | 8523 | case IrBinOpSubWrap: |
| 8417 | 8524 | case IrBinOpMult: |
| 8418 | 8525 | case IrBinOpMultWrap: |
| 8419 | case IrBinOpDiv: | |
| 8420 | case IrBinOpRem: | |
| 8526 | case IrBinOpDivUnspecified: | |
| 8527 | case IrBinOpDivTrunc: | |
| 8528 | case IrBinOpDivFloor: | |
| 8529 | case IrBinOpDivExact: | |
| 8530 | case IrBinOpRemUnspecified: | |
| 8531 | case IrBinOpRemRem: | |
| 8532 | case IrBinOpRemMod: | |
| 8421 | 8533 | return ir_analyze_bin_op_math(ira, bin_op_instruction); |
| 8422 | 8534 | case IrBinOpArrayCat: |
| 8423 | 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 | 10119 | buf_ptr(&child_type->name), buf_ptr(field_name))); |
| 10008 | 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 | 10137 | } else { |
| 10011 | 10138 | ir_add_error(ira, &field_ptr_instruction->base, |
| 10012 | 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 | 12157 | return ira->codegen->builtin_types.entry_void; |
| 12031 | 12158 | } |
| 12032 | 12159 | |
| 12033 | static 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 | ||
| 12098 | 12160 | static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) { |
| 12099 | 12161 | IrInstruction *dest_type_value = instruction->dest_type->other; |
| 12100 | 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 | 13323 | return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); |
| 13262 | 13324 | case IrInstructionIdFence: |
| 13263 | 13325 | return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); |
| 13264 | case IrInstructionIdDivExact: | |
| 13265 | return ir_analyze_instruction_div_exact(ira, (IrInstructionDivExact *)instruction); | |
| 13266 | 13326 | case IrInstructionIdTruncate: |
| 13267 | 13327 | return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction); |
| 13268 | 13328 | case IrInstructionIdIntType: |
| ... | ... | @@ -13469,7 +13529,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 13469 | 13529 | case IrInstructionIdMinValue: |
| 13470 | 13530 | case IrInstructionIdMaxValue: |
| 13471 | 13531 | case IrInstructionIdEmbedFile: |
| 13472 | case IrInstructionIdDivExact: | |
| 13473 | 13532 | case IrInstructionIdTruncate: |
| 13474 | 13533 | case IrInstructionIdIntType: |
| 13475 | 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 | 109 | return "*"; |
| 110 | 110 | case IrBinOpMultWrap: |
| 111 | 111 | return "*%"; |
| 112 | case IrBinOpDiv: | |
| 112 | case IrBinOpDivUnspecified: | |
| 113 | 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 | 121 | return "%"; |
| 122 | case IrBinOpRemRem: | |
| 123 | return "@rem"; | |
| 124 | case IrBinOpRemMod: | |
| 125 | return "@mod"; | |
| 116 | 126 | case IrBinOpArrayCat: |
| 117 | 127 | return "++"; |
| 118 | 128 | case IrBinOpArrayMult: |
| ... | ... | @@ -580,14 +590,6 @@ static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) { |
| 580 | 590 | fprintf(irp->f, ")"); |
| 581 | 591 | } |
| 582 | 592 | |
| 583 | static 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 | ||
| 591 | 593 | static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) { |
| 592 | 594 | fprintf(irp->f, "@truncate("); |
| 593 | 595 | ir_print_other_instruction(irp, instruction->dest_type); |
| ... | ... | @@ -1056,9 +1058,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1056 | 1058 | case IrInstructionIdFence: |
| 1057 | 1059 | ir_print_fence(irp, (IrInstructionFence *)instruction); |
| 1058 | 1060 | break; |
| 1059 | case IrInstructionIdDivExact: | |
| 1060 | ir_print_div_exact(irp, (IrInstructionDivExact *)instruction); | |
| 1061 | break; | |
| 1062 | 1061 | case IrInstructionIdTruncate: |
| 1063 | 1062 | ir_print_truncate(irp, (IrInstructionTruncate *)instruction); |
| 1064 | 1063 | break; |
src/link.cpp+2| ... | ... | @@ -297,6 +297,7 @@ static void construct_linker_job_elf(LinkJob *lj) { |
| 297 | 297 | lj->args.append("-lgcc"); |
| 298 | 298 | lj->args.append("-lgcc_eh"); |
| 299 | 299 | lj->args.append("-lc"); |
| 300 | lj->args.append("-lm"); | |
| 300 | 301 | lj->args.append("--end-group"); |
| 301 | 302 | } else { |
| 302 | 303 | lj->args.append("-lgcc"); |
| ... | ... | @@ -304,6 +305,7 @@ static void construct_linker_job_elf(LinkJob *lj) { |
| 304 | 305 | lj->args.append("-lgcc_s"); |
| 305 | 306 | lj->args.append("--no-as-needed"); |
| 306 | 307 | lj->args.append("-lc"); |
| 308 | lj->args.append("-lm"); | |
| 307 | 309 | lj->args.append("-lgcc"); |
| 308 | 310 | lj->args.append("--as-needed"); |
| 309 | 311 | lj->args.append("-lgcc_s"); |
std/elf.zig+3-4| ... | ... | @@ -165,9 +165,9 @@ pub const Elf = struct { |
| 165 | 165 | if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat; |
| 166 | 166 | |
| 167 | 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 | 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); | |
| 171 | 171 | |
| 172 | 172 | const stream_end = %return elf.in_stream.getEndPos(); |
| 173 | 173 | if (stream_end < end_sh or stream_end < end_ph) { |
| ... | ... | @@ -214,8 +214,7 @@ pub const Elf = struct { |
| 214 | 214 | |
| 215 | 215 | for (elf.section_headers) |*section| { |
| 216 | 216 | if (section.sh_type != SHT_NOBITS) { |
| 217 | const file_end_offset = %return math.addOverflow(u64, | |
| 218 | section.offset, section.size); | |
| 217 | const file_end_offset = %return math.add(u64, section.offset, section.size); | |
| 219 | 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 | 305 | |
| 306 | 306 | for (buf) |c| { |
| 307 | 307 | const digit = %return charToDigit(c, radix); |
| 308 | x = %return math.mulOverflow(T, x, radix); | |
| 309 | x = %return math.addOverflow(T, x, digit); | |
| 308 | x = %return math.mul(T, x, radix); | |
| 309 | x = %return math.add(T, x, digit); | |
| 310 | 310 | } |
| 311 | 311 | |
| 312 | 312 | return x; |
std/math.zig+211-28| ... | ... | @@ -1,37 +1,64 @@ |
| 1 | 1 | const assert = @import("debug.zig").assert; |
| 2 | 2 | |
| 3 | 3 | pub const Cmp = enum { |
| 4 | Less, | |
| 4 | 5 | Equal, |
| 5 | 6 | Greater, |
| 6 | Less, | |
| 7 | 7 | }; |
| 8 | 8 | |
| 9 | 9 | pub fn min(x: var, y: var) -> @typeOf(x + y) { |
| 10 | 10 | if (x < y) x else y |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | test "math.min" { | |
| 14 | assert(min(i32(-1), i32(2)) == -1); | |
| 15 | } | |
| 16 | ||
| 13 | 17 | pub fn max(x: var, y: var) -> @typeOf(x + y) { |
| 14 | 18 | if (x > y) x else y |
| 15 | 19 | } |
| 16 | 20 | |
| 21 | test "math.max" { | |
| 22 | assert(max(i32(-1), i32(2)) == 2); | |
| 23 | } | |
| 24 | ||
| 17 | 25 | error Overflow; |
| 18 | pub fn mulOverflow(comptime T: type, a: T, b: T) -> %T { | |
| 26 | pub fn mul(comptime T: type, a: T, b: T) -> %T { | |
| 19 | 27 | var answer: T = undefined; |
| 20 | 28 | if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer |
| 21 | 29 | } |
| 22 | pub fn addOverflow(comptime T: type, a: T, b: T) -> %T { | |
| 30 | ||
| 31 | error Overflow; | |
| 32 | pub fn add(comptime T: type, a: T, b: T) -> %T { | |
| 23 | 33 | var answer: T = undefined; |
| 24 | 34 | if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer |
| 25 | 35 | } |
| 26 | pub fn subOverflow(comptime T: type, a: T, b: T) -> %T { | |
| 36 | ||
| 37 | error Overflow; | |
| 38 | pub fn sub(comptime T: type, a: T, b: T) -> %T { | |
| 27 | 39 | var answer: T = undefined; |
| 28 | 40 | if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer |
| 29 | 41 | } |
| 30 | pub fn shlOverflow(comptime T: type, a: T, b: T) -> %T { | |
| 42 | ||
| 43 | error Overflow; | |
| 44 | pub fn shl(comptime T: type, a: T, b: T) -> %T { | |
| 31 | 45 | var answer: T = undefined; |
| 32 | 46 | if (@shlWithOverflow(T, a, b, &answer)) error.Overflow else answer |
| 33 | 47 | } |
| 34 | 48 | |
| 49 | test "math overflow functions" { | |
| 50 | testOverflow(); | |
| 51 | comptime testOverflow(); | |
| 52 | } | |
| 53 | ||
| 54 | fn 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 | ||
| 35 | 62 | pub fn log(comptime base: usize, value: var) -> @typeOf(value) { |
| 36 | 63 | const T = @typeOf(value); |
| 37 | 64 | if (@isInteger(T)) { |
| ... | ... | @@ -47,35 +74,191 @@ pub fn log(comptime base: usize, value: var) -> @typeOf(value) { |
| 47 | 74 | } |
| 48 | 75 | } |
| 49 | 76 | |
| 50 | /// x must be an integer or a float | |
| 51 | /// Note that this causes undefined behavior if | |
| 52 | /// @typeOf(x).is_signed and x == @minValue(@typeOf(x)). | |
| 53 | pub fn abs(x: var) -> @typeOf(x) { | |
| 77 | error Overflow; | |
| 78 | pub fn absInt(x: var) -> %@typeOf(x) { | |
| 54 | 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 | 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 | } |
| 63 | fn getReturnTypeForAbs(comptime T: type) -> type { | |
| 64 | if (@isInteger(T)) { | |
| 65 | return @IntType(false, T.bit_count); | |
| 66 | } else { | |
| 67 | return T; | |
| 68 | } | |
| 89 | ||
| 90 | test "math.absInt" { | |
| 91 | testAbsInt(); | |
| 92 | comptime testAbsInt(); | |
| 93 | } | |
| 94 | fn testAbsInt() { | |
| 95 | assert(%%absInt(i32(-10)) == 10); | |
| 96 | assert(%%absInt(i32(10)) == 10); | |
| 97 | } | |
| 98 | ||
| 99 | pub fn absFloat(x: var) -> @typeOf(x) { | |
| 100 | comptime assert(@isFloat(@typeOf(x))); | |
| 101 | return if (x < 0) -x else x; | |
| 102 | } | |
| 103 | ||
| 104 | test "math.absFloat" { | |
| 105 | testAbsFloat(); | |
| 106 | comptime testAbsFloat(); | |
| 107 | } | |
| 108 | fn testAbsFloat() { | |
| 109 | assert(absFloat(f32(-10.0)) == 10.0); | |
| 110 | assert(absFloat(f32(10.0)) == 10.0); | |
| 111 | } | |
| 112 | ||
| 113 | error DivisionByZero; | |
| 114 | error Overflow; | |
| 115 | pub 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 | ||
| 124 | test "math.divTrunc" { | |
| 125 | testDivTrunc(); | |
| 126 | comptime testDivTrunc(); | |
| 127 | } | |
| 128 | fn 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 | ||
| 138 | error DivisionByZero; | |
| 139 | error Overflow; | |
| 140 | pub 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 | ||
| 149 | test "math.divFloor" { | |
| 150 | testDivFloor(); | |
| 151 | comptime testDivFloor(); | |
| 152 | } | |
| 153 | fn 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 | ||
| 163 | error DivisionByZero; | |
| 164 | error Overflow; | |
| 165 | error UnexpectedRemainder; | |
| 166 | pub 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 | ||
| 178 | test "math.divExact" { | |
| 179 | testDivExact(); | |
| 180 | comptime testDivExact(); | |
| 69 | 181 | } |
| 182 | fn 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); | |
| 70 | 188 | |
| 71 | test "testMath" { | |
| 72 | testMathImpl(); | |
| 73 | comptime testMathImpl(); | |
| 189 | assert(%%divExact(f32, 10.0, 5.0) == 2.0); | |
| 190 | assert(%%divExact(f32, -10.0, 5.0) == -2.0); | |
| 191 | if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder); | |
| 192 | } | |
| 193 | ||
| 194 | error DivisionByZero; | |
| 195 | error NegativeDenominator; | |
| 196 | pub 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 | ||
| 205 | test "math.mod" { | |
| 206 | testMod(); | |
| 207 | comptime testMod(); | |
| 208 | } | |
| 209 | fn 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 | ||
| 221 | error DivisionByZero; | |
| 222 | error NegativeDenominator; | |
| 223 | pub 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 | ||
| 232 | test "math.rem" { | |
| 233 | testRem(); | |
| 234 | comptime testRem(); | |
| 235 | } | |
| 236 | fn 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 | ||
| 248 | fn 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 | } |
| 75 | 259 | |
| 76 | fn testMathImpl() { | |
| 77 | assert(%%mulOverflow(i32, 3, 4) == 12); | |
| 78 | assert(%%addOverflow(i32, 3, 4) == 7); | |
| 79 | assert(%%subOverflow(i32, 3, 4) == -1); | |
| 80 | assert(%%shlOverflow(i32, 0b11, 4) == 0b110000); | |
| 260 | fn floatBits(comptime T: type, x: T) -> @IntType(false, T.bit_count) { | |
| 261 | assert(@isFloat(T)); | |
| 262 | const uint = @IntType(false, T.bit_count); | |
| 263 | return *@intToPtr(&const uint, &x); | |
| 81 | 264 | } |
std/mem.zig+28-2| ... | ... | @@ -35,12 +35,12 @@ pub const Allocator = struct { |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | 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 | 39 | ([]T)(%return self.allocFn(self, byte_count)) |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 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 | 44 | ([]T)(%return self.reallocFn(self, ([]u8)(old_mem), byte_count)) |
| 45 | 45 | } |
| 46 | 46 | |
| ... | ... | @@ -333,3 +333,29 @@ fn testWriteIntImpl() { |
| 333 | 333 | assert(eql(u8, bytes, []u8{ 0x34, 0x12, 0x00, 0x00 })); |
| 334 | 334 | } |
| 335 | 335 | |
| 336 | ||
| 337 | pub 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 | ||
| 346 | test "mem.min" { | |
| 347 | assert(min(u8, "abcdefg") == 'a'); | |
| 348 | } | |
| 349 | ||
| 350 | pub 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 | ||
| 359 | test "mem.max" { | |
| 360 | assert(max(u8, "abcdefg") == 'g'); | |
| 361 | } |
std/special/builtin.zig+92| ... | ... | @@ -29,3 +29,95 @@ export fn __stack_chk_fail() { |
| 29 | 29 | } |
| 30 | 30 | @panic("stack smashing detected"); |
| 31 | 31 | } |
| 32 | ||
| 33 | export fn fmodf(x: f32, y: f32) -> f32 { generic_fmod(f32, x, y) } | |
| 34 | export fn fmod(x: f64, y: f64) -> f64 { generic_fmod(f64, x, y) } | |
| 35 | ||
| 36 | fn 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 | ||
| 110 | fn 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 | |
| 121 | fn 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 | 1 | // This file contains functions that zig depends on to coordinate between |
| 2 | // multiple .o files. The symbols are defined Weak so that multiple | |
| 2 | // multiple .o files. The symbols are defined LinkOnce so that multiple | |
| 3 | 3 | // instances of zig_rt.zig do not conflict with each other. |
| 4 | 4 | |
| 5 | 5 | const builtin = @import("builtin"); |
test/cases/math.zig+64-26| ... | ... | @@ -1,47 +1,76 @@ |
| 1 | 1 | const assert = @import("std").debug.assert; |
| 2 | 2 | |
| 3 | test "exactDivision" { | |
| 4 | assert(divExact(55, 11) == 5); | |
| 3 | test "division" { | |
| 4 | testDivision(); | |
| 5 | comptime testDivision(); | |
| 6 | } | |
| 7 | fn 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 | } | |
| 30 | fn div(comptime T: type, a: T, b: T) -> T { | |
| 31 | a / b | |
| 5 | 32 | } |
| 6 | fn divExact(a: u32, b: u32) -> u32 { | |
| 33 | fn divExact(comptime T: type, a: T, b: T) -> T { | |
| 7 | 34 | @divExact(a, b) |
| 8 | 35 | } |
| 9 | ||
| 10 | test "floatDivision" { | |
| 11 | assert(fdiv32(12.0, 3.0) == 4.0); | |
| 36 | fn divFloor(comptime T: type, a: T, b: T) -> T { | |
| 37 | @divFloor(a, b) | |
| 12 | 38 | } |
| 13 | fn fdiv32(a: f32, b: f32) -> f32 { | |
| 14 | a / b | |
| 39 | fn divTrunc(comptime T: type, a: T, b: T) -> T { | |
| 40 | @divTrunc(a, b) | |
| 15 | 41 | } |
| 16 | 42 | |
| 17 | test "overflowIntrinsics" { | |
| 43 | test "@addWithOverflow" { | |
| 18 | 44 | var result: u8 = undefined; |
| 19 | 45 | assert(@addWithOverflow(u8, 250, 100, &result)); |
| 20 | 46 | assert(!@addWithOverflow(u8, 100, 150, &result)); |
| 21 | 47 | assert(result == 250); |
| 22 | 48 | } |
| 23 | 49 | |
| 24 | test "shlWithOverflow" { | |
| 50 | // TODO test mulWithOverflow | |
| 51 | // TODO test subWithOverflow | |
| 52 | ||
| 53 | test "@shlWithOverflow" { | |
| 25 | 54 | var result: u16 = undefined; |
| 26 | 55 | assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); |
| 27 | 56 | assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); |
| 28 | 57 | assert(result == 0b1011111111111100); |
| 29 | 58 | } |
| 30 | 59 | |
| 31 | test "countLeadingZeroes" { | |
| 60 | test "@clz" { | |
| 32 | 61 | assert(@clz(u8(0b00001010)) == 4); |
| 33 | 62 | assert(@clz(u8(0b10001010)) == 0); |
| 34 | 63 | assert(@clz(u8(0b00000000)) == 8); |
| 35 | 64 | } |
| 36 | 65 | |
| 37 | test "countTrailingZeroes" { | |
| 66 | test "@ctz" { | |
| 38 | 67 | assert(@ctz(u8(0b10100000)) == 5); |
| 39 | 68 | assert(@ctz(u8(0b10001010)) == 1); |
| 40 | 69 | assert(@ctz(u8(0b00000000)) == 8); |
| 41 | 70 | } |
| 42 | 71 | |
| 43 | test "modifyOperators" { | |
| 44 | var i : i32 = 0; | |
| 72 | test "assignment operators" { | |
| 73 | var i: u32 = 0; | |
| 45 | 74 | i += 5; assert(i == 5); |
| 46 | 75 | i -= 2; assert(i == 3); |
| 47 | 76 | i *= 20; assert(i == 60); |
| ... | ... | @@ -57,6 +86,8 @@ test "modifyOperators" { |
| 57 | 86 | } |
| 58 | 87 | |
| 59 | 88 | test "threeExprInARow" { |
| 89 | testThreeExprInARow(false, true); | |
| 90 | comptime testThreeExprInARow(false, true); | |
| 60 | 91 | } |
| 61 | 92 | fn testThreeExprInARow(f: bool, t: bool) { |
| 62 | 93 | assertFalse(f or f or f); |
| ... | ... | @@ -72,13 +103,12 @@ fn testThreeExprInARow(f: bool, t: bool) { |
| 72 | 103 | assertFalse(!!false); |
| 73 | 104 | assertFalse(i32(7) != --(i32(7))); |
| 74 | 105 | } |
| 75 | ||
| 76 | 106 | fn assertFalse(b: bool) { |
| 77 | 107 | assert(!b); |
| 78 | 108 | } |
| 79 | 109 | |
| 80 | 110 | |
| 81 | test "constNumberLiteral" { | |
| 111 | test "const number literal" { | |
| 82 | 112 | const one = 1; |
| 83 | 113 | const eleven = ten + one; |
| 84 | 114 | |
| ... | ... | @@ -88,8 +118,9 @@ const ten = 10; |
| 88 | 118 | |
| 89 | 119 | |
| 90 | 120 | |
| 91 | test "unsignedWrapping" { | |
| 121 | test "unsigned wrapping" { | |
| 92 | 122 | testUnsignedWrappingEval(@maxValue(u32)); |
| 123 | comptime testUnsignedWrappingEval(@maxValue(u32)); | |
| 93 | 124 | } |
| 94 | 125 | fn testUnsignedWrappingEval(x: u32) { |
| 95 | 126 | const zero = x +% 1; |
| ... | ... | @@ -98,8 +129,9 @@ fn testUnsignedWrappingEval(x: u32) { |
| 98 | 129 | assert(orig == @maxValue(u32)); |
| 99 | 130 | } |
| 100 | 131 | |
| 101 | test "signedWrapping" { | |
| 132 | test "signed wrapping" { | |
| 102 | 133 | testSignedWrappingEval(@maxValue(i32)); |
| 134 | comptime testSignedWrappingEval(@maxValue(i32)); | |
| 103 | 135 | } |
| 104 | 136 | fn testSignedWrappingEval(x: i32) { |
| 105 | 137 | const min_val = x +% 1; |
| ... | ... | @@ -108,8 +140,9 @@ fn testSignedWrappingEval(x: i32) { |
| 108 | 140 | assert(max_val == @maxValue(i32)); |
| 109 | 141 | } |
| 110 | 142 | |
| 111 | test "negationWrapping" { | |
| 143 | test "negation wrapping" { | |
| 112 | 144 | testNegationWrappingEval(@minValue(i16)); |
| 145 | comptime testNegationWrappingEval(@minValue(i16)); | |
| 113 | 146 | } |
| 114 | 147 | fn testNegationWrappingEval(x: i16) { |
| 115 | 148 | assert(x == -32768); |
| ... | ... | @@ -117,20 +150,25 @@ fn testNegationWrappingEval(x: i16) { |
| 117 | 150 | assert(neg == -32768); |
| 118 | 151 | } |
| 119 | 152 | |
| 120 | test "shlWrapping" { | |
| 153 | test "shift left wrapping" { | |
| 121 | 154 | testShlWrappingEval(@maxValue(u16)); |
| 155 | comptime testShlWrappingEval(@maxValue(u16)); | |
| 122 | 156 | } |
| 123 | 157 | fn testShlWrappingEval(x: u16) { |
| 124 | 158 | const shifted = x <<% 1; |
| 125 | 159 | assert(shifted == 65534); |
| 126 | 160 | } |
| 127 | 161 | |
| 128 | test "unsigned64BitDivision" { | |
| 129 | const result = div(1152921504606846976, 34359738365); | |
| 162 | test "unsigned 64-bit division" { | |
| 163 | test_u64_div(); | |
| 164 | comptime test_u64_div(); | |
| 165 | } | |
| 166 | fn test_u64_div() { | |
| 167 | const result = divWithResult(1152921504606846976, 34359738365); | |
| 130 | 168 | assert(result.quotient == 33554432); |
| 131 | 169 | assert(result.remainder == 100663296); |
| 132 | 170 | } |
| 133 | fn div(a: u64, b: u64) -> DivResult { | |
| 171 | fn divWithResult(a: u64, b: u64) -> DivResult { | |
| 134 | 172 | DivResult { |
| 135 | 173 | .quotient = a / b, |
| 136 | 174 | .remainder = a % b, |
| ... | ... | @@ -141,7 +179,7 @@ const DivResult = struct { |
| 141 | 179 | remainder: u64, |
| 142 | 180 | }; |
| 143 | 181 | |
| 144 | test "binaryNot" { | |
| 182 | test "binary not" { | |
| 145 | 183 | assert(comptime {~u16(0b1010101010101010) == 0b0101010101010101}); |
| 146 | 184 | assert(comptime {~u64(2147483647) == 18446744071562067968}); |
| 147 | 185 | testBinaryNot(0b1010101010101010); |
| ... | ... | @@ -151,7 +189,7 @@ fn testBinaryNot(x: u16) { |
| 151 | 189 | assert(~x == 0b0101010101010101); |
| 152 | 190 | } |
| 153 | 191 | |
| 154 | test "smallIntAddition" { | |
| 192 | test "small int addition" { | |
| 155 | 193 | var x: @IntType(false, 2) = 0; |
| 156 | 194 | assert(x == 0); |
| 157 | 195 | |
| ... | ... | @@ -170,7 +208,7 @@ test "smallIntAddition" { |
| 170 | 208 | assert(result == 0); |
| 171 | 209 | } |
| 172 | 210 | |
| 173 | test "testFloatEquality" { | |
| 211 | test "float equality" { | |
| 174 | 212 | const x: f64 = 0.012; |
| 175 | 213 | const y: f64 = x + 1.0; |
| 176 | 214 |
test/cases/misc.zig+5| ... | ... | @@ -49,6 +49,11 @@ test "@IntType builtin" { |
| 49 | 49 | assert(!usize.is_signed); |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | test "floating point primitive bit counts" { | |
| 53 | assert(f32.bit_count == 32); | |
| 54 | assert(f64.bit_count == 64); | |
| 55 | } | |
| 56 | ||
| 52 | 57 | const u1 = @IntType(false, 1); |
| 53 | 58 | const u63 = @IntType(false, 63); |
| 54 | 59 | const i1 = @IntType(true, 1); |
test/compile_errors.zig+16-2| ... | ... | @@ -702,7 +702,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 702 | 702 | cases.add("division by zero", |
| 703 | 703 | \\const lit_int_x = 1 / 0; |
| 704 | 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 | 706 | \\const float_x = f32(1.0) / f32(0.0); |
| 707 | 707 | \\ |
| 708 | 708 | \\export fn entry1() -> usize { @sizeOf(@typeOf(lit_int_x)) } |
| ... | ... | @@ -792,7 +792,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 792 | 792 | |
| 793 | 793 | cases.add("compile time division by zero", |
| 794 | 794 | \\const y = foo(0); |
| 795 | \\fn foo(x: i32) -> i32 { | |
| 795 | \\fn foo(x: u32) -> u32 { | |
| 796 | 796 | \\ 1 / x |
| 797 | 797 | \\} |
| 798 | 798 | \\ |
| ... | ... | @@ -1709,4 +1709,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 1709 | 1709 | \\extern fn quux(usize); |
| 1710 | 1710 | , |
| 1711 | 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 | 97 | \\ if (x == 32767) return error.Whatever; |
| 98 | 98 | \\} |
| 99 | 99 | \\fn div(a: i16, b: i16) -> i16 { |
| 100 | \\ a / b | |
| 100 | \\ @divTrunc(a, b) | |
| 101 | 101 | \\} |
| 102 | 102 | ); |
| 103 | 103 | |
| ... | ... | @@ -141,7 +141,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) { |
| 141 | 141 | \\ const x = div0(999, 0); |
| 142 | 142 | \\} |
| 143 | 143 | \\fn div0(a: i32, b: i32) -> i32 { |
| 144 | \\ a / b | |
| 144 | \\ @divTrunc(a, b) | |
| 145 | 145 | \\} |
| 146 | 146 | ); |
| 147 | 147 |