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)
130130
131131test "__udivei4/__umodei4" {
132132 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
133 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
134133
135134 const RndGen = std.Random.DefaultPrng;
136135 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 {
30083008
30093009 try self.genCopy(dst_ty, dst_mcv, src_mcv);
30103010 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
30133013 if (dst_ty.zigTypeTag(mod) == .Vector) {
30143014 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 {
34293429 };
34303430
34313431 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
34333436 const lhs_mcv = try self.resolveInst(bin_op.lhs);
34343437 const rhs_mcv = try self.resolveInst(bin_op.rhs);
34353438 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 {
36853688 .{ty.fmt(mod)},
36863689 );
36873690
3688 try self.spillRegisters(&.{ .rax, .rdx });
3689 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx });
3690 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
3691 try self.spillRegisters(&.{ .rax, .rcx, .rdx });
3692 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx });
3693 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
36913694
36923695 const lhs_mcv = try self.resolveInst(bin_op.lhs);
36933696 const lhs_lock = switch (lhs_mcv) {
......@@ -3950,11 +3953,154 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
39503953 .Vector => return self.fail("TODO implement airMulWithOverflow for {}", .{dst_ty.fmt(mod)}),
39513954 .Int => result: {
39523955 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
39534100 const lhs_active_bits = self.activeIntBits(bin_op.lhs);
39544101 const rhs_active_bits = self.activeIntBits(bin_op.rhs);
39554102 const src_bits = @max(lhs_active_bits, rhs_active_bits, dst_info.bits / 2);
39564103 const src_ty = try mod.intType(dst_info.signedness, src_bits);
3957
39584104 if (src_bits > 64 and src_bits <= 128 and
39594105 dst_info.bits > 64 and dst_info.bits <= 128) switch (dst_info.signedness) {
39604106 .signed => {
......@@ -4110,7 +4256,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
41104256 };
41114257
41124258 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
41154263 const cc: Condition = switch (dst_info.signedness) {
41164264 .unsigned => .c,
......@@ -8053,13 +8201,14 @@ fn genMulDivBinOp(
80538201 const src_abi_size: u32 = @intCast(src_ty.abiSize(mod));
80548202
80558203 assert(self.register_manager.isRegFree(.rax));
8204 assert(self.register_manager.isRegFree(.rcx));
80568205 assert(self.register_manager.isRegFree(.rdx));
80578206 assert(self.eflags_inst == null);
80588207
80598208 if (dst_abi_size == 16 and src_abi_size == 16) {
80608209 assert(tag == .mul or tag == .mul_wrap);
8061 const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
8062 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
8210 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx });
8211 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
80638212
80648213 const mat_lhs_mcv = switch (lhs_mcv) {
80658214 .load_symbol => mat_lhs_mcv: {
......@@ -8124,10 +8273,171 @@ fn genMulDivBinOp(
81248273 else => unreachable,
81258274 .mul, .mul_wrap => dst_abi_size != src_abi_size and dst_abi_size != src_abi_size * 2,
81268275 .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_abi_size != src_abi_size,
8127 } or src_abi_size > 8) return self.fail(
8128 "TODO implement genMulDivBinOp for {s} from {} to {}",
8129 .{ @tagName(tag), src_ty.fmt(mod), dst_ty.fmt(mod) },
8130 );
8276 } or src_abi_size > 8) {
8277 const src_info = src_ty.intInfo(mod);
8278 switch (tag) {
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 }
81318441 const ty = if (dst_abi_size <= 8) dst_ty else src_ty;
81328442 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 {
232232 cmps, cmpsb, cmpsd, cmpsq, cmpsw,
233233 cmpxchg, cmpxchg8b, cmpxchg16b,
234234 cpuid, cqo, cwd, cwde,
235 div,
236 idiv, imul, int3,
235 dec, div, idiv, imul, inc, int3,
237236 ja, jae, jb, jbe, jc, jrcxz, je, jg, jge, jl, jle, jna, jnae, jnb, jnbe,
238237 jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz,
239238 jmp,
src/arch/x86_64/Mir.zig+5-1
......@@ -325,6 +325,8 @@ pub const Inst = struct {
325325 cwd,
326326 /// Convert word to doubleword
327327 cwde,
328 /// Decrement by 1
329 dec,
328330 /// Unsigned division
329331 /// Signed division
330332 /// Divide packed single-precision floating-point values
......@@ -332,7 +334,9 @@ pub const Inst = struct {
332334 /// Divide packed double-precision floating-point values
333335 /// Divide scalar double-precision floating-point values
334336 div,
335 ///
337 /// Increment by 1
338 inc,
339 /// Call to interrupt procedure
336340 int3,
337341 /// Conditional jump
338342 j,
src/arch/x86_64/encodings.zig+12
......@@ -269,6 +269,12 @@ pub const table = [_]Entry{
269269
270270 .{ .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
272278 .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .none, .none },
273279 .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .rex, .none },
274280 .{ .div, .m, &.{ .rm16 }, &.{ 0xf7 }, 6, .short, .none },
......@@ -296,6 +302,12 @@ pub const table = [_]Entry{
296302 .{ .imul, .rmi, &.{ .r32, .rm32, .imm32 }, &.{ 0x69 }, 0, .none, .none },
297303 .{ .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
299311 .{ .int3, .zo, &.{}, &.{ 0xcc }, 0, .none, .none },
300312
301313 .{ .ja, .d, &.{ .rel32 }, &.{ 0x0f, 0x87 }, 0, .none, .none },
test/behavior/math.zig+34
......@@ -1059,6 +1059,40 @@ test "@mulWithOverflow bitsize > 32" {
10591059 }
10601060}
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
10621096test "@subWithOverflow" {
10631097 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10641098 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO