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...@@ -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);
24612461
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 // else2580
2581 // return result - 1;2581 LLVMValueRef div_trunc = LLVMBuildSDiv(g->builder, val1, val2, "");
25822582 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
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 {...@@ -17,6 +17,7 @@ pub extern fn __mulsf3(a: f32, b: f32) f32 {
17}17}
1818
19fn mulXf3(comptime T: type, a: T, b: T) T {19fn 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);
2122
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}
146147
147fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void {148fn 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 multiply152 // 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}
254256
255fn normalize(comptime T: type, significand: *@IntType(false, T.bit_count)) i32 {257fn 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}
264267
265fn wideRightShiftWithSticky(comptime Z: type, hi: *Z, lo: *Z, count: u32) void {268fn 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);
3437
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);
4353
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 {
77fn divTrunc(comptime T: type, a: T, b: T) T {87fn divTrunc(comptime T: type, a: T, b: T) T {
78 return @divTrunc(a, b);88 return @divTrunc(a, b);
79}89}
90fn mod(comptime T: type, a: T, b: T) T {
91 return @mod(a, b);
92}
8093
81test "@addWithOverflow" {94test "@addWithOverflow" {
82 var result: u8 = undefined;95 var result: u8 = undefined;