authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-25 17:15:48-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-26 21:45:57-04:00
log5e83441096ca77b379be55ad2c197f70ce374eac
treee7d6616bb07da1bf53af1bafe1d735433af198e1
parentda06269d706087207fc94c2039006f2f2d18aa2e

x86_64: implement `@divFloor` and `@mod` for `i128`


8 files changed, 147 insertions(+), 87 deletions(-)

lib/std/fs.zig+1-3
......@@ -3248,9 +3248,7 @@ test {
32483248 _ = &makeDirAbsolute;
32493249 _ = &makeDirAbsoluteZ;
32503250 _ = &copyFileAbsolute;
3251 if (builtin.zig_backend != .stage2_x86_64) {
3252 _ = &updateFileAbsolute;
3253 }
3251 _ = &updateFileAbsolute;
32543252 }
32553253 _ = &Dir.copyFile;
32563254 _ = @import("fs/test.zig");
lib/std/fs/test.zig-4
......@@ -1460,8 +1460,6 @@ test "walker without fully iterating" {
14601460test ". and .. in fs.Dir functions" {
14611461 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
14621462
1463 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1464
14651463 if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) {
14661464 // https://github.com/ziglang/zig/issues/17134
14671465 return error.SkipZigTest;
......@@ -1502,8 +1500,6 @@ test ". and .. in fs.Dir functions" {
15021500test ". and .. in absolute functions" {
15031501 if (builtin.os.tag == .wasi) return error.SkipZigTest;
15041502
1505 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1506
15071503 var tmp = tmpDir(.{});
15081504 defer tmp.cleanup();
15091505
lib/std/io/test.zig-2
......@@ -161,8 +161,6 @@ test "setEndPos" {
161161}
162162
163163test "updateTimes" {
164 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
165
166164 var tmp = tmpDir(.{});
167165 defer tmp.cleanup();
168166
lib/std/math/big/int_test.zig-20
......@@ -1182,8 +1182,6 @@ test "big.int div single-single with rem" {
11821182}
11831183
11841184test "big.int div multi-single no rem" {
1185 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1186
11871185 var op1: u128 = 0xffffeeeeddddcccc;
11881186 var op2: u128 = 34;
11891187
......@@ -1203,8 +1201,6 @@ test "big.int div multi-single no rem" {
12031201}
12041202
12051203test "big.int div multi-single with rem" {
1206 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1207
12081204 var op1: u128 = 0xffffeeeeddddcccf;
12091205 var op2: u128 = 34;
12101206
......@@ -1224,8 +1220,6 @@ test "big.int div multi-single with rem" {
12241220}
12251221
12261222test "big.int div multi>2-single" {
1227 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1228
12291223 var op1: u128 = 0xfefefefefefefefefefefefefefefefe;
12301224 var op2: u128 = 0xefab8;
12311225
......@@ -1405,8 +1399,6 @@ test "big.int div trunc single-single -/-" {
14051399}
14061400
14071401test "big.int divTrunc #15535" {
1408 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1409
14101402 var one = try Managed.initSet(testing.allocator, 1);
14111403 defer one.deinit();
14121404 var x = try Managed.initSet(testing.allocator, std.math.pow(u128, 2, 64));
......@@ -1949,8 +1941,6 @@ test "big.int truncate multi to multi signed" {
19491941}
19501942
19511943test "big.int truncate negative multi to single" {
1952 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1953
19541944 var a = try Managed.initSet(testing.allocator, -@as(SignedDoubleLimb, maxInt(Limb) + 1));
19551945 defer a.deinit();
19561946
......@@ -2362,8 +2352,6 @@ test "big.int bitwise xor simple" {
23622352}
23632353
23642354test "big.int bitwise xor multi-limb" {
2365 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2366
23672355 var x: DoubleLimb = maxInt(Limb) + 1;
23682356 var y: DoubleLimb = maxInt(Limb);
23692357 var a = try Managed.initSet(testing.allocator, x);
......@@ -2538,8 +2526,6 @@ test "big.int var args" {
25382526}
25392527
25402528test "big.int gcd non-one small" {
2541 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2542
25432529 var a = try Managed.initSet(testing.allocator, 17);
25442530 defer a.deinit();
25452531 var b = try Managed.initSet(testing.allocator, 97);
......@@ -2553,8 +2539,6 @@ test "big.int gcd non-one small" {
25532539}
25542540
25552541test "big.int gcd non-one medium" {
2556 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2557
25582542 var a = try Managed.initSet(testing.allocator, 4864);
25592543 defer a.deinit();
25602544 var b = try Managed.initSet(testing.allocator, 3458);
......@@ -2568,8 +2552,6 @@ test "big.int gcd non-one medium" {
25682552}
25692553
25702554test "big.int gcd non-one large" {
2571 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2572
25732555 var a = try Managed.initSet(testing.allocator, 0xffffffffffffffff);
25742556 defer a.deinit();
25752557 var b = try Managed.initSet(testing.allocator, 0xffffffffffffffff7777);
......@@ -2602,8 +2584,6 @@ test "big.int gcd large multi-limb result" {
26022584}
26032585
26042586test "big.int gcd one large" {
2605 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2606
26072587 var a = try Managed.initSet(testing.allocator, 1897056385327307);
26082588 defer a.deinit();
26092589 var b = try Managed.initSet(testing.allocator, 2251799813685248);
lib/std/math/big/rational.zig-20
......@@ -514,8 +514,6 @@ test "big.rational extractLowBits" {
514514}
515515
516516test "big.rational set" {
517 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
518
519517 var a = try Rational.init(testing.allocator);
520518 defer a.deinit();
521519
......@@ -615,8 +613,6 @@ test "big.rational set/to Float round-trip" {
615613}
616614
617615test "big.rational copy" {
618 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
619
620616 var a = try Rational.init(testing.allocator);
621617 defer a.deinit();
622618
......@@ -681,8 +677,6 @@ test "big.rational abs" {
681677}
682678
683679test "big.rational swap" {
684 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
685
686680 var a = try Rational.init(testing.allocator);
687681 defer a.deinit();
688682 var b = try Rational.init(testing.allocator);
......@@ -707,8 +701,6 @@ test "big.rational swap" {
707701}
708702
709703test "big.rational order" {
710 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
711
712704 var a = try Rational.init(testing.allocator);
713705 defer a.deinit();
714706 var b = try Rational.init(testing.allocator);
......@@ -724,8 +716,6 @@ test "big.rational order" {
724716}
725717
726718test "big.rational order/orderAbs with negative" {
727 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
728
729719 var a = try Rational.init(testing.allocator);
730720 defer a.deinit();
731721 var b = try Rational.init(testing.allocator);
......@@ -738,8 +728,6 @@ test "big.rational order/orderAbs with negative" {
738728}
739729
740730test "big.rational add single-limb" {
741 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
742
743731 var a = try Rational.init(testing.allocator);
744732 defer a.deinit();
745733 var b = try Rational.init(testing.allocator);
......@@ -755,8 +743,6 @@ test "big.rational add single-limb" {
755743}
756744
757745test "big.rational add" {
758 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
759
760746 var a = try Rational.init(testing.allocator);
761747 defer a.deinit();
762748 var b = try Rational.init(testing.allocator);
......@@ -773,8 +759,6 @@ test "big.rational add" {
773759}
774760
775761test "big.rational sub" {
776 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
777
778762 var a = try Rational.init(testing.allocator);
779763 defer a.deinit();
780764 var b = try Rational.init(testing.allocator);
......@@ -791,8 +775,6 @@ test "big.rational sub" {
791775}
792776
793777test "big.rational mul" {
794 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
795
796778 var a = try Rational.init(testing.allocator);
797779 defer a.deinit();
798780 var b = try Rational.init(testing.allocator);
......@@ -809,8 +791,6 @@ test "big.rational mul" {
809791}
810792
811793test "big.rational div" {
812 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
813
814794 {
815795 var a = try Rational.init(testing.allocator);
816796 defer a.deinit();
lib/std/os/linux/io_uring.zig-2
......@@ -2204,8 +2204,6 @@ test "sendmsg/recvmsg" {
22042204test "timeout (after a relative time)" {
22052205 if (builtin.os.tag != .linux) return error.SkipZigTest;
22062206
2207 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2208
22092207 var ring = IO_Uring.init(1, 0) catch |err| switch (err) {
22102208 error.SystemOutdated => return error.SkipZigTest,
22112209 error.PermissionDenied => return error.SkipZigTest,
lib/std/time.zig-2
......@@ -122,8 +122,6 @@ pub fn nanoTimestamp() i128 {
122122}
123123
124124test "timestamp" {
125 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
126
127125 const margin = ns_per_ms * 50;
128126
129127 const time_0 = milliTimestamp();
src/arch/x86_64/CodeGen.zig+146-34
......@@ -3221,6 +3221,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
32213221 .Float, .Vector => break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs),
32223222 else => {},
32233223 }
3224 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(mod));
32243225
32253226 const dst_info = dst_ty.intInfo(mod);
32263227 const src_ty = try mod.intType(dst_info.signedness, switch (tag) {
......@@ -3232,12 +3233,153 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
32323233 ),
32333234 .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_info.bits,
32343235 });
3236 const src_abi_size: u32 = @intCast(src_ty.abiSize(mod));
3237
3238 if (dst_abi_size == 16 and src_abi_size == 16) switch (tag) {
3239 else => unreachable,
3240 .mul, .mul_wrap => {},
3241 .div_trunc, .div_floor, .div_exact, .rem, .mod => {
3242 const signed = dst_ty.isSignedInt(mod);
3243 var callee_buf: ["__udiv?i3".len]u8 = undefined;
3244 const signed_div_floor_state: struct {
3245 frame_index: FrameIndex,
3246 reloc: Mir.Inst.Index,
3247 } = if (signed and tag == .div_floor) state: {
3248 const frame_index = try self.allocFrameIndex(FrameAlloc.initType(Type.usize, mod));
3249 try self.asmMemoryImmediate(
3250 .{ ._, .mov },
3251 Memory.sib(.qword, .{ .base = .{ .frame = frame_index } }),
3252 Immediate.u(0),
3253 );
3254
3255 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
3256 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
3257 defer self.register_manager.unlockReg(tmp_lock);
3258
3259 const lhs_mcv = try self.resolveInst(bin_op.lhs);
3260 if (lhs_mcv.isMemory()) try self.asmRegisterMemory(
3261 .{ ._, .mov },
3262 tmp_reg,
3263 lhs_mcv.address().offset(8).deref().mem(.qword),
3264 ) else try self.asmRegisterRegister(
3265 .{ ._, .mov },
3266 tmp_reg,
3267 lhs_mcv.register_pair[1],
3268 );
3269
3270 const rhs_mcv = try self.resolveInst(bin_op.rhs);
3271 if (rhs_mcv.isMemory()) try self.asmRegisterMemory(
3272 .{ ._, .xor },
3273 tmp_reg,
3274 rhs_mcv.address().offset(8).deref().mem(.qword),
3275 ) else try self.asmRegisterRegister(
3276 .{ ._, .xor },
3277 tmp_reg,
3278 rhs_mcv.register_pair[1],
3279 );
3280 const reloc = try self.asmJccReloc(.ns, undefined);
3281
3282 break :state .{ .frame_index = frame_index, .reloc = reloc };
3283 } else undefined;
3284 const call_mcv = try self.genCall(
3285 .{ .lib = .{
3286 .return_type = dst_ty.toIntern(),
3287 .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() },
3288 .callee = std.fmt.bufPrint(&callee_buf, "__{s}{s}{c}i3", .{
3289 if (signed) "" else "u",
3290 switch (tag) {
3291 .div_trunc, .div_exact => "div",
3292 .div_floor => if (signed) "mod" else "div",
3293 .rem, .mod => "mod",
3294 else => unreachable,
3295 },
3296 intCompilerRtAbiName(@intCast(dst_ty.bitSize(mod))),
3297 }) catch unreachable,
3298 } },
3299 &.{ src_ty, src_ty },
3300 &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } },
3301 );
3302 break :result if (signed) switch (tag) {
3303 .div_floor => {
3304 try self.asmRegisterRegister(
3305 .{ ._, .@"or" },
3306 call_mcv.register_pair[0],
3307 call_mcv.register_pair[1],
3308 );
3309 try self.asmSetccMemory(.nz, Memory.sib(.byte, .{
3310 .base = .{ .frame = signed_div_floor_state.frame_index },
3311 }));
3312 try self.performReloc(signed_div_floor_state.reloc);
3313 const dst_mcv = try self.genCall(
3314 .{ .lib = .{
3315 .return_type = dst_ty.toIntern(),
3316 .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() },
3317 .callee = std.fmt.bufPrint(&callee_buf, "__div{c}i3", .{
3318 intCompilerRtAbiName(@intCast(dst_ty.bitSize(mod))),
3319 }) catch unreachable,
3320 } },
3321 &.{ src_ty, src_ty },
3322 &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } },
3323 );
3324 try self.asmRegisterMemory(
3325 .{ ._, .sub },
3326 dst_mcv.register_pair[0],
3327 Memory.sib(.qword, .{
3328 .base = .{ .frame = signed_div_floor_state.frame_index },
3329 }),
3330 );
3331 try self.asmRegisterImmediate(
3332 .{ ._, .sbb },
3333 dst_mcv.register_pair[1],
3334 Immediate.u(0),
3335 );
3336 try self.freeValue(
3337 .{ .load_frame = .{ .index = signed_div_floor_state.frame_index } },
3338 );
3339 break :result dst_mcv;
3340 },
3341 .mod => {
3342 const dst_regs = call_mcv.register_pair;
3343 const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs);
3344 defer for (dst_locks) |lock| self.register_manager.unlockReg(lock);
3345
3346 const tmp_regs =
3347 try self.register_manager.allocRegs(2, .{null} ** 2, abi.RegisterClass.gp);
3348 const tmp_locks = self.register_manager.lockRegsAssumeUnused(2, tmp_regs);
3349 defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock);
3350
3351 const rhs_mcv = try self.resolveInst(bin_op.rhs);
3352
3353 for (tmp_regs, dst_regs) |tmp_reg, dst_reg|
3354 try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, dst_reg);
3355 if (rhs_mcv.isMemory()) {
3356 try self.asmRegisterMemory(.{ ._, .add }, tmp_regs[0], rhs_mcv.mem(.qword));
3357 try self.asmRegisterMemory(
3358 .{ ._, .adc },
3359 tmp_regs[1],
3360 rhs_mcv.address().offset(8).deref().mem(.qword),
3361 );
3362 } else for (
3363 [_]Mir.Inst.Tag{ .add, .adc },
3364 tmp_regs,
3365 rhs_mcv.register_pair,
3366 ) |op, tmp_reg, rhs_reg|
3367 try self.asmRegisterRegister(.{ ._, op }, tmp_reg, rhs_reg);
3368 try self.asmRegisterRegister(.{ ._, .@"test" }, dst_regs[1], dst_regs[1]);
3369 for (dst_regs, tmp_regs) |dst_reg, tmp_reg|
3370 try self.asmCmovccRegisterRegister(.s, dst_reg, tmp_reg);
3371 break :result call_mcv;
3372 },
3373 else => call_mcv,
3374 } else call_mcv;
3375 },
3376 };
32353377
32363378 try self.spillEflagsIfOccupied();
32373379 try self.spillRegisters(&.{ .rax, .rdx });
3238 const lhs = try self.resolveInst(bin_op.lhs);
3239 const rhs = try self.resolveInst(bin_op.rhs);
3240 break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs, rhs);
3380 const lhs_mcv = try self.resolveInst(bin_op.lhs);
3381 const rhs_mcv = try self.resolveInst(bin_op.rhs);
3382 break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs_mcv, rhs_mcv);
32413383 };
32423384 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
32433385}
......@@ -7201,37 +7343,7 @@ fn genMulDivBinOp(
72017343 assert(self.eflags_inst == null);
72027344
72037345 if (dst_abi_size == 16 and src_abi_size == 16) {
7204 switch (tag) {
7205 else => unreachable,
7206 .mul, .mul_wrap => {},
7207 .div_trunc, .div_floor, .div_exact, .rem, .mod => {
7208 const signed = dst_ty.isSignedInt(mod);
7209 if (signed) switch (tag) {
7210 .div_floor, .mod => return self.fail(
7211 "TODO implement genMulDivBinOp for {s} from {} to {}",
7212 .{ @tagName(tag), src_ty.fmt(mod), dst_ty.fmt(mod) },
7213 ),
7214 else => {},
7215 };
7216 var callee_buf: ["__udiv?i3".len]u8 = undefined;
7217 return try self.genCall(.{ .lib = .{
7218 .return_type = dst_ty.toIntern(),
7219 .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() },
7220 .callee = std.fmt.bufPrint(&callee_buf, "__{s}{s}{c}i3", .{
7221 if (signed) "" else "u",
7222 switch (tag) {
7223 .div_trunc, .div_exact => "div",
7224 .div_floor => if (signed) unreachable else "div",
7225 .rem => "mod",
7226 .mod => if (signed) unreachable else "mod",
7227 else => unreachable,
7228 },
7229 intCompilerRtAbiName(@intCast(dst_ty.bitSize(mod))),
7230 }) catch unreachable,
7231 } }, &.{ src_ty, src_ty }, &.{ lhs_mcv, rhs_mcv });
7232 },
7233 }
7234
7346 assert(tag == .mul or tag == .mul_wrap);
72357347 const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
72367348 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
72377349