authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-05 05:31:57+01:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2024-02-12 05:25:07+01:00
log6235762c095e0031b4d5c3da1687f184a2125988
tree3d50f10a4a4f70cd2e6a8c4097af0f27af16cb78
parentc3eb592a343d25bbb0f79b80104d7890877c7af6

x86_64: implement mul, div, and mod of large integers

This enables the last compiler-rt test disabled for the x86_64 backend.

6 files changed, 375 insertions(+), 17 deletions(-)

lib/compiler_rt/udivmodei4.zig-1
...@@ -130,7 +130,6 @@ pub fn __umodei4(r_p: [*]u32, u_p: [*]const u32, v_p: [*]const u32, bits: usize)...@@ -130,7 +130,6 @@ pub fn __umodei4(r_p: [*]u32, u_p: [*]const u32, v_p: [*]const u32, bits: usize)
130130
131test "__udivei4/__umodei4" {131test "__udivei4/__umodei4" {
132 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;132 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
133 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
134133
135 const RndGen = std.Random.DefaultPrng;134 const RndGen = std.Random.DefaultPrng;
136 var rnd = RndGen.init(42);135 var rnd = RndGen.init(42);
src/arch/x86_64/CodeGen.zig+323-13
...@@ -3008,7 +3008,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -3008,7 +3008,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
30083008
3009 try self.genCopy(dst_ty, dst_mcv, src_mcv);3009 try self.genCopy(dst_ty, dst_mcv, src_mcv);
3010 break :dst dst_mcv;3010 break :dst dst_mcv;
3011 } else return self.fail("TODO implement trunc from {} to {}", .{ src_ty.fmt(mod), dst_ty.fmt(mod) });3011 } else try self.allocRegOrMem(inst, true);
30123012
3013 if (dst_ty.zigTypeTag(mod) == .Vector) {3013 if (dst_ty.zigTypeTag(mod) == .Vector) {
3014 assert(src_ty.zigTypeTag(mod) == .Vector and dst_ty.vectorLen(mod) == src_ty.vectorLen(mod));3014 assert(src_ty.zigTypeTag(mod) == .Vector and dst_ty.vectorLen(mod) == src_ty.vectorLen(mod));
...@@ -3429,7 +3429,10 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {...@@ -3429,7 +3429,10 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
3429 };3429 };
34303430
3431 try self.spillEflagsIfOccupied();3431 try self.spillEflagsIfOccupied();
3432 try self.spillRegisters(&.{ .rax, .rdx });3432 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
3433 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx });
3434 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
3435
3433 const lhs_mcv = try self.resolveInst(bin_op.lhs);3436 const lhs_mcv = try self.resolveInst(bin_op.lhs);
3434 const rhs_mcv = try self.resolveInst(bin_op.rhs);3437 const rhs_mcv = try self.resolveInst(bin_op.rhs);
3435 break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs_mcv, rhs_mcv);3438 break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs_mcv, rhs_mcv);
...@@ -3685,9 +3688,9 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {...@@ -3685,9 +3688,9 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
3685 .{ty.fmt(mod)},3688 .{ty.fmt(mod)},
3686 );3689 );
36873690
3688 try self.spillRegisters(&.{ .rax, .rdx });3691 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
3689 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx });3692 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx });
3690 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);3693 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
36913694
3692 const lhs_mcv = try self.resolveInst(bin_op.lhs);3695 const lhs_mcv = try self.resolveInst(bin_op.lhs);
3693 const lhs_lock = switch (lhs_mcv) {3696 const lhs_lock = switch (lhs_mcv) {
...@@ -3950,11 +3953,154 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -3950,11 +3953,154 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
3950 .Vector => return self.fail("TODO implement airMulWithOverflow for {}", .{dst_ty.fmt(mod)}),3953 .Vector => return self.fail("TODO implement airMulWithOverflow for {}", .{dst_ty.fmt(mod)}),
3951 .Int => result: {3954 .Int => result: {
3952 const dst_info = dst_ty.intInfo(mod);3955 const dst_info = dst_ty.intInfo(mod);
3956 if (dst_info.bits > 128 and dst_info.signedness == .unsigned) {
3957 const slow_inc = self.hasFeature(.slow_incdec);
3958 const abi_size: u32 = @intCast(dst_ty.abiSize(mod));
3959 const limb_len = std.math.divCeil(u32, abi_size, 8) catch unreachable;
3960
3961 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
3962 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx });
3963 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
3964
3965 const dst_mcv = try self.allocRegOrMem(inst, false);
3966 try self.genInlineMemset(
3967 dst_mcv.address(),
3968 .{ .immediate = 0 },
3969 .{ .immediate = tuple_ty.abiSize(mod) },
3970 );
3971 const lhs_mcv = try self.resolveInst(bin_op.lhs);
3972 const rhs_mcv = try self.resolveInst(bin_op.rhs);
3973
3974 const temp_regs = try self.register_manager.allocRegs(
3975 4,
3976 .{ null, null, null, null },
3977 abi.RegisterClass.gp,
3978 );
3979 const temp_locks = self.register_manager.lockRegsAssumeUnused(4, temp_regs);
3980 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock);
3981
3982 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[0].to32(), temp_regs[0].to32());
3983
3984 const outer_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
3985 try self.asmRegisterMemory(.{ ._, .mov }, temp_regs[1].to64(), .{
3986 .base = .{ .frame = rhs_mcv.load_frame.index },
3987 .mod = .{ .rm = .{
3988 .size = .qword,
3989 .index = temp_regs[0].to64(),
3990 .scale = .@"8",
3991 } },
3992 });
3993 try self.asmRegisterRegister(.{ ._, .@"test" }, temp_regs[1].to64(), temp_regs[1].to64());
3994 const skip_inner = try self.asmJccReloc(.z, undefined);
3995
3996 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[2].to32(), temp_regs[2].to32());
3997 try self.asmRegisterRegister(.{ ._, .mov }, temp_regs[3].to32(), temp_regs[0].to32());
3998 try self.asmRegisterRegister(.{ ._, .xor }, .ecx, .ecx);
3999 try self.asmRegisterRegister(.{ ._, .xor }, .edx, .edx);
4000
4001 const inner_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
4002 try self.asmRegisterImmediate(.{ ._r, .sh }, .cl, Immediate.u(1));
4003 try self.asmMemoryRegister(.{ ._, .adc }, .{
4004 .base = .{ .frame = dst_mcv.load_frame.index },
4005 .mod = .{ .rm = .{
4006 .size = .qword,
4007 .index = temp_regs[3].to64(),
4008 .scale = .@"8",
4009 .disp = @intCast(tuple_ty.structFieldOffset(0, mod)),
4010 } },
4011 }, .rdx);
4012 try self.asmSetccRegister(.c, .cl);
4013
4014 try self.asmRegisterMemory(.{ ._, .mov }, .rax, .{
4015 .base = .{ .frame = lhs_mcv.load_frame.index },
4016 .mod = .{ .rm = .{
4017 .size = .qword,
4018 .index = temp_regs[2].to64(),
4019 .scale = .@"8",
4020 } },
4021 });
4022 try self.asmRegister(.{ ._, .mul }, temp_regs[1].to64());
4023
4024 try self.asmRegisterImmediate(.{ ._r, .sh }, .ch, Immediate.u(1));
4025 try self.asmMemoryRegister(.{ ._, .adc }, .{
4026 .base = .{ .frame = dst_mcv.load_frame.index },
4027 .mod = .{ .rm = .{
4028 .size = .qword,
4029 .index = temp_regs[3].to64(),
4030 .scale = .@"8",
4031 .disp = @intCast(tuple_ty.structFieldOffset(0, mod)),
4032 } },
4033 }, .rax);
4034 try self.asmSetccRegister(.c, .ch);
4035
4036 if (slow_inc) {
4037 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[2].to32(), Immediate.u(1));
4038 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[3].to32(), Immediate.u(1));
4039 } else {
4040 try self.asmRegister(.{ ._, .inc }, temp_regs[2].to32());
4041 try self.asmRegister(.{ ._, .inc }, temp_regs[3].to32());
4042 }
4043 try self.asmRegisterImmediate(
4044 .{ ._, .cmp },
4045 temp_regs[3].to32(),
4046 Immediate.u(limb_len),
4047 );
4048 _ = try self.asmJccReloc(.b, inner_loop);
4049
4050 try self.asmRegisterRegister(.{ ._, .@"or" }, .rdx, .rcx);
4051 const overflow = try self.asmJccReloc(.nz, undefined);
4052 const overflow_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
4053 try self.asmRegisterImmediate(
4054 .{ ._, .cmp },
4055 temp_regs[2].to32(),
4056 Immediate.u(limb_len),
4057 );
4058 const no_overflow = try self.asmJccReloc(.nb, undefined);
4059 if (slow_inc) {
4060 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[2].to32(), Immediate.u(1));
4061 } else {
4062 try self.asmRegister(.{ ._, .inc }, temp_regs[2].to32());
4063 }
4064 try self.asmMemoryImmediate(.{ ._, .cmp }, .{
4065 .base = .{ .frame = lhs_mcv.load_frame.index },
4066 .mod = .{ .rm = .{
4067 .size = .qword,
4068 .index = temp_regs[2].to64(),
4069 .scale = .@"8",
4070 .disp = -8,
4071 } },
4072 }, Immediate.u(0));
4073 _ = try self.asmJccReloc(.z, overflow_loop);
4074 try self.performReloc(overflow);
4075 try self.asmMemoryImmediate(.{ ._, .mov }, .{
4076 .base = .{ .frame = dst_mcv.load_frame.index },
4077 .mod = .{ .rm = .{
4078 .size = .byte,
4079 .disp = @intCast(tuple_ty.structFieldOffset(1, mod)),
4080 } },
4081 }, Immediate.u(1));
4082 try self.performReloc(no_overflow);
4083
4084 try self.performReloc(skip_inner);
4085 if (slow_inc) {
4086 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[0].to32(), Immediate.u(1));
4087 } else {
4088 try self.asmRegister(.{ ._, .inc }, temp_regs[0].to32());
4089 }
4090 try self.asmRegisterImmediate(
4091 .{ ._, .cmp },
4092 temp_regs[0].to32(),
4093 Immediate.u(limb_len),
4094 );
4095 _ = try self.asmJccReloc(.b, outer_loop);
4096
4097 break :result dst_mcv;
4098 }
4099
3953 const lhs_active_bits = self.activeIntBits(bin_op.lhs);4100 const lhs_active_bits = self.activeIntBits(bin_op.lhs);
3954 const rhs_active_bits = self.activeIntBits(bin_op.rhs);4101 const rhs_active_bits = self.activeIntBits(bin_op.rhs);
3955 const src_bits = @max(lhs_active_bits, rhs_active_bits, dst_info.bits / 2);4102 const src_bits = @max(lhs_active_bits, rhs_active_bits, dst_info.bits / 2);
3956 const src_ty = try mod.intType(dst_info.signedness, src_bits);4103 const src_ty = try mod.intType(dst_info.signedness, src_bits);
3957
3958 if (src_bits > 64 and src_bits <= 128 and4104 if (src_bits > 64 and src_bits <= 128 and
3959 dst_info.bits > 64 and dst_info.bits <= 128) switch (dst_info.signedness) {4105 dst_info.bits > 64 and dst_info.bits <= 128) switch (dst_info.signedness) {
3960 .signed => {4106 .signed => {
...@@ -4110,7 +4256,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -4110,7 +4256,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
4110 };4256 };
41114257
4112 try self.spillEflagsIfOccupied();4258 try self.spillEflagsIfOccupied();
4113 try self.spillRegisters(&.{ .rax, .rdx });4259 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
4260 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx });
4261 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
41144262
4115 const cc: Condition = switch (dst_info.signedness) {4263 const cc: Condition = switch (dst_info.signedness) {
4116 .unsigned => .c,4264 .unsigned => .c,
...@@ -8053,13 +8201,14 @@ fn genMulDivBinOp(...@@ -8053,13 +8201,14 @@ fn genMulDivBinOp(
8053 const src_abi_size: u32 = @intCast(src_ty.abiSize(mod));8201 const src_abi_size: u32 = @intCast(src_ty.abiSize(mod));
80548202
8055 assert(self.register_manager.isRegFree(.rax));8203 assert(self.register_manager.isRegFree(.rax));
8204 assert(self.register_manager.isRegFree(.rcx));
8056 assert(self.register_manager.isRegFree(.rdx));8205 assert(self.register_manager.isRegFree(.rdx));
8057 assert(self.eflags_inst == null);8206 assert(self.eflags_inst == null);
80588207
8059 if (dst_abi_size == 16 and src_abi_size == 16) {8208 if (dst_abi_size == 16 and src_abi_size == 16) {
8060 assert(tag == .mul or tag == .mul_wrap);8209 assert(tag == .mul or tag == .mul_wrap);
8061 const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });8210 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx });
8062 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);8211 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
80638212
8064 const mat_lhs_mcv = switch (lhs_mcv) {8213 const mat_lhs_mcv = switch (lhs_mcv) {
8065 .load_symbol => mat_lhs_mcv: {8214 .load_symbol => mat_lhs_mcv: {
...@@ -8124,10 +8273,171 @@ fn genMulDivBinOp(...@@ -8124,10 +8273,171 @@ fn genMulDivBinOp(
8124 else => unreachable,8273 else => unreachable,
8125 .mul, .mul_wrap => dst_abi_size != src_abi_size and dst_abi_size != src_abi_size * 2,8274 .mul, .mul_wrap => dst_abi_size != src_abi_size and dst_abi_size != src_abi_size * 2,
8126 .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_abi_size != src_abi_size,8275 .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_abi_size != src_abi_size,
8127 } or src_abi_size > 8) return self.fail(8276 } or src_abi_size > 8) {
8128 "TODO implement genMulDivBinOp for {s} from {} to {}",8277 const src_info = src_ty.intInfo(mod);
8129 .{ @tagName(tag), src_ty.fmt(mod), dst_ty.fmt(mod) },8278 switch (tag) {
8130 );8279 .mul, .mul_wrap => {
8280 const slow_inc = self.hasFeature(.slow_incdec);
8281 const limb_len = std.math.divCeil(u32, src_abi_size, 8) catch unreachable;
8282
8283 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
8284 const reg_locks = self.register_manager.lockRegs(3, .{ .rax, .rcx, .rdx });
8285 defer for (reg_locks) |reg_lock| if (reg_lock) |lock|
8286 self.register_manager.unlockReg(lock);
8287
8288 const dst_mcv = try self.allocRegOrMemAdvanced(dst_ty, maybe_inst, false);
8289 try self.genInlineMemset(
8290 dst_mcv.address(),
8291 .{ .immediate = 0 },
8292 .{ .immediate = src_abi_size },
8293 );
8294
8295 const temp_regs = try self.register_manager.allocRegs(
8296 4,
8297 .{ null, null, null, null },
8298 abi.RegisterClass.gp,
8299 );
8300 const temp_locks = self.register_manager.lockRegs(4, temp_regs);
8301 defer for (temp_locks) |temp_lock| if (temp_lock) |lock|
8302 self.register_manager.unlockReg(lock);
8303
8304 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[0].to32(), temp_regs[0].to32());
8305
8306 const outer_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
8307 try self.asmRegisterMemory(.{ ._, .mov }, temp_regs[1].to64(), .{
8308 .base = .{ .frame = rhs_mcv.load_frame.index },
8309 .mod = .{ .rm = .{
8310 .size = .qword,
8311 .index = temp_regs[0].to64(),
8312 .scale = .@"8",
8313 } },
8314 });
8315 try self.asmRegisterRegister(.{ ._, .@"test" }, temp_regs[1].to64(), temp_regs[1].to64());
8316 const skip_inner = try self.asmJccReloc(.z, undefined);
8317
8318 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[2].to32(), temp_regs[2].to32());
8319 try self.asmRegisterRegister(.{ ._, .mov }, temp_regs[3].to32(), temp_regs[0].to32());
8320 try self.asmRegisterRegister(.{ ._, .xor }, .ecx, .ecx);
8321 try self.asmRegisterRegister(.{ ._, .xor }, .edx, .edx);
8322
8323 const inner_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len);
8324 try self.asmRegisterImmediate(.{ ._r, .sh }, .cl, Immediate.u(1));
8325 try self.asmMemoryRegister(.{ ._, .adc }, .{
8326 .base = .{ .frame = dst_mcv.load_frame.index },
8327 .mod = .{ .rm = .{
8328 .size = .qword,
8329 .index = temp_regs[3].to64(),
8330 .scale = .@"8",
8331 } },
8332 }, .rdx);
8333 try self.asmSetccRegister(.c, .cl);
8334
8335 try self.asmRegisterMemory(.{ ._, .mov }, .rax, .{
8336 .base = .{ .frame = lhs_mcv.load_frame.index },
8337 .mod = .{ .rm = .{
8338 .size = .qword,
8339 .index = temp_regs[2].to64(),
8340 .scale = .@"8",
8341 } },
8342 });
8343 try self.asmRegister(.{ ._, .mul }, temp_regs[1].to64());
8344
8345 try self.asmRegisterImmediate(.{ ._r, .sh }, .ch, Immediate.u(1));
8346 try self.asmMemoryRegister(.{ ._, .adc }, .{
8347 .base = .{ .frame = dst_mcv.load_frame.index },
8348 .mod = .{ .rm = .{
8349 .size = .qword,
8350 .index = temp_regs[3].to64(),
8351 .scale = .@"8",
8352 } },
8353 }, .rax);
8354 try self.asmSetccRegister(.c, .ch);
8355
8356 if (slow_inc) {
8357 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[2].to32(), Immediate.u(1));
8358 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[3].to32(), Immediate.u(1));
8359 } else {
8360 try self.asmRegister(.{ ._, .inc }, temp_regs[2].to32());
8361 try self.asmRegister(.{ ._, .inc }, temp_regs[3].to32());
8362 }
8363 try self.asmRegisterImmediate(
8364 .{ ._, .cmp },
8365 temp_regs[3].to32(),
8366 Immediate.u(limb_len),
8367 );
8368 _ = try self.asmJccReloc(.b, inner_loop);
8369
8370 try self.performReloc(skip_inner);
8371 if (slow_inc) {
8372 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[0].to32(), Immediate.u(1));
8373 } else {
8374 try self.asmRegister(.{ ._, .inc }, temp_regs[0].to32());
8375 }
8376 try self.asmRegisterImmediate(
8377 .{ ._, .cmp },
8378 temp_regs[0].to32(),
8379 Immediate.u(limb_len),
8380 );
8381 _ = try self.asmJccReloc(.b, outer_loop);
8382
8383 return dst_mcv;
8384 },
8385 .div_trunc, .div_floor, .div_exact, .rem, .mod => switch (src_info.signedness) {
8386 .signed => {},
8387 .unsigned => {
8388 const dst_mcv = try self.allocRegOrMemAdvanced(dst_ty, maybe_inst, false);
8389 const manyptr_u32_ty = try mod.ptrType(.{
8390 .child = .u32_type,
8391 .flags = .{
8392 .size = .Many,
8393 },
8394 });
8395 const manyptr_const_u32_ty = try mod.ptrType(.{
8396 .child = .u32_type,
8397 .flags = .{
8398 .size = .Many,
8399 .is_const = true,
8400 },
8401 });
8402 _ = try self.genCall(.{ .lib = .{
8403 .return_type = .void_type,
8404 .param_types = &.{
8405 manyptr_u32_ty.toIntern(),
8406 manyptr_const_u32_ty.toIntern(),
8407 manyptr_const_u32_ty.toIntern(),
8408 .usize_type,
8409 },
8410 .callee = switch (tag) {
8411 .div_trunc,
8412 .div_floor,
8413 .div_exact,
8414 => "__udivei4",
8415 .rem,
8416 .mod,
8417 => "__umodei4",
8418 else => unreachable,
8419 },
8420 } }, &.{
8421 manyptr_u32_ty,
8422 manyptr_const_u32_ty,
8423 manyptr_const_u32_ty,
8424 Type.usize,
8425 }, &.{
8426 dst_mcv.address(),
8427 lhs_mcv.address(),
8428 rhs_mcv.address(),
8429 .{ .immediate = src_info.bits },
8430 });
8431 return dst_mcv;
8432 },
8433 },
8434 else => {},
8435 }
8436 return self.fail(
8437 "TODO implement genMulDivBinOp for {s} from {} to {}",
8438 .{ @tagName(tag), src_ty.fmt(mod), dst_ty.fmt(mod) },
8439 );
8440 }
8131 const ty = if (dst_abi_size <= 8) dst_ty else src_ty;8441 const ty = if (dst_abi_size <= 8) dst_ty else src_ty;
8132 const abi_size = if (dst_abi_size <= 8) dst_abi_size else src_abi_size;8442 const abi_size = if (dst_abi_size <= 8) dst_abi_size else src_abi_size;
81338443
src/arch/x86_64/Encoding.zig+1-2
...@@ -232,8 +232,7 @@ pub const Mnemonic = enum {...@@ -232,8 +232,7 @@ pub const Mnemonic = enum {
232 cmps, cmpsb, cmpsd, cmpsq, cmpsw,232 cmps, cmpsb, cmpsd, cmpsq, cmpsw,
233 cmpxchg, cmpxchg8b, cmpxchg16b,233 cmpxchg, cmpxchg8b, cmpxchg16b,
234 cpuid, cqo, cwd, cwde,234 cpuid, cqo, cwd, cwde,
235 div,235 dec, div, idiv, imul, inc, int3,
236 idiv, imul, int3,
237 ja, jae, jb, jbe, jc, jrcxz, je, jg, jge, jl, jle, jna, jnae, jnb, jnbe,236 ja, jae, jb, jbe, jc, jrcxz, je, jg, jge, jl, jle, jna, jnae, jnb, jnbe,
238 jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz,237 jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz,
239 jmp, 238 jmp,
src/arch/x86_64/Mir.zig+5-1
...@@ -325,6 +325,8 @@ pub const Inst = struct {...@@ -325,6 +325,8 @@ pub const Inst = struct {
325 cwd,325 cwd,
326 /// Convert word to doubleword326 /// Convert word to doubleword
327 cwde,327 cwde,
328 /// Decrement by 1
329 dec,
328 /// Unsigned division330 /// Unsigned division
329 /// Signed division331 /// Signed division
330 /// Divide packed single-precision floating-point values332 /// Divide packed single-precision floating-point values
...@@ -332,7 +334,9 @@ pub const Inst = struct {...@@ -332,7 +334,9 @@ pub const Inst = struct {
332 /// Divide packed double-precision floating-point values334 /// Divide packed double-precision floating-point values
333 /// Divide scalar double-precision floating-point values335 /// Divide scalar double-precision floating-point values
334 div,336 div,
335 ///337 /// Increment by 1
338 inc,
339 /// Call to interrupt procedure
336 int3,340 int3,
337 /// Conditional jump341 /// Conditional jump
338 j,342 j,
src/arch/x86_64/encodings.zig+12
...@@ -269,6 +269,12 @@ pub const table = [_]Entry{...@@ -269,6 +269,12 @@ pub const table = [_]Entry{
269269
270 .{ .cpuid, .zo, &.{}, &.{ 0x0f, 0xa2 }, 0, .none, .none },270 .{ .cpuid, .zo, &.{}, &.{ 0x0f, 0xa2 }, 0, .none, .none },
271271
272 .{ .dec, .m, &.{ .rm8 }, &.{ 0xfe }, 1, .none, .none },
273 .{ .dec, .m, &.{ .rm8 }, &.{ 0xfe }, 1, .rex, .none },
274 .{ .dec, .m, &.{ .rm16 }, &.{ 0xff }, 1, .short, .none },
275 .{ .dec, .m, &.{ .rm32 }, &.{ 0xff }, 1, .none, .none },
276 .{ .dec, .m, &.{ .rm64 }, &.{ 0xff }, 1, .long, .none },
277
272 .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .none, .none },278 .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .none, .none },
273 .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .rex, .none },279 .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .rex, .none },
274 .{ .div, .m, &.{ .rm16 }, &.{ 0xf7 }, 6, .short, .none },280 .{ .div, .m, &.{ .rm16 }, &.{ 0xf7 }, 6, .short, .none },
...@@ -296,6 +302,12 @@ pub const table = [_]Entry{...@@ -296,6 +302,12 @@ pub const table = [_]Entry{
296 .{ .imul, .rmi, &.{ .r32, .rm32, .imm32 }, &.{ 0x69 }, 0, .none, .none },302 .{ .imul, .rmi, &.{ .r32, .rm32, .imm32 }, &.{ 0x69 }, 0, .none, .none },
297 .{ .imul, .rmi, &.{ .r64, .rm64, .imm32 }, &.{ 0x69 }, 0, .long, .none },303 .{ .imul, .rmi, &.{ .r64, .rm64, .imm32 }, &.{ 0x69 }, 0, .long, .none },
298304
305 .{ .inc, .m, &.{ .rm8 }, &.{ 0xfe }, 0, .none, .none },
306 .{ .inc, .m, &.{ .rm8 }, &.{ 0xfe }, 0, .rex, .none },
307 .{ .inc, .m, &.{ .rm16 }, &.{ 0xff }, 0, .short, .none },
308 .{ .inc, .m, &.{ .rm32 }, &.{ 0xff }, 0, .none, .none },
309 .{ .inc, .m, &.{ .rm64 }, &.{ 0xff }, 0, .long, .none },
310
299 .{ .int3, .zo, &.{}, &.{ 0xcc }, 0, .none, .none },311 .{ .int3, .zo, &.{}, &.{ 0xcc }, 0, .none, .none },
300312
301 .{ .ja, .d, &.{ .rel32 }, &.{ 0x0f, 0x87 }, 0, .none, .none },313 .{ .ja, .d, &.{ .rel32 }, &.{ 0x0f, 0x87 }, 0, .none, .none },
test/behavior/math.zig+34
...@@ -1059,6 +1059,40 @@ test "@mulWithOverflow bitsize > 32" {...@@ -1059,6 +1059,40 @@ test "@mulWithOverflow bitsize > 32" {
1059 }1059 }
1060}1060}
10611061
1062test "@mulWithOverflow u256" {
1063 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1064 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1065
1066 {
1067 const const_lhs: u256 = 8035709466408580321693645878924206181189;
1068 const const_rhs: u256 = 343954217539185679456797259115612849079;
1069 const const_result = @mulWithOverflow(const_lhs, const_rhs);
1070 comptime assert(const_result[0] == 100698109432518020450541558444080472799095368135495022414802684874680804056403);
1071 comptime assert(const_result[1] == 1);
1072
1073 var var_lhs = const_lhs;
1074 var var_rhs = const_rhs;
1075 _ = .{ &var_lhs, &var_rhs };
1076 const var_result = @mulWithOverflow(var_lhs, var_rhs);
1077 try std.testing.expect(var_result[0] == const_result[0]);
1078 try std.testing.expect(var_result[1] == const_result[1]);
1079 }
1080 {
1081 const const_lhs: u256 = 100477140835310762407466294984162740292250605075409128262608;
1082 const const_rhs: u256 = 406310585934439581231;
1083 const const_result = @mulWithOverflow(const_lhs, const_rhs);
1084 comptime assert(const_result[0] == 66110554277021146912650321519727251744526528332039438002889524600764482652976);
1085 comptime assert(const_result[1] == 1);
1086
1087 var var_lhs = const_lhs;
1088 var var_rhs = const_rhs;
1089 _ = .{ &var_lhs, &var_rhs };
1090 const var_result = @mulWithOverflow(var_lhs, var_rhs);
1091 try std.testing.expect(var_result[0] == const_result[0]);
1092 try std.testing.expect(var_result[1] == const_result[1]);
1093 }
1094}
1095
1062test "@subWithOverflow" {1096test "@subWithOverflow" {
1063 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1097 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1064 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1098 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO