authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-04-04 15:44:19-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-04-04 15:45:37-04:00
logbe0f656c2130e1460a2ad48c202185c302e38c55
tree4ff26946416e2e2aefc3ca24060612ae17178272
parent12c4ab39275553e8bdd8e451141b60149b314aa7
signaturelock-open Commit is signed but in an unrecognized format.

fix `@divFloor` returning incorrect value and add `__modti3`

Closes #2152 See #1290

7 files changed, 103 insertions(+), 17 deletions(-)

CMakeLists.txt+2-1
......@@ -662,8 +662,9 @@ set(ZIG_STD_FILES
662662 "special/compiler_rt/floatuntidf.zig"
663663 "special/compiler_rt/floatuntisf.zig"
664664 "special/compiler_rt/floatuntitf.zig"
665 "special/compiler_rt/muloti4.zig"
665 "special/compiler_rt/modti3.zig"
666666 "special/compiler_rt/mulXf3.zig"
667 "special/compiler_rt/muloti4.zig"
667668 "special/compiler_rt/multi3.zig"
668669 "special/compiler_rt/negXf2.zig"
669670 "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
24552455 } else {
24562456 zig_unreachable();
24572457 }
2458 LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk");
24592458 LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail");
2459 LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk");
24602460 LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block);
24612461
24622462 LLVMPositionBuilderAtEnd(g->builder, div_zero_fail_block);
......@@ -2469,8 +2469,8 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
24692469 BigInt int_min_bi = {0};
24702470 eval_min_max_value_int(g, type_entry, &int_min_bi, false);
24712471 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");
24732472 LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail");
2473 LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk");
24742474 LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, "");
24752475 LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, "");
24762476 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
25742574 if (!type_entry->data.integral.is_signed) {
25752575 return LLVMBuildUDiv(g->builder, val1, val2, "");
25762576 }
2577 // const result = @divTrunc(a, b);
2578 // if (result >= 0 or result * b == a)
2579 // return result;
2580 // else
2581 // return result - 1;
2582
2583 LLVMValueRef result = LLVMBuildSDiv(g->builder, val1, val2, "");
2584 LLVMValueRef is_pos = LLVMBuildICmp(g->builder, LLVMIntSGE, result, zero, "");
2585 LLVMValueRef orig_num = LLVMBuildNSWMul(g->builder, result, val2, "");
2586 LLVMValueRef orig_ok = LLVMBuildICmp(g->builder, LLVMIntEQ, orig_num, val1, "");
2587 LLVMValueRef ok_bit = LLVMBuildOr(g->builder, orig_ok, is_pos, "");
2588 LLVMValueRef one = LLVMConstInt(get_llvm_type(g, type_entry), 1, true);
2589 LLVMValueRef result_minus_1 = LLVMBuildNSWSub(g->builder, result, one, "");
2590 return LLVMBuildSelect(g->builder, ok_bit, result, result_minus_1, "");
2577 // const d = @divTrunc(a, b);
2578 // const r = @rem(a, b);
2579 // return if (r == 0) d else d - ((a < 0) ^ (b < 0));
2580
2581 LLVMValueRef div_trunc = LLVMBuildSDiv(g->builder, val1, val2, "");
2582 LLVMValueRef rem = LLVMBuildSRem(g->builder, val1, val2, "");
2583 LLVMValueRef rem_eq_0 = LLVMBuildICmp(g->builder, LLVMIntEQ, rem, zero, "");
2584 LLVMValueRef a_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val1, zero, "");
2585 LLVMValueRef b_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val2, zero, "");
2586 LLVMValueRef a_b_xor = LLVMBuildXor(g->builder, a_lt_0, b_lt_0, "");
2587 LLVMValueRef a_b_xor_ext = LLVMBuildZExt(g->builder, a_b_xor, LLVMTypeOf(div_trunc), "");
2588 LLVMValueRef d_sub_xor = LLVMBuildSub(g->builder, div_trunc, a_b_xor_ext, "");
2589 return LLVMBuildSelect(g->builder, rem_eq_0, div_trunc, d_sub_xor, "");
25912590 }
25922591 }
25932592 zig_unreachable();
std/special/compiler_rt.zig+2
......@@ -159,6 +159,7 @@ comptime {
159159 @export("___chkstk_ms", ___chkstk_ms, linkage);
160160 }
161161 @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);
162163 @export("__multi3", @import("compiler_rt/multi3.zig").__multi3_windows_x86_64, linkage);
163164 @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4_windows_x86_64, linkage);
164165 @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64, linkage);
......@@ -169,6 +170,7 @@ comptime {
169170 }
170171 } else {
171172 @export("__divti3", @import("compiler_rt/divti3.zig").__divti3, linkage);
173 @export("__modti3", @import("compiler_rt/modti3.zig").__modti3, linkage);
172174 @export("__multi3", @import("compiler_rt/multi3.zig").__multi3, linkage);
173175 @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4, linkage);
174176 @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
5const udivmod = @import("udivmod.zig").udivmod;
6const builtin = @import("builtin");
7const compiler_rt = @import("../compiler_rt.zig");
8
9pub 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
23pub 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
28test "import modti3" {
29 _ = @import("modti3_test.zig");
30}
std/special/compiler_rt/modti3_test.zig created+37
......@@ -0,0 +1,37 @@
1const __modti3 = @import("modti3.zig").__modti3;
2const testing = @import("std").testing;
3
4fn test__modti3(a: i128, b: i128, expected: i128) void {
5 const x = __modti3(a, b);
6 testing.expect(x == expected);
7}
8
9test "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
32fn 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 {
1717}
1818
1919fn mulXf3(comptime T: type, a: T, b: T) T {
20 @setRuntimeSafety(builtin.is_test);
2021 const Z = @IntType(false, T.bit_count);
2122
2223 const typeWidth = T.bit_count;
......@@ -145,6 +146,7 @@ fn mulXf3(comptime T: type, a: T, b: T) T {
145146}
146147
147148fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
149 @setRuntimeSafety(builtin.is_test);
148150 switch (Z) {
149151 u32 => {
150152 // 32x32 --> 64 bit multiply
......@@ -253,6 +255,7 @@ fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {
253255}
254256
255257fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 {
258 @setRuntimeSafety(builtin.is_test);
256259 const Z = @IntType(false, T.bit_count);
257260 const significandBits = std.math.floatMantissaBits(T);
258261 const implicitBit = Z(1) << significandBits;
......@@ -263,6 +266,7 @@ fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 {
263266}
264267
265268fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void {
269 @setRuntimeSafety(builtin.is_test);
266270 const typeWidth = Z.bit_count;
267271 const S = std.math.Log2Int(Z);
268272 if (count < typeWidth) {
test/stage1/behavior/math.zig+13
......@@ -31,6 +31,9 @@ fn testDivision() void {
3131 expect(divFloor(i32, 0, -0x80000000) == 0);
3232 expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
3333 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);
3437
3538 expect(divTrunc(i32, 5, 3) == 1);
3639 expect(divTrunc(i32, -5, 3) == -1);
......@@ -40,6 +43,13 @@ fn testDivision() void {
4043 expect(divTrunc(f32, -5.0, 3.0) == -1.0);
4144 expect(divTrunc(f64, 5.0, 3.0) == 1.0);
4245 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);
4353
4454 comptime {
4555 expect(
......@@ -77,6 +87,9 @@ fn divFloor(comptime T: type, a: T, b: T) T {
7787fn divTrunc(comptime T: type, a: T, b: T) T {
7888 return @divTrunc(a, b);
7989}
90fn mod(comptime T: type, a: T, b: T) T {
91 return @mod(a, b);
92}
8093
8194test "@addWithOverflow" {
8295 var result: u8 = undefined;