aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/c.zig
diff options
context:
space:
mode:
authorMatt Knight <mattnite@protonmail.com>2021-04-10 14:21:59 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-07-08 09:56:40 -0700
commitfb16633ecb496f3f30cdac11987baad40b7793b2 (patch)
tree96d0b726a2fac3faa5f13777652806d846916025 /src/codegen/c.zig
parent62d27fcfb687e3ab1f10c72513e19529d8ffceed (diff)
downloadzig-fb16633ecb496f3f30cdac11987baad40b7793b2.tar.gz
zig-fb16633ecb496f3f30cdac11987baad40b7793b2.zip
C backend: add/sub/mul wrapping for the C backend
Diffstat (limited to 'src/codegen/c.zig')
-rw-r--r--src/codegen/c.zig134
1 files changed, 128 insertions, 6 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 3aaf559802..db84f13c1a 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -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, .add, inst.castTag(.addwrap).?),
// 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, .sub, inst.castTag(.subwrap).?),
// 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, .mul, inst.castTag(.mulwrap).?),
// TODO use a different strategy for div that communicates to the optimizer
// that wrapping is UB.
.div => try genBinOp(o, inst.castTag(.div).?, " / "),
@@ -1042,6 +1039,131 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
return CValue.none;
}
+const WrappingOp = enum {
+ add,
+ sub,
+ mul,
+};
+
+fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
+ if (inst.base.isUnused())
+ return CValue.none;
+
+ const is_signed = inst.base.ty.isSignedInt();
+
+ // if it's an unsigned int with non-arbitrary bit size then we can just add
+ if (!is_signed and inst.base.ty.tag() != .int_unsigned) {
+ return try genBinOp(o, inst, switch (op) {
+ .add => " + ",
+ .sub => " - ",
+ .mul => " * ",
+ });
+ }
+
+ var min_buf: [80]u8 = undefined;
+ const min = if (!is_signed)
+ "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: {
+ // should be able to use undefined here since all the target specifics are handled
+ const bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;
+ assert(bits <= 64); // TODO: large integers
+ const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1));
+ break :blk std.fmt.bufPrint(&min_buf, "{}", .{val}) catch |e|
+ // doesn't fit in some upwards error set, but should never happen
+ return if (e == error.NoSpaceLeft) unreachable else 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: {
+ // should be able to use undefined here since all the target specifics are handled
+ const bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;
+ assert(bits <= 64); // TODO: large integers
+ const val = std.math.pow(u64, 2, if (is_signed) (bits - 1) else bits) - 1;
+ break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |e|
+ // doesn't fit in some upwards error set, but should never happen
+ return if (e == error.NoSpaceLeft) unreachable else 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.writeAll(" = zig_");
+ try w.writeAll(switch (op) {
+ .add => "addw_",
+ .sub => "subw_",
+ .mul => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement wrapping multiplication operator", .{}),
+ });
+
+ switch (inst.base.ty.tag()) {
+ .u8 => try w.writeAll("u8"),
+ .i8 => try w.writeAll("i8"),
+ .u16 => try w.writeAll("u16"),
+ .i16 => try w.writeAll("i16"),
+ .u32 => try w.writeAll("u32"),
+ .i32 => try w.writeAll("i32"),
+ .u64 => try w.writeAll("u64"),
+ .i64 => try w.writeAll("i64"),
+ .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"),
+ .int_signed, .int_unsigned => {
+ if (is_signed) {
+ try w.writeByte('i');
+ } else {
+ try w.writeByte('u');
+ }
+
+ const info_bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;
+ inline for (.{ 8, 16, 32, 64 }) |nbits| {
+ if (info_bits <= nbits) {
+ try w.print("{d}", .{nbits});
+ break;
+ }
+ } else {
+ return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 64 bits", .{});
+ }
+ },
+ else => unreachable,
+ }
+
+ try w.writeByte('(');
+ try o.writeCValue(w, lhs);
+ try w.writeAll(", ");
+ try o.writeCValue(w, rhs);
+
+ if (is_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: []const u8) !CValue {
if (inst.base.isUnused())
return CValue.none;