Skip to content

Commit be0f656

Browse files
committed
fix @divFloor returning incorrect value and add __modti3
Closes #2152 See #1290
1 parent 12c4ab3 commit be0f656

File tree

7 files changed

+103
-17
lines changed

7 files changed

+103
-17
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub extern fn __mulsf3(a: f32, b: f32) f32 {
1717
}
1818

1919
fn 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

147148
fn 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

255257
fn 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

265268
fn 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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 {
7787
fn divTrunc(comptime T: type, a: T, b: T) T {
7888
return @divTrunc(a, b);
7989
}
90+
fn mod(comptime T: type, a: T, b: T) T {
91+
return @mod(a, b);
92+
}
8093

8194
test "@addWithOverflow" {
8295
var result: u8 = undefined;

0 commit comments

Comments
 (0)