mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-21 08:32:02 +03:00
stage2-wasm: bigint div mod rem
This commit is contained in:
@@ -24,23 +24,30 @@ inline fn neg(x: []u32) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutates the arguments!
|
||||
fn divmod(q: ?[]u32, r: ?[]u32, u: []u32, v: []u32) !void {
|
||||
const max_limbs = std.math.divCeil(usize, 65535, 32) catch unreachable;
|
||||
|
||||
fn divmod(q: ?[]u32, r: ?[]u32, u: []const u32, v: []const u32) !void {
|
||||
const u_sign: i32 = @bitCast(u[u.len - 1]);
|
||||
const v_sign: i32 = @bitCast(v[v.len - 1]);
|
||||
if (u_sign < 0) neg(u);
|
||||
if (v_sign < 0) neg(v);
|
||||
try @call(.always_inline, udivmod, .{ q, r, u, v });
|
||||
var ua: [max_limbs]u32 = undefined;
|
||||
const us = ua[0..u.len];
|
||||
@memcpy(us, u);
|
||||
var va: [max_limbs]u32 = undefined;
|
||||
const vs = va[0..v.len];
|
||||
@memcpy(vs, v);
|
||||
if (u_sign < 0) neg(us);
|
||||
if (v_sign < 0) neg(vs);
|
||||
try @call(.always_inline, udivmod, .{ q, r, us, vs });
|
||||
if (q) |x| if (u_sign ^ v_sign < 0) neg(x);
|
||||
if (r) |x| if (u_sign < 0) neg(x);
|
||||
}
|
||||
|
||||
pub fn __divei4(q_p: [*]u8, u_p: [*]u8, v_p: [*]u8, bits: usize) callconv(.c) void {
|
||||
pub fn __divei4(q_p: [*]u8, u_p: [*]const u8, v_p: [*]const u8, bits: usize) callconv(.c) void {
|
||||
@setRuntimeSafety(compiler_rt.test_safety);
|
||||
const byte_size = std.zig.target.intByteSize(&builtin.target, @intCast(bits));
|
||||
const q: []u32 = @ptrCast(@alignCast(q_p[0..byte_size]));
|
||||
const u: []u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
|
||||
const v: []u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
|
||||
const u: []const u32 = @ptrCast(@alignCast(u_p[0..byte_size]));
|
||||
const v: []const u32 = @ptrCast(@alignCast(v_p[0..byte_size]));
|
||||
@call(.always_inline, divmod, .{ q, null, u, v }) catch unreachable;
|
||||
}
|
||||
|
||||
|
||||
+39
-13
@@ -25,10 +25,26 @@ inline fn limbSet(limbs: []u64, i: usize, value: u64) void {
|
||||
}
|
||||
}
|
||||
|
||||
fn limbCount(bits: u16) u16 {
|
||||
fn usedLimbCount(bits: u16) u16 {
|
||||
return divCeil(u16, bits, 64) catch unreachable;
|
||||
}
|
||||
|
||||
fn limbCount(bits: u16) u16 {
|
||||
return @divExact(std.zig.target.intByteSize(&builtin.target, bits), 8);
|
||||
}
|
||||
|
||||
fn fixLastLimb(out_ptr: [*]u64, is_signed: bool, bits: u16) void {
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const true_limb_cnt = limbCount(bits);
|
||||
if (limb_cnt == true_limb_cnt) return;
|
||||
const true_out = out_ptr[0..true_limb_cnt];
|
||||
|
||||
const sign: u64 = if (!is_signed or @as(i64, @bitCast(true_out[limb_cnt - 1])) >= 0) 0 else ~@as(u64, 0);
|
||||
for (limb_cnt..true_limb_cnt) |i| {
|
||||
true_out[i] = sign;
|
||||
}
|
||||
}
|
||||
|
||||
fn Limbs(T: type) type {
|
||||
const int_info = @typeInfo(T).int;
|
||||
const limb_cnt = comptime limbCount(int_info.bits);
|
||||
@@ -60,7 +76,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
const b = b_ptr[0..limb_cnt];
|
||||
@@ -92,11 +108,13 @@ fn __addo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s
|
||||
|
||||
if (bits % 64 == 0) {
|
||||
limbSet(out, i, limb);
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
return carry != 0;
|
||||
} else {
|
||||
assert(carry == 0);
|
||||
const wrapped_limb = limbWrap(limb, is_signed, bits);
|
||||
limbSet(out, i, wrapped_limb);
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
return wrapped_limb != limb;
|
||||
}
|
||||
}
|
||||
@@ -132,7 +150,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
const b = b_ptr[0..limb_cnt];
|
||||
@@ -164,10 +182,12 @@ fn __subo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s
|
||||
|
||||
if (bits % 64 == 0) {
|
||||
limbSet(out, i, limb);
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
return borrow != 0;
|
||||
} else {
|
||||
const wrapped_limb = limbWrap(limb, is_signed, bits);
|
||||
limbSet(out, i, wrapped_limb);
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
return borrow != 0 or wrapped_limb != limb;
|
||||
}
|
||||
}
|
||||
@@ -206,7 +226,7 @@ comptime {
|
||||
// a == b -> 0
|
||||
// a > b -> 1
|
||||
fn __cmp_limb64(a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) i8 {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
const b = b_ptr[0..limb_cnt];
|
||||
|
||||
@@ -391,7 +411,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
@@ -405,6 +425,7 @@ fn __not_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16
|
||||
limb = limbWrap(limb, is_signed, bits);
|
||||
}
|
||||
limbSet(out, i, limb);
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
}
|
||||
|
||||
fn test__not_limb64(comptime T: type, a: T, expected: T) !void {
|
||||
@@ -436,7 +457,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) bool {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
@@ -477,6 +498,7 @@ fn __shlo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bo
|
||||
overflow = overflow or limbGet(a, j) != sign_extend;
|
||||
}
|
||||
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
return overflow;
|
||||
}
|
||||
|
||||
@@ -526,7 +548,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __shr_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, shift: u16, is_signed: bool, bits: u16) callconv(.c) void {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
@@ -594,7 +616,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __clz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
var res: u16 = 0;
|
||||
@@ -652,7 +674,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __ctz_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
var res: u16 = 0;
|
||||
@@ -705,7 +727,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __popcount_limb64(a_ptr: [*]const u64, bits: u16) callconv(.c) u16 {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
var res: u16 = 0;
|
||||
@@ -751,7 +773,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
@@ -764,6 +786,7 @@ fn __bitreverse_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bi
|
||||
if (bits % 64 != 0) {
|
||||
__shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits);
|
||||
}
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
}
|
||||
|
||||
fn test__bitreverse_limb64(comptime T: type, a: T, expected: T) !void {
|
||||
@@ -797,7 +820,7 @@ comptime {
|
||||
}
|
||||
|
||||
fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) void {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
|
||||
@@ -812,6 +835,7 @@ fn __byteswap_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, is_signed: bool, bits
|
||||
if (bits % 64 != 0) {
|
||||
__shr_limb64(out_ptr, out_ptr, 64 - bits % 64, is_signed, bits);
|
||||
}
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
}
|
||||
|
||||
fn test__byteswap_limb64(comptime T: type, a: T, expected: T) !void {
|
||||
@@ -861,7 +885,7 @@ fn mulwide(a: u64, b: u64) [2]u64 {
|
||||
}
|
||||
|
||||
fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_signed: bool, bits: u16) callconv(.c) bool {
|
||||
const limb_cnt = limbCount(bits);
|
||||
const limb_cnt = usedLimbCount(bits);
|
||||
|
||||
const out = out_ptr[0..limb_cnt];
|
||||
const a = a_ptr[0..limb_cnt];
|
||||
@@ -921,6 +945,8 @@ fn __mulo_limb64(out_ptr: [*]u64, a_ptr: [*]const u64, b_ptr: [*]const u64, is_s
|
||||
limbSet(out, limb_cnt - 1, last);
|
||||
}
|
||||
|
||||
fixLastLimb(out_ptr, is_signed, bits);
|
||||
|
||||
if (!is_signed) {
|
||||
return !hi_zero or raw_last != last;
|
||||
}
|
||||
|
||||
+119
-39
@@ -2357,6 +2357,15 @@ const IntType = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn intBackingBits(cg: *CodeGen, bits: u16) u16 {
|
||||
return switch (bits) {
|
||||
0 => unreachable,
|
||||
1...32 => 32,
|
||||
33...64 => 64,
|
||||
else => std.zig.target.intByteSize(cg.target, bits) * 8,
|
||||
};
|
||||
}
|
||||
|
||||
fn intAdd(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue {
|
||||
switch (ty.bits) {
|
||||
0 => unreachable,
|
||||
@@ -2518,7 +2527,15 @@ fn intDiv(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
|
||||
return cg.callIntrinsic(.__udivti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs });
|
||||
}
|
||||
},
|
||||
else => return cg.fail("TODO: Support intDiv for integer bitsize: {d}", .{ty.bits}),
|
||||
else => {
|
||||
const result = try cg.allocInt(ty);
|
||||
if (ty.is_signed) {
|
||||
_ = try cg.callIntrinsic(.__divei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
|
||||
} else {
|
||||
_ = try cg.callIntrinsic(.__udivei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
|
||||
}
|
||||
return result;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2570,7 +2587,22 @@ fn intDivFloor(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!W
|
||||
try cg.addTag(.i64_sub);
|
||||
return .stack;
|
||||
},
|
||||
else => return cg.fail("TODO: Support intDivFloor for signed integer bitsize: {d}", .{ty.bits}),
|
||||
else => {
|
||||
const q = try cg.intDiv(ty, lhs, rhs);
|
||||
|
||||
const zero = try cg.intZeroValue(ty);
|
||||
|
||||
const r = try cg.intRem(ty, lhs, rhs);
|
||||
_ = try cg.intCmp(ty, .neq, r, zero);
|
||||
|
||||
const sign_xor = try cg.intXor(ty, lhs, rhs);
|
||||
_ = try cg.intCmp(ty, .lt, sign_xor, zero);
|
||||
var adjust = try (try cg.intAnd(.u32, .stack, .stack)).toLocal(cg, Type.u32);
|
||||
|
||||
const adjust_bigint = try cg.intCast(ty, .u32, adjust);
|
||||
adjust.free(cg);
|
||||
return try cg.intSub(ty, q, adjust_bigint);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2596,7 +2628,15 @@ fn intRem(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerError!WValue
|
||||
return cg.callIntrinsic(.__umodti3, &.{ .i128_type, .i128_type }, Type.i128, &.{ lhs, rhs });
|
||||
}
|
||||
},
|
||||
else => return cg.fail("TODO: Support intRem for integer bitsize: {d}", .{ty.bits}),
|
||||
else => {
|
||||
const result = try cg.allocInt(ty);
|
||||
if (ty.is_signed) {
|
||||
_ = try cg.callIntrinsic(.__modei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
|
||||
} else {
|
||||
_ = try cg.callIntrinsic(.__umodei4, &.{ .usize_type, .usize_type, .usize_type, .usize_type }, .void, &.{ result, lhs, rhs, .{ .imm32 = ty.bits } });
|
||||
}
|
||||
return result;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3315,26 +3355,43 @@ fn intWrap(cg: *CodeGen, ty: IntType, operand: WValue) InnerError!WValue {
|
||||
},
|
||||
128 => return operand,
|
||||
else => {
|
||||
const bits = mem.alignForward(u16, ty.bits, 64);
|
||||
const bits = cg.intBackingBits(ty.bits);
|
||||
if (ty.bits == bits) return operand;
|
||||
|
||||
const result = try cg.allocInt(ty);
|
||||
|
||||
const len = bits / 8;
|
||||
try cg.memcpy(result, operand, .{ .imm32 = len - 8 });
|
||||
const copy_len = (ty.bits / 64) * 8;
|
||||
try cg.memcpy(result, operand, .{ .imm32 = copy_len });
|
||||
|
||||
try cg.emitWValue(result);
|
||||
_ = try cg.load(operand, Type.u64, len - 8);
|
||||
if (ty.is_signed) {
|
||||
try cg.addImm64(bits - ty.bits);
|
||||
try cg.addTag(.i64_shl);
|
||||
try cg.addImm64(bits - ty.bits);
|
||||
try cg.addTag(.i64_shr_s);
|
||||
} else {
|
||||
try cg.addImm64(~@as(u64, 0) >> @intCast(bits - ty.bits));
|
||||
try cg.addTag(.i64_and);
|
||||
if (ty.bits % 64 != 0) {
|
||||
const pad = 64 - ty.bits % 64;
|
||||
|
||||
try cg.emitWValue(result);
|
||||
_ = try cg.load(operand, Type.u64, copy_len);
|
||||
if (ty.is_signed) {
|
||||
try cg.addImm64(pad);
|
||||
try cg.addTag(.i64_shl);
|
||||
try cg.addImm64(pad);
|
||||
try cg.addTag(.i64_shr_s);
|
||||
} else {
|
||||
try cg.addImm64(~@as(u64, 0) >> @intCast(pad));
|
||||
try cg.addTag(.i64_and);
|
||||
}
|
||||
try cg.store(.stack, .stack, Type.u64, result.offset() + copy_len);
|
||||
}
|
||||
|
||||
const full_len = @divExact(bits, 8);
|
||||
if (copy_len + 16 == full_len) { // last limb needs sign extended
|
||||
try cg.emitWValue(result);
|
||||
if (ty.is_signed) {
|
||||
_ = try cg.load(result, Type.u64, copy_len);
|
||||
try cg.addImm64(63);
|
||||
try cg.addTag(.i64_shr_s);
|
||||
} else {
|
||||
try cg.addImm64(0);
|
||||
}
|
||||
try cg.store(.stack, .stack, Type.u64, result.offset() + copy_len + 8);
|
||||
}
|
||||
try cg.store(.stack, .stack, Type.u64, result.offset() + len - 8);
|
||||
|
||||
return result;
|
||||
},
|
||||
@@ -3354,8 +3411,8 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
|
||||
} else {
|
||||
return .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_ty.bits) };
|
||||
}
|
||||
} else {
|
||||
const result = try cg.allocStack(Type.u128);
|
||||
} else if (int_ty.bits <= 128) {
|
||||
const result = try cg.allocInt(int_ty);
|
||||
try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, 0);
|
||||
|
||||
if (int_ty.is_signed) {
|
||||
@@ -3363,6 +3420,24 @@ fn intMaxValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
|
||||
} else {
|
||||
try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast(128 - int_ty.bits) }, Type.u64, 8);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
const result = try cg.allocInt(int_ty);
|
||||
const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
|
||||
const normal_len = (int_ty.bits / 64) * 8;
|
||||
|
||||
try cg.memset(Type.u8, result, .{ .imm32 = normal_len }, .{ .imm32 = 0xFF });
|
||||
|
||||
if (int_ty.is_signed) {
|
||||
try cg.store(result, .{ .imm64 = (~@as(u64, 0) >> @intCast((normal_len + 8) * 8 - int_ty.bits)) >> 1 }, Type.u64, normal_len);
|
||||
} else {
|
||||
try cg.store(result, .{ .imm64 = ~@as(u64, 0) >> @intCast((normal_len + 8) * 8 - int_ty.bits) }, Type.u64, normal_len);
|
||||
}
|
||||
|
||||
if (normal_len + 16 == full_len) {
|
||||
try cg.store(result, .{ .imm64 = 0 }, Type.u64, full_len - 8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -3375,10 +3450,23 @@ fn intMinValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
|
||||
return .{ .imm32 = ~@as(u32, 0) << @intCast(int_ty.bits - 1) };
|
||||
} else if (int_ty.bits <= 64) {
|
||||
return .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 1) };
|
||||
} else {
|
||||
const result = try cg.allocStack(Type.u128);
|
||||
} else if (int_ty.bits <= 128) {
|
||||
const result = try cg.allocInt(int_ty);
|
||||
try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
|
||||
try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - 65) }, Type.u64, 8);
|
||||
return result;
|
||||
} else {
|
||||
const result = try cg.allocInt(int_ty);
|
||||
const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
|
||||
const normal_len = (int_ty.bits / 64) * 8;
|
||||
|
||||
try cg.memset(Type.u8, result, .{ .imm32 = normal_len }, .{ .imm32 = 0 });
|
||||
try cg.store(result, .{ .imm64 = ~@as(u64, 0) << @intCast(int_ty.bits - normal_len * 8 - 1) }, Type.u64, normal_len);
|
||||
|
||||
if (normal_len + 16 == full_len) {
|
||||
try cg.store(result, .{ .imm64 = ~@as(u64, 0) }, Type.u64, full_len - 8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -3572,12 +3660,17 @@ fn intZeroValue(cg: *CodeGen, int_ty: IntType) InnerError!WValue {
|
||||
1...32 => return .{ .imm32 = 0 },
|
||||
33...64 => return .{ .imm64 = 0 },
|
||||
65...128 => {
|
||||
const result = try cg.allocStack(Type.u128);
|
||||
const result = try cg.allocInt(int_ty);
|
||||
try cg.store(result, .{ .imm64 = 0 }, Type.u64, 0);
|
||||
try cg.store(result, .{ .imm64 = 0 }, Type.u64, 8);
|
||||
return result;
|
||||
},
|
||||
else => return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_ty.bits}),
|
||||
else => {
|
||||
const result = try cg.allocInt(int_ty);
|
||||
const full_len = @divExact(cg.intBackingBits(int_ty.bits), 8);
|
||||
try cg.memset(Type.u8, result, .{ .imm32 = full_len }, .{ .imm32 = 0 });
|
||||
return result;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3757,17 +3850,8 @@ fn intShlOverflow(cg: *CodeGen, ty: IntType, lhs: WValue, rhs: WValue) InnerErro
|
||||
}
|
||||
|
||||
fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue {
|
||||
const src_bits: u16 = switch (src_ty.bits) {
|
||||
0 => unreachable,
|
||||
1...32 => 32,
|
||||
else => mem.alignForward(u16, src_ty.bits, 64),
|
||||
};
|
||||
|
||||
const dest_bits: u16 = switch (dest_ty.bits) {
|
||||
0 => unreachable,
|
||||
1...32 => 32,
|
||||
else => mem.alignForward(u16, dest_ty.bits, 64),
|
||||
};
|
||||
const src_bits: u16 = cg.intBackingBits(src_ty.bits);
|
||||
const dest_bits: u16 = cg.intBackingBits(dest_ty.bits);
|
||||
|
||||
if (src_bits == dest_bits) {
|
||||
return operand;
|
||||
@@ -3859,11 +3943,7 @@ fn intCast(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) Inn
|
||||
fn intTrunc(cg: *CodeGen, dest_ty: IntType, src_ty: IntType, operand: WValue) InnerError!WValue {
|
||||
var result = try cg.intCast(dest_ty, src_ty, operand);
|
||||
|
||||
const dest_wasm_bits: u16 = switch (dest_ty.bits) {
|
||||
0 => unreachable,
|
||||
1...32 => 32,
|
||||
else => mem.alignForward(u16, dest_ty.bits, 64),
|
||||
};
|
||||
const dest_wasm_bits = cg.intBackingBits(dest_ty.bits);
|
||||
|
||||
if (dest_wasm_bits != dest_ty.bits) {
|
||||
result = try cg.intWrap(dest_ty, result);
|
||||
|
||||
@@ -825,6 +825,7 @@ pub const Intrinsic = enum(u32) {
|
||||
__ceilx,
|
||||
__cosh,
|
||||
__cosx,
|
||||
__divei4,
|
||||
__divhf3,
|
||||
__divtf3,
|
||||
__divti3,
|
||||
@@ -950,6 +951,7 @@ pub const Intrinsic = enum(u32) {
|
||||
__lshrti3,
|
||||
__lttf2,
|
||||
__ltxf2,
|
||||
__modei4,
|
||||
__modti3,
|
||||
__mulhf3,
|
||||
__mulodi4,
|
||||
@@ -980,7 +982,9 @@ pub const Intrinsic = enum(u32) {
|
||||
__truncxfdf2,
|
||||
__truncxfhf2,
|
||||
__truncxfsf2,
|
||||
__udivei4,
|
||||
__udivti3,
|
||||
__umodei4,
|
||||
__umodti3,
|
||||
ceilq,
|
||||
cos,
|
||||
|
||||
@@ -1736,6 +1736,112 @@ test "@abs > 128 bits" {
|
||||
try testAbs(i200, minInt(i200), 1 << 199);
|
||||
}
|
||||
|
||||
fn testRem(comptime T: type, numerator: T, denominator: T, expected: T) !void {
|
||||
try expect(@rem(numerator, denominator) == expected);
|
||||
}
|
||||
|
||||
test "@rem > 128 bits" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
try testRem(u140, 0, maxInt(u140), 0);
|
||||
try testRem(u140, maxInt(u140), maxInt(u140), 0);
|
||||
try testRem(u140, maxInt(u140), 2, 1);
|
||||
try testRem(u140, (1 << 139) + 5, 1 << 70, 5);
|
||||
try testRem(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, 7);
|
||||
try testRem(u200, 123, 1 << 100, 123);
|
||||
try testRem(u200, 1 << 120, 1 << 60, 0);
|
||||
try testRem(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
|
||||
|
||||
try testRem(i140, 0, maxInt(i140), 0);
|
||||
try testRem(i140, maxInt(i140), maxInt(i140), 0);
|
||||
try testRem(i140, -((1 << 100) + 1), 1 << 50, -1);
|
||||
try testRem(i140, (1 << 100) + 1, -(1 << 50), 1);
|
||||
try testRem(i140, -((1 << 100) + 1), -(1 << 50), -1);
|
||||
try testRem(i200, minInt(i200), 1, 0);
|
||||
try testRem(i200, minInt(i200), -2, 0);
|
||||
try testRem(i200, maxInt(i200), 2, 1);
|
||||
}
|
||||
|
||||
fn testMod(comptime T: type, numerator: T, denominator: T, expected: T) !void {
|
||||
try expect(@mod(numerator, denominator) == expected);
|
||||
}
|
||||
|
||||
test "@mod > 128 bits" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
try testMod(u140, 0, maxInt(u140), 0);
|
||||
try testMod(u140, maxInt(u140), maxInt(u140), 0);
|
||||
try testMod(u140, maxInt(u140), 2, 1);
|
||||
try testMod(u140, (1 << 139) + 5, 1 << 70, 5);
|
||||
try testMod(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, 7);
|
||||
try testMod(u200, 123, 1 << 100, 123);
|
||||
try testMod(u200, 1 << 120, 1 << 60, 0);
|
||||
try testMod(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
|
||||
|
||||
try testMod(i140, 0, maxInt(i140), 0);
|
||||
try testMod(i140, maxInt(i140), maxInt(i140), 0);
|
||||
try testMod(i140, -((1 << 100) + 1), 1 << 50, (1 << 50) - 1);
|
||||
try testMod(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50) + 1);
|
||||
try testMod(i140, -((1 << 100) + 1), -(1 << 50), -1);
|
||||
try testMod(i200, minInt(i200), 1, 0);
|
||||
try testMod(i200, minInt(i200), -2, 0);
|
||||
try testMod(i200, maxInt(i200), 2, 1);
|
||||
}
|
||||
|
||||
fn testDivFloor(comptime T: type, numerator: T, denominator: T, expected: T) !void {
|
||||
try expect(@divFloor(numerator, denominator) == expected);
|
||||
}
|
||||
|
||||
test "@divFloor > 128 bits" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
try testDivFloor(u140, 0, maxInt(u140), 0);
|
||||
try testDivFloor(u140, maxInt(u140), maxInt(u140), 1);
|
||||
try testDivFloor(u140, maxInt(u140), 2, maxInt(u140) >> 1);
|
||||
try testDivFloor(u140, (1 << 139) + 5, 1 << 70, 1 << 69);
|
||||
try testDivFloor(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 1);
|
||||
try testDivFloor(u200, 123, 1 << 100, 0);
|
||||
try testDivFloor(u200, 1 << 120, 1 << 60, 1 << 60);
|
||||
try testDivFloor(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
|
||||
|
||||
try testDivFloor(i140, 0, maxInt(i140), 0);
|
||||
try testDivFloor(i140, maxInt(i140), maxInt(i140), 1);
|
||||
try testDivFloor(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50) - 1);
|
||||
try testDivFloor(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50) - 1);
|
||||
try testDivFloor(i140, -((1 << 100) + 1), -(1 << 50), 1 << 50);
|
||||
try testDivFloor(i200, -3, 2, -2);
|
||||
try testDivFloor(i200, minInt(i200), 1, minInt(i200));
|
||||
try testDivFloor(i200, minInt(i200), -2, 1 << 198);
|
||||
try testDivFloor(i200, maxInt(i200), 2, (1 << 198) - 1);
|
||||
}
|
||||
|
||||
fn testDivTrunc(comptime T: type, numerator: T, denominator: T, expected: T) !void {
|
||||
try expect(@divTrunc(numerator, denominator) == expected);
|
||||
}
|
||||
|
||||
test "@divTrunc > 128 bits" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
try testDivTrunc(u140, 0, maxInt(u140), 0);
|
||||
try testDivTrunc(u140, maxInt(u140), maxInt(u140), 1);
|
||||
try testDivTrunc(u140, maxInt(u140), 2, maxInt(u140) >> 1);
|
||||
try testDivTrunc(u140, (1 << 139) + 5, 1 << 70, 1 << 69);
|
||||
try testDivTrunc(u140, (1 << 100) + (1 << 50) + 7, 1 << 50, (1 << 50) + 1);
|
||||
try testDivTrunc(u200, 123, 1 << 100, 0);
|
||||
try testDivTrunc(u200, 1 << 120, 1 << 60, 1 << 60);
|
||||
try testDivTrunc(u200, maxInt(u200), 1 << 100, (1 << 100) - 1);
|
||||
|
||||
try testDivTrunc(i140, 0, maxInt(i140), 0);
|
||||
try testDivTrunc(i140, maxInt(i140), maxInt(i140), 1);
|
||||
try testDivTrunc(i140, -((1 << 100) + 1), 1 << 50, -(1 << 50));
|
||||
try testDivTrunc(i140, (1 << 100) + 1, -(1 << 50), -(1 << 50));
|
||||
try testDivTrunc(i140, -((1 << 100) + 1), -(1 << 50), 1 << 50);
|
||||
try testDivTrunc(i200, -3, 2, -1);
|
||||
try testDivTrunc(i200, minInt(i200), 1, minInt(i200));
|
||||
try testDivTrunc(i200, minInt(i200), -2, 1 << 198);
|
||||
try testDivTrunc(i200, maxInt(i200), 2, (1 << 198) - 1);
|
||||
}
|
||||
|
||||
test "overflow arithmetic with u0 values" {
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user