| author | |
| committer | |
| log | be0f656c2130e1460a2ad48c202185c302e38c55 |
| tree | 4ff26946416e2e2aefc3ca24060612ae17178272 |
| parent | 12c4ab39275553e8bdd8e451141b60149b314aa7 |
| signature |
Closes #2152
See #12907 files changed, 103 insertions(+), 17 deletions(-)
CMakeLists.txt+2-1| ... | @@ -662,8 +662,9 @@ set(ZIG_STD_FILES | ... | @@ -662,8 +662,9 @@ set(ZIG_STD_FILES |
| 662 | "special/compiler_rt/floatuntidf.zig" | 662 | "special/compiler_rt/floatuntidf.zig" |
| 663 | "special/compiler_rt/floatuntisf.zig" | 663 | "special/compiler_rt/floatuntisf.zig" |
| 664 | "special/compiler_rt/floatuntitf.zig" | 664 | "special/compiler_rt/floatuntitf.zig" |
| 665 | "special/compiler_rt/muloti4.zig" | 665 | "special/compiler_rt/modti3.zig" |
| 666 | "special/compiler_rt/mulXf3.zig" | 666 | "special/compiler_rt/mulXf3.zig" |
| 667 | "special/compiler_rt/muloti4.zig" | ||
| 667 | "special/compiler_rt/multi3.zig" | 668 | "special/compiler_rt/multi3.zig" |
| 668 | "special/compiler_rt/negXf2.zig" | 669 | "special/compiler_rt/negXf2.zig" |
| 669 | "special/compiler_rt/popcountdi2.zig" | 670 | "special/compiler_rt/popcountdi2.zig" |
src/codegen.cpp+15-16| ... | @@ -2455,8 +2455,8 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2455,8 +2455,8 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2455 | } else { | 2455 | } else { |
| 2456 | zig_unreachable(); | 2456 | zig_unreachable(); |
| 2457 | } | 2457 | } |
| 2458 | LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); | ||
| 2459 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); | 2458 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); |
| 2459 | LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); | ||
| 2460 | LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); | 2460 | LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); |
| 2461 | 2461 | ||
| 2462 | LLVMPositionBuilderAtEnd(g->builder, div_zero_fail_block); | 2462 | LLVMPositionBuilderAtEnd(g->builder, div_zero_fail_block); |
| ... | @@ -2469,8 +2469,8 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2469,8 +2469,8 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2469 | BigInt int_min_bi = {0}; | 2469 | BigInt int_min_bi = {0}; |
| 2470 | eval_min_max_value_int(g, type_entry, &int_min_bi, false); | 2470 | eval_min_max_value_int(g, type_entry, &int_min_bi, false); |
| 2471 | LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, type_entry), &int_min_bi); | 2471 | LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, type_entry), &int_min_bi); |
| 2472 | LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); | ||
| 2473 | LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); | 2472 | LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); |
| 2473 | LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); | ||
| 2474 | LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); | 2474 | LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); |
| 2475 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); | 2475 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); |
| 2476 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); | 2476 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); |
| ... | @@ -2574,20 +2574,19 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2574,20 +2574,19 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2574 | if (!type_entry->data.integral.is_signed) { | 2574 | if (!type_entry->data.integral.is_signed) { |
| 2575 | return LLVMBuildUDiv(g->builder, val1, val2, ""); | 2575 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 2576 | } | 2576 | } |
| 2577 | // const result = @divTrunc(a, b); | 2577 | // const d = @divTrunc(a, b); |
| 2578 | // if (result >= 0 or result * b == a) | 2578 | // const r = @rem(a, b); |
| 2579 | // return result; | 2579 | // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); |
| 2580 | // else | 2580 | |
| 2581 | // return result - 1; | 2581 | LLVMValueRef div_trunc = LLVMBuildSDiv(g->builder, val1, val2, ""); |
| 2582 | 2582 | LLVMValueRef rem = LLVMBuildSRem(g->builder, val1, val2, ""); | |
| 2583 | LLVMValueRef result = LLVMBuildSDiv(g->builder, val1, val2, ""); | 2583 | LLVMValueRef rem_eq_0 = LLVMBuildICmp(g->builder, LLVMIntEQ, rem, zero, ""); |
| 2584 | LLVMValueRef is_pos = LLVMBuildICmp(g->builder, LLVMIntSGE, result, zero, ""); | 2584 | LLVMValueRef a_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val1, zero, ""); |
| 2585 | LLVMValueRef orig_num = LLVMBuildNSWMul(g->builder, result, val2, ""); | 2585 | LLVMValueRef b_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val2, zero, ""); |
| 2586 | LLVMValueRef orig_ok = LLVMBuildICmp(g->builder, LLVMIntEQ, orig_num, val1, ""); | 2586 | LLVMValueRef a_b_xor = LLVMBuildXor(g->builder, a_lt_0, b_lt_0, ""); |
| 2587 | LLVMValueRef ok_bit = LLVMBuildOr(g->builder, orig_ok, is_pos, ""); | 2587 | LLVMValueRef a_b_xor_ext = LLVMBuildZExt(g->builder, a_b_xor, LLVMTypeOf(div_trunc), ""); |
| 2588 | LLVMValueRef one = LLVMConstInt(get_llvm_type(g, type_entry), 1, true); | 2588 | LLVMValueRef d_sub_xor = LLVMBuildSub(g->builder, div_trunc, a_b_xor_ext, ""); |
| 2589 | LLVMValueRef result_minus_1 = LLVMBuildNSWSub(g->builder, result, one, ""); | 2589 | return LLVMBuildSelect(g->builder, rem_eq_0, div_trunc, d_sub_xor, ""); |
| 2590 | return LLVMBuildSelect(g->builder, ok_bit, result, result_minus_1, ""); | ||
| 2591 | } | 2590 | } |
| 2592 | } | 2591 | } |
| 2593 | zig_unreachable(); | 2592 | zig_unreachable(); |
std/special/compiler_rt.zig+2| ... | @@ -159,6 +159,7 @@ comptime { | ... | @@ -159,6 +159,7 @@ comptime { |
| 159 | @export("___chkstk_ms", ___chkstk_ms, linkage); | 159 | @export("___chkstk_ms", ___chkstk_ms, linkage); |
| 160 | } | 160 | } |
| 161 | @export("__divti3", @import("compiler_rt/divti3.zig").__divti3_windows_x86_64, linkage); | 161 | @export("__divti3", @import("compiler_rt/divti3.zig").__divti3_windows_x86_64, linkage); |
| 162 | @export("__modti3", @import("compiler_rt/modti3.zig").__modti3_windows_x86_64, linkage); | ||
| 162 | @export("__multi3", @import("compiler_rt/multi3.zig").__multi3_windows_x86_64, linkage); | 163 | @export("__multi3", @import("compiler_rt/multi3.zig").__multi3_windows_x86_64, linkage); |
| 163 | @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4_windows_x86_64, linkage); | 164 | @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4_windows_x86_64, linkage); |
| 164 | @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64, linkage); | 165 | @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64, linkage); |
| ... | @@ -169,6 +170,7 @@ comptime { | ... | @@ -169,6 +170,7 @@ comptime { |
| 169 | } | 170 | } |
| 170 | } else { | 171 | } else { |
| 171 | @export("__divti3", @import("compiler_rt/divti3.zig").__divti3, linkage); | 172 | @export("__divti3", @import("compiler_rt/divti3.zig").__divti3, linkage); |
| 173 | @export("__modti3", @import("compiler_rt/modti3.zig").__modti3, linkage); | ||
| 172 | @export("__multi3", @import("compiler_rt/multi3.zig").__multi3, linkage); | 174 | @export("__multi3", @import("compiler_rt/multi3.zig").__multi3, linkage); |
| 173 | @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4, linkage); | 175 | @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4, linkage); |
| 174 | @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3, linkage); | 176 | @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3, linkage); |
std/special/compiler_rt/modti3.zig created+30| ... | @@ -0,0 +1,30 @@ | ||
| 1 | // Ported from: | ||
| 2 | // | ||
| 3 | // https://github.com/llvm/llvm-project/blob/2ffb1b0413efa9a24eb3c49e710e36f92e2cb50b/compiler-rt/lib/builtins/modti3.c | ||
| 4 | |||
| 5 | const udivmod = @import("udivmod.zig").udivmod; | ||
| 6 | const builtin = @import("builtin"); | ||
| 7 | const compiler_rt = @import("../compiler_rt.zig"); | ||
| 8 | |||
| 9 | pub extern fn __modti3(a: i128, b: i128) i128 { | ||
| 10 | @setRuntimeSafety(builtin.is_test); | ||
| 11 | |||
| 12 | const s_a = a >> (i128.bit_count - 1); // s = a < 0 ? -1 : 0 | ||
| 13 | const s_b = b >> (i128.bit_count - 1); // s = b < 0 ? -1 : 0 | ||
| 14 | |||
| 15 | const an = (a ^ s_a) -% s_a; // negate if s == -1 | ||
| 16 | const bn = (b ^ s_b) -% s_b; // negate if s == -1 | ||
| 17 | |||
| 18 | var r: u128 = undefined; | ||
| 19 | _ = udivmod(u128, @bitCast(u128, an), @bitCast(u128, bn), &r); | ||
| 20 | return (@bitCast(i128, r) ^ s_a) -% s_a; // negate if s == -1 | ||
| 21 | } | ||
| 22 | |||
| 23 | pub extern fn __modti3_windows_x86_64(a: *const i128, b: *const i128) void { | ||
| 24 | @setRuntimeSafety(builtin.is_test); | ||
| 25 | compiler_rt.setXmm0(i128, __modti3(a.*, b.*)); | ||
| 26 | } | ||
| 27 | |||
| 28 | test "import modti3" { | ||
| 29 | _ = @import("modti3_test.zig"); | ||
| 30 | } | ||
std/special/compiler_rt/modti3_test.zig created+37| ... | @@ -0,0 +1,37 @@ | ||
| 1 | const __modti3 = @import("modti3.zig").__modti3; | ||
| 2 | const testing = @import("std").testing; | ||
| 3 | |||
| 4 | fn test__modti3(a: i128, b: i128, expected: i128) void { | ||
| 5 | const x = __modti3(a, b); | ||
| 6 | testing.expect(x == expected); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "modti3" { | ||
| 10 | test__modti3(0, 1, 0); | ||
| 11 | test__modti3(0, -1, 0); | ||
| 12 | test__modti3(5, 3, 2); | ||
| 13 | test__modti3(5, -3, 2); | ||
| 14 | test__modti3(-5, 3, -2); | ||
| 15 | test__modti3(-5, -3, -2); | ||
| 16 | |||
| 17 | test__modti3(0x8000000000000000, 1, 0x0); | ||
| 18 | test__modti3(0x8000000000000000, -1, 0x0); | ||
| 19 | test__modti3(0x8000000000000000, 2, 0x0); | ||
| 20 | test__modti3(0x8000000000000000, -2, 0x0); | ||
| 21 | test__modti3(0x8000000000000000, 3, 2); | ||
| 22 | test__modti3(0x8000000000000000, -3, 2); | ||
| 23 | |||
| 24 | test__modti3(make_ti(0x8000000000000000, 0), 1, 0x0); | ||
| 25 | test__modti3(make_ti(0x8000000000000000, 0), -1, 0x0); | ||
| 26 | test__modti3(make_ti(0x8000000000000000, 0), 2, 0x0); | ||
| 27 | test__modti3(make_ti(0x8000000000000000, 0), -2, 0x0); | ||
| 28 | test__modti3(make_ti(0x8000000000000000, 0), 3, -2); | ||
| 29 | test__modti3(make_ti(0x8000000000000000, 0), -3, -2); | ||
| 30 | } | ||
| 31 | |||
| 32 | fn make_ti(high: u64, low: u64) i128 { | ||
| 33 | var result: u128 = high; | ||
| 34 | result <<= 64; | ||
| 35 | result |= low; | ||
| 36 | return @bitCast(i128, result); | ||
| 37 | } | ||
std/special/compiler_rt/mulXf3.zig+4| ... | @@ -17,6 +17,7 @@ pub extern fn __mulsf3(a: f32, b: f32) f32 { | ... | @@ -17,6 +17,7 @@ pub extern fn __mulsf3(a: f32, b: f32) f32 { |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | fn mulXf3(comptime T: type, a: T, b: T) T { | 19 | fn mulXf3(comptime T: type, a: T, b: T) T { |
| 20 | @setRuntimeSafety(builtin.is_test); | ||
| 20 | const Z = @IntType(false, T.bit_count); | 21 | const Z = @IntType(false, T.bit_count); |
| 21 | 22 | ||
| 22 | const typeWidth = T.bit_count; | 23 | const typeWidth = T.bit_count; |
| ... | @@ -145,6 +146,7 @@ fn mulXf3(comptime T: type, a: T, b: T) T { | ... | @@ -145,6 +146,7 @@ fn mulXf3(comptime T: type, a: T, b: T) T { |
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { | 148 | fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { |
| 149 | @setRuntimeSafety(builtin.is_test); | ||
| 148 | switch (Z) { | 150 | switch (Z) { |
| 149 | u32 => { | 151 | u32 => { |
| 150 | // 32x32 --> 64 bit multiply | 152 | // 32x32 --> 64 bit multiply |
| ... | @@ -253,6 +255,7 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { | ... | @@ -253,6 +255,7 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { |
| 253 | } | 255 | } |
| 254 | 256 | ||
| 255 | fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { | 257 | fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { |
| 258 | @setRuntimeSafety(builtin.is_test); | ||
| 256 | const Z = @IntType(false, T.bit_count); | 259 | const Z = @IntType(false, T.bit_count); |
| 257 | const significandBits = std.math.floatMantissaBits(T); | 260 | const significandBits = std.math.floatMantissaBits(T); |
| 258 | const implicitBit = Z(1) << significandBits; | 261 | const implicitBit = Z(1) << significandBits; |
| ... | @@ -263,6 +266,7 @@ fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { | ... | @@ -263,6 +266,7 @@ fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 { |
| 263 | } | 266 | } |
| 264 | 267 | ||
| 265 | fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void { | 268 | fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void { |
| 269 | @setRuntimeSafety(builtin.is_test); | ||
| 266 | const typeWidth = Z.bit_count; | 270 | const typeWidth = Z.bit_count; |
| 267 | const S = std.math.Log2Int(Z); | 271 | const S = std.math.Log2Int(Z); |
| 268 | if (count < typeWidth) { | 272 | if (count < typeWidth) { |
test/stage1/behavior/math.zig+13| ... | @@ -31,6 +31,9 @@ fn testDivision() void { | ... | @@ -31,6 +31,9 @@ fn testDivision() void { |
| 31 | expect(divFloor(i32, 0, -0x80000000) == 0); | 31 | expect(divFloor(i32, 0, -0x80000000) == 0); |
| 32 | expect(divFloor(i32, -0x40000001, 0x40000000) == -2); | 32 | expect(divFloor(i32, -0x40000001, 0x40000000) == -2); |
| 33 | expect(divFloor(i32, -0x80000000, 1) == -0x80000000); | 33 | expect(divFloor(i32, -0x80000000, 1) == -0x80000000); |
| 34 | expect(divFloor(i32, 10, 12) == 0); | ||
| 35 | expect(divFloor(i32, -14, 12) == -2); | ||
| 36 | expect(divFloor(i32, -2, 12) == -1); | ||
| 34 | 37 | ||
| 35 | expect(divTrunc(i32, 5, 3) == 1); | 38 | expect(divTrunc(i32, 5, 3) == 1); |
| 36 | expect(divTrunc(i32, -5, 3) == -1); | 39 | expect(divTrunc(i32, -5, 3) == -1); |
| ... | @@ -40,6 +43,13 @@ fn testDivision() void { | ... | @@ -40,6 +43,13 @@ fn testDivision() void { |
| 40 | expect(divTrunc(f32, -5.0, 3.0) == -1.0); | 43 | expect(divTrunc(f32, -5.0, 3.0) == -1.0); |
| 41 | expect(divTrunc(f64, 5.0, 3.0) == 1.0); | 44 | expect(divTrunc(f64, 5.0, 3.0) == 1.0); |
| 42 | expect(divTrunc(f64, -5.0, 3.0) == -1.0); | 45 | expect(divTrunc(f64, -5.0, 3.0) == -1.0); |
| 46 | expect(divTrunc(i32, 10, 12) == 0); | ||
| 47 | expect(divTrunc(i32, -14, 12) == -1); | ||
| 48 | expect(divTrunc(i32, -2, 12) == 0); | ||
| 49 | |||
| 50 | expect(mod(i32, 10, 12) == 10); | ||
| 51 | expect(mod(i32, -14, 12) == 10); | ||
| 52 | expect(mod(i32, -2, 12) == 10); | ||
| 43 | 53 | ||
| 44 | comptime { | 54 | comptime { |
| 45 | expect( | 55 | expect( |
| ... | @@ -77,6 +87,9 @@ fn divFloor(comptime T: type, a: T, b: T) T { | ... | @@ -77,6 +87,9 @@ fn divFloor(comptime T: type, a: T, b: T) T { |
| 77 | fn divTrunc(comptime T: type, a: T, b: T) T { | 87 | fn divTrunc(comptime T: type, a: T, b: T) T { |
| 78 | return @divTrunc(a, b); | 88 | return @divTrunc(a, b); |
| 79 | } | 89 | } |
| 90 | fn mod(comptime T: type, a: T, b: T) T { | ||
| 91 | return @mod(a, b); | ||
| 92 | } | ||
| 80 | 93 | ||
| 81 | test "@addWithOverflow" { | 94 | test "@addWithOverflow" { |
| 82 | var result: u8 = undefined; | 95 | var result: u8 = undefined; |