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