mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 00:08:56 +00:00
Merge pull request #8494 from mattnite/cbe-wrapping-addition
Wrapping add/sub for the C backend
This commit is contained in:
commit
7bdeda82ae
@ -846,18 +846,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
|
||||
// TODO use a different strategy for add that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.add => try genBinOp(o, inst.castTag(.add).?, " + "),
|
||||
// TODO make this do wrapping arithmetic for signed ints
|
||||
.addwrap => try genBinOp(o, inst.castTag(.add).?, " + "),
|
||||
.addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"),
|
||||
// TODO use a different strategy for sub that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
|
||||
// TODO make this do wrapping arithmetic for signed ints
|
||||
.subwrap => try genBinOp(o, inst.castTag(.sub).?, " - "),
|
||||
.subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"),
|
||||
// TODO use a different strategy for mul that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.mul => try genBinOp(o, inst.castTag(.sub).?, " * "),
|
||||
// TODO make this do wrapping multiplication for signed ints
|
||||
.mulwrap => try genBinOp(o, inst.castTag(.sub).?, " * "),
|
||||
.mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"),
|
||||
// TODO use a different strategy for div that communicates to the optimizer
|
||||
// that wrapping is UB.
|
||||
.div => try genBinOp(o, inst.castTag(.div).?, " / "),
|
||||
@ -1042,7 +1039,117 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
|
||||
return CValue.none;
|
||||
}
|
||||
|
||||
fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
|
||||
fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue {
|
||||
if (inst.base.isUnused())
|
||||
return CValue.none;
|
||||
|
||||
const int_info = inst.base.ty.intInfo(o.dg.module.getTarget());
|
||||
const bits = int_info.bits;
|
||||
|
||||
// if it's an unsigned int with non-arbitrary bit size then we can just add
|
||||
if (int_info.signedness == .unsigned) {
|
||||
const ok_bits = switch (bits) {
|
||||
8, 16, 32, 64, 128 => true,
|
||||
else => false,
|
||||
};
|
||||
if (ok_bits or inst.base.ty.tag() != .int_unsigned) {
|
||||
return try genBinOp(o, inst, str_op);
|
||||
}
|
||||
}
|
||||
|
||||
if (bits > 64) {
|
||||
return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: genWrapOp for large integers", .{});
|
||||
}
|
||||
|
||||
var min_buf: [80]u8 = undefined;
|
||||
const min = switch (int_info.signedness) {
|
||||
.unsigned => "0",
|
||||
else => switch (inst.base.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,
|
||||
else => |e| return e,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var max_buf: [80]u8 = undefined;
|
||||
const max = switch (inst.base.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,
|
||||
else => |e| return e,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const lhs = try o.resolveInst(inst.lhs);
|
||||
const rhs = try o.resolveInst(inst.rhs);
|
||||
const w = o.writer();
|
||||
|
||||
const ret = try o.allocLocal(inst.base.ty, .Mut);
|
||||
try w.print(" = zig_{s}", .{fn_op});
|
||||
|
||||
switch (inst.base.ty.tag()) {
|
||||
.isize => try w.writeAll("isize"),
|
||||
.c_short => try w.writeAll("short"),
|
||||
.c_int => try w.writeAll("int"),
|
||||
.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',
|
||||
};
|
||||
for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
|
||||
if (bits <= nbits) {
|
||||
try w.print("{c}{d}", .{ prefix_byte, nbits });
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
try w.writeByte('(');
|
||||
try o.writeCValue(w, lhs);
|
||||
try w.writeAll(", ");
|
||||
try o.writeCValue(w, rhs);
|
||||
|
||||
if (int_info.signedness == .signed) {
|
||||
try w.print(", {s}", .{min});
|
||||
}
|
||||
|
||||
try w.print(", {s});", .{max});
|
||||
try o.indent_writer.insertNewline();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {
|
||||
if (inst.base.isUnused())
|
||||
return CValue.none;
|
||||
|
||||
@ -1054,7 +1161,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
|
||||
|
||||
try writer.writeAll(" = ");
|
||||
try o.writeCValue(writer, lhs);
|
||||
try writer.writeAll(operator);
|
||||
try writer.print("{s}", .{operator});
|
||||
try o.writeCValue(writer, rhs);
|
||||
try writer.writeAll(";\n");
|
||||
|
||||
|
||||
229
src/link/C/zig.h
229
src/link/C/zig.h
@ -62,7 +62,236 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <limits.h>
|
||||
#define int128_t __int128
|
||||
#define uint128_t unsigned __int128
|
||||
ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
|
||||
|
||||
static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
|
||||
uint8_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return lhs - thresh - 1;
|
||||
} else {
|
||||
return lhs + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int8_t zig_addw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {
|
||||
if ((lhs > 0) && (rhs > 0)) {
|
||||
int8_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return min + lhs - thresh - 1;
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs < 0)) {
|
||||
int8_t thresh = min - rhs;
|
||||
if (lhs < thresh) {
|
||||
return max + lhs - thresh + 1;
|
||||
}
|
||||
}
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
static inline uint16_t zig_addw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {
|
||||
uint16_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return lhs - thresh - 1;
|
||||
} else {
|
||||
return lhs + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int16_t zig_addw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {
|
||||
if ((lhs > 0) && (rhs > 0)) {
|
||||
int16_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return min + lhs - thresh - 1;
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs < 0)) {
|
||||
int16_t thresh = min - rhs;
|
||||
if (lhs < thresh) {
|
||||
return max + lhs - thresh + 1;
|
||||
}
|
||||
}
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
static inline uint32_t zig_addw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {
|
||||
uint32_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return lhs - thresh - 1;
|
||||
} else {
|
||||
return lhs + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int32_t zig_addw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {
|
||||
if ((lhs > 0) && (rhs > 0)) {
|
||||
int32_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return min + lhs - thresh - 1;
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs < 0)) {
|
||||
int32_t thresh = min - rhs;
|
||||
if (lhs < thresh) {
|
||||
return max + lhs - thresh + 1;
|
||||
}
|
||||
}
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
static inline uint64_t zig_addw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {
|
||||
uint64_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return lhs - thresh - 1;
|
||||
} else {
|
||||
return lhs + rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int64_t zig_addw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {
|
||||
if ((lhs > 0) && (rhs > 0)) {
|
||||
int64_t thresh = max - rhs;
|
||||
if (lhs > thresh) {
|
||||
return min + lhs - thresh - 1;
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs < 0)) {
|
||||
int64_t thresh = min - rhs;
|
||||
if (lhs < thresh) {
|
||||
return max + lhs - thresh + 1;
|
||||
}
|
||||
}
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
static inline intptr_t zig_addw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {
|
||||
return (intptr_t)(((uintptr_t)lhs) + ((uintptr_t)rhs));
|
||||
}
|
||||
|
||||
static inline short zig_addw_short(short lhs, short rhs, short min, short max) {
|
||||
return (short)(((unsigned short)lhs) + ((unsigned short)rhs));
|
||||
}
|
||||
|
||||
static inline int zig_addw_int(int lhs, int rhs, int min, int max) {
|
||||
return (int)(((unsigned)lhs) + ((unsigned)rhs));
|
||||
}
|
||||
|
||||
static inline long zig_addw_long(long lhs, long rhs, long min, long max) {
|
||||
return (long)(((unsigned long)lhs) + ((unsigned long)rhs));
|
||||
}
|
||||
|
||||
static inline long long zig_addw_longlong(long long lhs, long long rhs, long long min, long long max) {
|
||||
return (long long)(((unsigned long long)lhs) + ((unsigned long long)rhs));
|
||||
}
|
||||
|
||||
static inline uint8_t zig_subw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
|
||||
if (lhs < rhs) {
|
||||
return max - rhs - lhs + 1;
|
||||
} else {
|
||||
return lhs - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int8_t zig_subw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {
|
||||
if ((lhs > 0) && (rhs < 0)) {
|
||||
int8_t thresh = lhs - max;
|
||||
if (rhs < thresh) {
|
||||
return min + (thresh - rhs - 1);
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs > 0)) {
|
||||
int8_t thresh = lhs - min;
|
||||
if (rhs > thresh) {
|
||||
return max - (rhs - thresh - 1);
|
||||
}
|
||||
}
|
||||
return lhs - rhs;
|
||||
}
|
||||
|
||||
static inline uint16_t zig_subw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {
|
||||
if (lhs < rhs) {
|
||||
return max - rhs - lhs + 1;
|
||||
} else {
|
||||
return lhs - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int16_t zig_subw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {
|
||||
if ((lhs > 0) && (rhs < 0)) {
|
||||
int16_t thresh = lhs - max;
|
||||
if (rhs < thresh) {
|
||||
return min + (thresh - rhs - 1);
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs > 0)) {
|
||||
int16_t thresh = lhs - min;
|
||||
if (rhs > thresh) {
|
||||
return max - (rhs - thresh - 1);
|
||||
}
|
||||
}
|
||||
return lhs - rhs;
|
||||
}
|
||||
|
||||
static inline uint32_t zig_subw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {
|
||||
if (lhs < rhs) {
|
||||
return max - rhs - lhs + 1;
|
||||
} else {
|
||||
return lhs - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int32_t zig_subw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {
|
||||
if ((lhs > 0) && (rhs < 0)) {
|
||||
int32_t thresh = lhs - max;
|
||||
if (rhs < thresh) {
|
||||
return min + (thresh - rhs - 1);
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs > 0)) {
|
||||
int32_t thresh = lhs - min;
|
||||
if (rhs > thresh) {
|
||||
return max - (rhs - thresh - 1);
|
||||
}
|
||||
}
|
||||
return lhs - rhs;
|
||||
}
|
||||
|
||||
static inline uint64_t zig_subw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {
|
||||
if (lhs < rhs) {
|
||||
return max - rhs - lhs + 1;
|
||||
} else {
|
||||
return lhs - rhs;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int64_t zig_subw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {
|
||||
if ((lhs > 0) && (rhs < 0)) {
|
||||
int64_t thresh = lhs - max;
|
||||
if (rhs < thresh) {
|
||||
return min + (thresh - rhs - 1);
|
||||
}
|
||||
} else if ((lhs < 0) && (rhs > 0)) {
|
||||
int64_t thresh = lhs - min;
|
||||
if (rhs > thresh) {
|
||||
return max - (rhs - thresh - 1);
|
||||
}
|
||||
}
|
||||
return lhs - rhs;
|
||||
}
|
||||
|
||||
static inline intptr_t zig_subw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {
|
||||
return (intptr_t)(((uintptr_t)lhs) - ((uintptr_t)rhs));
|
||||
}
|
||||
|
||||
static inline short zig_subw_short(short lhs, short rhs, short min, short max) {
|
||||
return (short)(((unsigned short)lhs) - ((unsigned short)rhs));
|
||||
}
|
||||
|
||||
static inline int zig_subw_int(int lhs, int rhs, int min, int max) {
|
||||
return (int)(((unsigned)lhs) - ((unsigned)rhs));
|
||||
}
|
||||
|
||||
static inline long zig_subw_long(long lhs, long rhs, long min, long max) {
|
||||
return (long)(((unsigned long)lhs) - ((unsigned long)rhs));
|
||||
}
|
||||
|
||||
static inline long long zig_subw_longlong(long long lhs, long long rhs, long long min, long long max) {
|
||||
return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
|
||||
}
|
||||
|
||||
|
||||
@ -824,6 +824,56 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
, "");
|
||||
}
|
||||
|
||||
{
|
||||
// TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64)
|
||||
var case = ctx.exeFromCompiledC("add/sub wrapping operations", .{});
|
||||
case.addCompareOutput(
|
||||
\\pub export fn main() c_int {
|
||||
\\ // Addition
|
||||
\\ if (!add_u3(1, 1, 2)) return 1;
|
||||
\\ if (!add_u3(7, 1, 0)) return 1;
|
||||
\\ if (!add_i3(1, 1, 2)) return 1;
|
||||
\\ if (!add_i3(3, 2, -3)) return 1;
|
||||
\\ if (!add_i3(-3, -2, 3)) return 1;
|
||||
\\ if (!add_c_int(1, 1, 2)) return 1;
|
||||
\\ // TODO enable these when stage2 supports std.math.maxInt
|
||||
\\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1;
|
||||
\\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1;
|
||||
\\
|
||||
\\ // Subtraction
|
||||
\\ if (!sub_u3(2, 1, 1)) return 1;
|
||||
\\ if (!sub_u3(0, 1, 7)) return 1;
|
||||
\\ if (!sub_i3(2, 1, 1)) return 1;
|
||||
\\ if (!sub_i3(3, -2, -3)) return 1;
|
||||
\\ if (!sub_i3(-3, 2, 3)) return 1;
|
||||
\\ if (!sub_c_int(2, 1, 1)) return 1;
|
||||
\\ // TODO enable these when stage2 supports std.math.maxInt
|
||||
\\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1;
|
||||
\\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1;
|
||||
\\
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool {
|
||||
\\ return expected == lhs +% rhs;
|
||||
\\}
|
||||
\\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool {
|
||||
\\ return expected == lhs +% rhs;
|
||||
\\}
|
||||
\\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
|
||||
\\ return expected == lhs +% rhs;
|
||||
\\}
|
||||
\\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool {
|
||||
\\ return expected == lhs -% rhs;
|
||||
\\}
|
||||
\\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool {
|
||||
\\ return expected == lhs -% rhs;
|
||||
\\}
|
||||
\\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
|
||||
\\ return expected == lhs -% rhs;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
ctx.h("simple header", linux_x64,
|
||||
\\export fn start() void{}
|
||||
,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user