diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 53786c8661..d352ac29dc 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -767,8 +767,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn // Until self-hosted catches up with stage1 language features, we have a simpler // default panic function: - if ((builtin.zig_backend == .stage2_llvm and builtin.link_libc) or - builtin.zig_backend == .stage2_c or + if (builtin.zig_backend == .stage2_c or builtin.zig_backend == .stage2_wasm or builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_aarch64 or diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 44b616c493..998271cd7f 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1766,10 +1766,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul_add => try airMulAdd(f, inst), - .add_with_overflow => try airAddWithOverflow(f, inst), - .sub_with_overflow => try airSubWithOverflow(f, inst), - .mul_with_overflow => try airMulWithOverflow(f, inst), - .shl_with_overflow => try airShlWithOverflow(f, inst), + .add_with_overflow => try airOverflow(f, inst, "addo_"), + .sub_with_overflow => try airOverflow(f, inst, "subo_"), + .mul_with_overflow => try airOverflow(f, inst, "mulo_"), + .shl_with_overflow => try airOverflow(f, inst, "shlo_"), .min => try airMinMax(f, inst, "<"), .max => try airMinMax(f, inst, ">"), @@ -2295,7 +2295,8 @@ fn airWrapOp( const bin_op = f.air.instructions.items(.data)[inst].bin_op; const inst_ty = f.air.typeOfIndex(inst); - const int_info = inst_ty.intInfo(f.object.dg.module.getTarget()); + const target = f.object.dg.module.getTarget(); + const int_info = inst_ty.intInfo(target); const bits = int_info.bits; // if it's an unsigned int with non-arbitrary bit size then we can just add @@ -2313,47 +2314,8 @@ fn airWrapOp( return f.fail("TODO: C backend: airWrapOp for large integers", .{}); } - var min_buf: [80]u8 = undefined; - const min = switch (int_info.signedness) { - .unsigned => "0", - else => switch (inst_ty.tag()) { - .c_short => "SHRT_MIN", - .c_int => "INT_MIN", - .c_long => "LONG_MIN", - .c_longlong => "LLONG_MIN", - .isize => "INTPTR_MIN", - else => blk: { - const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1)); - break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, - }, - }; - var max_buf: [80]u8 = undefined; - const max = switch (inst_ty.tag()) { - .c_short => "SHRT_MAX", - .c_ushort => "USHRT_MAX", - .c_int => "INT_MAX", - .c_uint => "UINT_MAX", - .c_long => "LONG_MAX", - .c_ulong => "ULONG_MAX", - .c_longlong => "LLONG_MAX", - .c_ulonglong => "ULLONG_MAX", - .isize => "INTPTR_MAX", - .usize => "UINTPTR_MAX", - else => blk: { - const pow_bits = switch (int_info.signedness) { - .signed => bits - 1, - .unsigned => bits, - }; - const val = std.math.pow(u64, 2, pow_bits) - 1; - break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - }; - }, - }; + const max = intMax(inst_ty, target, &max_buf); const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -2369,10 +2331,7 @@ fn airWrapOp( .c_long => try w.writeAll("long"), .c_longlong => try w.writeAll("longlong"), else => { - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; + const prefix_byte: u8 = signAbbrev(int_info.signedness); for ([_]u8{ 8, 16, 32, 64 }) |nbits| { if (bits <= nbits) { try w.print("{c}{d}", .{ prefix_byte, nbits }); @@ -2390,6 +2349,9 @@ fn airWrapOp( try f.writeCValue(w, rhs); if (int_info.signedness == .signed) { + var min_buf: [80]u8 = undefined; + const min = intMin(inst_ty, target, &min_buf); + try w.print(", {s}", .{min}); } @@ -2475,10 +2437,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { .c_long => try w.writeAll("long"), .c_longlong => try w.writeAll("longlong"), else => { - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; + const prefix_byte: u8 = signAbbrev(int_info.signedness); for ([_]u8{ 8, 16, 32, 64 }) |nbits| { if (bits <= nbits) { try w.print("{c}{d}", .{ prefix_byte, nbits }); @@ -2505,28 +2464,63 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue { return ret; } -fn airAddWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO add with overflow", .{}); -} +fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CValue { + if (f.liveness.isUnused(inst)) + return CValue.none; -fn airSubWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO sub with overflow", .{}); -} + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; -fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO mul with overflow", .{}); -} + const lhs = try f.resolveInst(bin_op.lhs); + const rhs = try f.resolveInst(bin_op.rhs); -fn airShlWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue { - _ = f; - _ = inst; - return f.fail("TODO shl with overflow", .{}); + const inst_ty = f.air.typeOfIndex(inst); + const scalar_ty = f.air.typeOf(bin_op.lhs).scalarType(); + const target = f.object.dg.module.getTarget(); + const int_info = scalar_ty.intInfo(target); + const w = f.object.writer(); + const c_bits = toCIntBits(int_info.bits) orelse + return f.fail("TODO: C backend: implement integer arithmetic larger than 128 bits", .{}); + + var max_buf: [80]u8 = undefined; + const max = intMax(scalar_ty, target, &max_buf); + + const ret = try f.allocLocal(inst_ty, .Mut); + try w.writeAll(";"); + try f.object.indent_writer.insertNewline(); + try f.writeCValue(w, ret); + + switch (int_info.signedness) { + .unsigned => { + try w.print(".field_1 = zig_{s}u{d}(", .{ + op_abbrev, c_bits, + }); + try f.writeCValue(w, lhs); + try w.writeAll(", "); + try f.writeCValue(w, rhs); + try w.writeAll(", &"); + try f.writeCValue(w, ret); + try w.print(".field_0, {s}", .{max}); + }, + .signed => { + var min_buf: [80]u8 = undefined; + const min = intMin(scalar_ty, target, &min_buf); + + try w.print(".field_1 = zig_{s}i{d}(", .{ + op_abbrev, c_bits, + }); + try f.writeCValue(w, lhs); + try w.writeAll(", "); + try f.writeCValue(w, rhs); + try w.writeAll(", &"); + try f.writeCValue(w, ret); + try w.print(".field_0, {s}, {s}", .{ min, max }); + }, + } + + try w.writeAll(");"); + try f.object.indent_writer.insertNewline(); + return ret; } fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3571,11 +3565,7 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); try writer.print(" = zig_{s}_", .{fn_name}); - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; - try writer.print("{c}{d}(", .{ prefix_byte, c_bits }); + try writer.print("{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); try f.writeCValue(writer, try f.resolveInst(operand)); try writer.print(", {d});\n", .{int_info.bits}); return local; @@ -3596,11 +3586,7 @@ fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u const int_info = lhs_ty.intInfo(target); const c_bits = toCIntBits(int_info.bits) orelse return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); - const prefix_byte: u8 = switch (int_info.signedness) { - .signed => 'i', - .unsigned => 'u', - }; - try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits }); + try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, signAbbrev(int_info.signedness), c_bits }); } else if (lhs_ty.isRuntimeFloat()) { const c_bits = lhs_ty.floatBits(target); try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits }); @@ -4085,3 +4071,53 @@ fn toCIntBits(zig_bits: u32) ?u32 { } return null; } + +fn signAbbrev(signedness: std.builtin.Signedness) u8 { + return switch (signedness) { + .signed => 'i', + .unsigned => 'u', + }; +} + +fn intMax(ty: Type, target: std.Target, buf: []u8) []const u8 { + switch (ty.tag()) { + .c_short => return "SHRT_MAX", + .c_ushort => return "USHRT_MAX", + .c_int => return "INT_MAX", + .c_uint => return "UINT_MAX", + .c_long => return "LONG_MAX", + .c_ulong => return "ULONG_MAX", + .c_longlong => return "LLONG_MAX", + .c_ulonglong => return "ULLONG_MAX", + else => { + const int_info = ty.intInfo(target); + const rhs = @intCast(u7, int_info.bits - @boolToInt(int_info.signedness == .signed)); + const val = (@as(u128, 1) << rhs) - 1; + // TODO make this integer literal have a suffix if necessary (such as "ull") + return std.fmt.bufPrint(buf, "{}", .{val}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + }; + }, + } +} + +fn intMin(ty: Type, target: std.Target, buf: []u8) []const u8 { + switch (ty.tag()) { + .c_short => return "SHRT_MIN", + .c_int => return "INT_MIN", + .c_long => return "LONG_MIN", + .c_longlong => return "LLONG_MIN", + else => { + const int_info = ty.intInfo(target); + assert(int_info.signedness == .signed); + const val = v: { + if (int_info.bits == 0) break :v 0; + const rhs = @intCast(u7, (int_info.bits - 1)); + break :v -(@as(i128, 1) << rhs); + }; + return std.fmt.bufPrint(buf, "{d}", .{val}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + }; + }, + } +} diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 85c7856d2b..6bafee987b 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -165,8 +165,24 @@ #define int128_t __int128 #define uint128_t unsigned __int128 +#define UINT128_MAX ((uint128_t)(0xffffffffffffffffull) | 0xffffffffffffffffull) ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); ZIG_EXTERN_C void *memset (void *, int, size_t); +ZIG_EXTERN_C int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); +ZIG_EXTERN_C int128_t __addoti4(int128_t lhs, int128_t rhs, int *overflow); +ZIG_EXTERN_C uint64_t __uaddodi4(uint64_t lhs, uint64_t rhs, int *overflow); +ZIG_EXTERN_C uint128_t __uaddoti4(uint128_t lhs, uint128_t rhs, int *overflow); +ZIG_EXTERN_C int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); +ZIG_EXTERN_C int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); +ZIG_EXTERN_C int128_t __suboti4(int128_t lhs, int128_t rhs, int *overflow); +ZIG_EXTERN_C uint32_t __usubosi4(uint32_t lhs, uint32_t rhs, int *overflow); +ZIG_EXTERN_C uint64_t __usubodi4(uint64_t lhs, uint64_t rhs, int *overflow); +ZIG_EXTERN_C uint128_t __usuboti4(uint128_t lhs, uint128_t rhs, int *overflow); +ZIG_EXTERN_C int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); +ZIG_EXTERN_C int128_t __muloti4(int128_t lhs, int128_t rhs, int *overflow); +ZIG_EXTERN_C uint64_t __umulodi4(uint64_t lhs, uint64_t rhs, int *overflow); +ZIG_EXTERN_C uint128_t __umuloti4(uint128_t lhs, uint128_t rhs, int *overflow); + static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { uint8_t thresh = max - rhs; @@ -396,6 +412,811 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); } +static inline bool zig_addo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { +#if defined(__GNUC__) && INT8_MAX == INT_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_sadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_saddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_saddll_overflow(lhs, rhs, res); + } +#endif + int16_t big_result = (int16_t)lhs + (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_addo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { +#if defined(__GNUC__) && INT16_MAX == INT_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_sadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_saddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_saddll_overflow(lhs, rhs, res); + } +#endif + int32_t big_result = (int32_t)lhs + (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_addo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { +#if defined(__GNUC__) && INT32_MAX == INT_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_sadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_saddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_saddll_overflow(lhs, rhs, res); + } +#endif + int64_t big_result = (int64_t)lhs + (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_addo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + bool overflow; +#if defined(__GNUC__) && INT64_MAX == INT_MAX + overflow = __builtin_sadd_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LONG_MAX + overflow = __builtin_saddl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX + overflow = __builtin_saddll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __addodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_addo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + bool overflow; +#if defined(__GNUC__) && INT128_MAX == INT_MAX + overflow = __builtin_sadd_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LONG_MAX + overflow = __builtin_saddl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX + overflow = __builtin_saddll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __addoti4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_addo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { +#if defined(__GNUC__) && UINT8_MAX == UINT_MAX + if (max == UINT8_MAX) { + return __builtin_uadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX + if (max == UINT8_MAX) { + return __builtin_uaddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX + if (max == UINT8_MAX) { + return __builtin_uaddll_overflow(lhs, rhs, res); + } +#endif + uint16_t big_result = (uint16_t)lhs + (uint16_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint16_t zig_addo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { +#if defined(__GNUC__) && UINT16_MAX == UINT_MAX + if (max == UINT16_MAX) { + return __builtin_uadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX + if (max == UINT16_MAX) { + return __builtin_uaddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX + if (max == UINT16_MAX) { + return __builtin_uaddll_overflow(lhs, rhs, res); + } +#endif + uint32_t big_result = (uint32_t)lhs + (uint32_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint32_t zig_addo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { +#if defined(__GNUC__) && UINT32_MAX == UINT_MAX + if (max == UINT32_MAX) { + return __builtin_uadd_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX + if (max == UINT32_MAX) { + return __builtin_uaddl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX + if (max == UINT32_MAX) { + return __builtin_uaddll_overflow(lhs, rhs, res); + } +#endif + uint64_t big_result = (uint64_t)lhs + (uint64_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint64_t zig_addo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + bool overflow; +#if defined(__GNUC__) && UINT64_MAX == UINT_MAX + overflow = __builtin_uadd_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX + overflow = __builtin_uaddl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX + overflow = __builtin_uaddll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __uaddodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (*res > max && !overflow) { + *res -= max - 1; + return true; + } + return overflow; +} + +static inline uint128_t zig_addo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + bool overflow; + *res = __uaddoti4(lhs, rhs, &overflow); + if (*res > max && !overflow) { + *res -= max - 1; + return true; + } + return overflow; +} + +static inline bool zig_subo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { +#if defined(__GNUC__) && INT8_MAX == INT_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_ssub_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_ssubl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_ssubll_overflow(lhs, rhs, res); + } +#endif + int16_t big_result = (int16_t)lhs - (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_subo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { +#if defined(__GNUC__) && INT16_MAX == INT_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_ssub_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_ssubl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_ssubll_overflow(lhs, rhs, res); + } +#endif + int32_t big_result = (int32_t)lhs - (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_subo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { +#if defined(__GNUC__) && INT32_MAX == INT_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_ssub_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_ssubl_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_ssubll_overflow(lhs, rhs, res); + } +#endif + int64_t big_result = (int64_t)lhs - (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_subo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + bool overflow; +#if defined(__GNUC__) && INT64_MAX == INT_MAX + overflow = __builtin_ssub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LONG_MAX + overflow = __builtin_ssubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX + overflow = __builtin_ssubll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __subodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_subo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + bool overflow; +#if defined(__GNUC__) && INT128_MAX == INT_MAX + overflow = __builtin_ssub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LONG_MAX + overflow = __builtin_ssubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX + overflow = __builtin_ssubll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __suboti4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_subo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { +#if defined(__GNUC__) && UINT8_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#endif + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; +} + +static inline uint16_t zig_subo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { +#if defined(__GNUC__) && UINT16_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#endif + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; +} + +static inline uint32_t zig_subo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { + if (max == UINT32_MAX) { +#if defined(__GNUC__) && UINT32_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#endif + int int_overflow; + *res = __usubosi4(lhs, rhs, &int_overflow); + return int_overflow != 0; + } else { + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; + } +} + +static inline uint64_t zig_subo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + if (max == UINT64_MAX) { +#if defined(__GNUC__) && UINT64_MAX == UINT_MAX + return __builtin_usub_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX + return __builtin_usubl_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX + return __builtin_usubll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __usubodi4(lhs, rhs, &int_overflow); + return int_overflow != 0; +#endif + } else { + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; + } +} + +static inline uint128_t zig_subo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + if (max == UINT128_MAX) { + int int_overflow; + *res = __usuboti4(lhs, rhs, &int_overflow); + return int_overflow != 0; + } else { + if (rhs > lhs) { + *res = max - (rhs - lhs - 1); + return true; + } + *res = lhs - rhs; + return false; + } +} + +static inline bool zig_mulo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { +#if defined(__GNUC__) && INT8_MAX == INT_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_smul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_smull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT8_MAX == LLONG_MAX + if (min == INT8_MIN && max == INT8_MAX) { + return __builtin_smulll_overflow(lhs, rhs, res); + } +#endif + int16_t big_result = (int16_t)lhs * (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_mulo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { +#if defined(__GNUC__) && INT16_MAX == INT_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_smul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_smull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT16_MAX == LLONG_MAX + if (min == INT16_MIN && max == INT16_MAX) { + return __builtin_smulll_overflow(lhs, rhs, res); + } +#endif + int32_t big_result = (int32_t)lhs * (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_mulo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { +#if defined(__GNUC__) && INT32_MAX == INT_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_smul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_smull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && INT32_MAX == LLONG_MAX + if (min == INT32_MIN && max == INT32_MAX) { + return __builtin_smulll_overflow(lhs, rhs, res); + } +#endif + int64_t big_result = (int64_t)lhs * (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_mulo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + bool overflow; +#if defined(__GNUC__) && INT64_MAX == INT_MAX + overflow = __builtin_smul_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LONG_MAX + overflow = __builtin_smull_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT64_MAX == LLONG_MAX + overflow = __builtin_smulll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __mulodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_mulo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + bool overflow; +#if defined(__GNUC__) && INT128_MAX == INT_MAX + overflow = __builtin_smul_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LONG_MAX + overflow = __builtin_smull_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && INT128_MAX == LLONG_MAX + overflow = __builtin_smulll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __muloti4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (!overflow) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow; +} + +static inline bool zig_mulo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { +#if defined(__GNUC__) && UINT8_MAX == UINT_MAX + if (max == UINT8_MAX) { + return __builtin_umul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULONG_MAX + if (max == UINT8_MAX) { + return __builtin_umull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT8_MAX == ULLONG_MAX + if (max == UINT8_MAX) { + return __builtin_umulll_overflow(lhs, rhs, res); + } +#endif + uint16_t big_result = (uint16_t)lhs * (uint16_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint16_t zig_mulo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { +#if defined(__GNUC__) && UINT16_MAX == UINT_MAX + if (max == UINT16_MAX) { + return __builtin_umul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULONG_MAX + if (max == UINT16_MAX) { + return __builtin_umull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT16_MAX == ULLONG_MAX + if (max == UINT16_MAX) { + return __builtin_umulll_overflow(lhs, rhs, res); + } +#endif + uint32_t big_result = (uint32_t)lhs * (uint32_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint32_t zig_mulo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { +#if defined(__GNUC__) && UINT32_MAX == UINT_MAX + if (max == UINT32_MAX) { + return __builtin_umul_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULONG_MAX + if (max == UINT32_MAX) { + return __builtin_umull_overflow(lhs, rhs, res); + } +#elif defined(__GNUC__) && UINT32_MAX == ULLONG_MAX + if (max == UINT32_MAX) { + return __builtin_umulll_overflow(lhs, rhs, res); + } +#endif + uint64_t big_result = (uint64_t)lhs * (uint64_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint64_t zig_mulo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + bool overflow; +#if defined(__GNUC__) && UINT64_MAX == UINT_MAX + overflow = __builtin_umul_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULONG_MAX + overflow = __builtin_umull_overflow(lhs, rhs, res); +#elif defined(__GNUC__) && UINT64_MAX == ULLONG_MAX + overflow = __builtin_umulll_overflow(lhs, rhs, res); +#else + int int_overflow; + *res = __umulodi4(lhs, rhs, &int_overflow); + overflow = int_overflow != 0; +#endif + if (*res > max && !overflow) { + *res -= max - 1; + return true; + } + return overflow; +} + +static inline uint128_t zig_mulo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + int overflow; + *res = __umuloti4(lhs, rhs, &overflow); + if (*res > max && overflow == 0) { + *res -= max - 1; + return true; + } + return overflow != 0; +} + +static inline bool zig_shlo_i8(int8_t lhs, int8_t rhs, int8_t *res, int8_t min, int8_t max) { + int16_t big_result = (int16_t)lhs << (int16_t)rhs; + if (big_result > max) { + *res = big_result - ((int16_t)max - (int16_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int16_t)max - (int16_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_shlo_i16(int16_t lhs, int16_t rhs, int16_t *res, int16_t min, int16_t max) { + int32_t big_result = (int32_t)lhs << (int32_t)rhs; + if (big_result > max) { + *res = big_result - ((int32_t)max - (int32_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int32_t)max - (int32_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_shlo_i32(int32_t lhs, int32_t rhs, int32_t *res, int32_t min, int32_t max) { + int64_t big_result = (int64_t)lhs << (int64_t)rhs; + if (big_result > max) { + *res = big_result - ((int64_t)max - (int64_t)min); + return true; + } + if (big_result < min) { + *res = big_result + ((int64_t)max - (int64_t)min); + return true; + } + *res = big_result; + return false; +} + +static inline bool zig_shlo_i64(int64_t lhs, int64_t rhs, int64_t *res, int64_t min, int64_t max) { + int overflow; + *res = __shlodi4(lhs, rhs, &overflow); + if (overflow == 0) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow != 0; +} + +static inline bool zig_shlo_i128(int128_t lhs, int128_t rhs, int128_t *res, int128_t min, int128_t max) { + int overflow; + *res = __shloti4(lhs, rhs, &overflow); + if (overflow == 0) { + if (*res > max) { + // TODO adjust the result to be the truncated bits + return true; + } else if (*res < min) { + // TODO adjust the result to be the truncated bits + return true; + } + } + return overflow != 0; +} + +static inline bool zig_shlo_u8(uint8_t lhs, uint8_t rhs, uint8_t *res, uint8_t max) { + uint16_t big_result = (uint16_t)lhs << (uint16_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint16_t zig_shlo_u16(uint16_t lhs, uint16_t rhs, uint16_t *res, uint16_t max) { + uint32_t big_result = (uint32_t)lhs << (uint32_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint32_t zig_shlo_u32(uint32_t lhs, uint32_t rhs, uint32_t *res, uint32_t max) { + uint64_t big_result = (uint64_t)lhs << (uint64_t)rhs; + if (big_result > max) { + *res = big_result - max - 1; + return true; + } + *res = big_result; + return false; +} + +static inline uint64_t zig_shlo_u64(uint64_t lhs, uint64_t rhs, uint64_t *res, uint64_t max) { + int overflow; + *res = __ushlodi4(lhs, rhs, &overflow); + if (*res > max && overflow == 0) { + *res -= max - 1; + return true; + } + return overflow != 0; +} + +static inline uint128_t zig_shlo_u128(uint128_t lhs, uint128_t rhs, uint128_t *res, uint128_t max) { + int overflow; + *res = __ushloti4(lhs, rhs, &overflow); + if (*res > max && overflow == 0) { + *res -= max - 1; + return true; + } + return overflow != 0; +} + static inline float zig_bitcast_f32_u32(uint32_t arg) { float dest; memcpy(&dest, &arg, sizeof dest);