C backend: cleanups to wrapping int operations
* less branching by passing parameters in the main op code switch. * properly pass the target when asking the type system for int info. * handle u8, i16, etc when it is represented using int_unsigned/int_signed tag. * compile error instead of assertion failure for unimplemented cases (greater than 64 bits integer). * control flow cleanups * zig.h: expand macros into inline functions * reduce the complexity of the test case by making it one test case that calls multiple functions. Also fix the problem of c_int max value mismatch between host and target.
This commit is contained in:
@@ -846,15 +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).?, " + "),
|
||||
.addwrap => try genWrapOp(o, .add, inst.castTag(.addwrap).?),
|
||||
.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).?, " - "),
|
||||
.subwrap => try genWrapOp(o, .sub, inst.castTag(.subwrap).?),
|
||||
.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).?, " * "),
|
||||
.mulwrap => try genWrapOp(o, .mul, inst.castTag(.mulwrap).?),
|
||||
.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).?, " / "),
|
||||
@@ -1039,44 +1039,44 @@ 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 {
|
||||
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 is_signed = inst.base.ty.isSignedInt();
|
||||
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 (!is_signed and inst.base.ty.tag() != .int_unsigned) {
|
||||
return try genBinOp(o, inst, switch (op) {
|
||||
.add => " + ",
|
||||
.sub => " - ",
|
||||
.mul => " * ",
|
||||
});
|
||||
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 = 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;
|
||||
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,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1093,13 +1093,15 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
|
||||
.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 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,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1108,45 +1110,28 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
|
||||
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", .{}),
|
||||
});
|
||||
try w.print(" = zig_{s}", .{fn_op});
|
||||
|
||||
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});
|
||||
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 {
|
||||
return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 64 bits", .{});
|
||||
unreachable;
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
try w.writeByte('(');
|
||||
@@ -1154,7 +1139,7 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
|
||||
try w.writeAll(", ");
|
||||
try o.writeCValue(w, rhs);
|
||||
|
||||
if (is_signed) {
|
||||
if (int_info.signedness == .signed) {
|
||||
try w.print(", {s}", .{min});
|
||||
}
|
||||
|
||||
@@ -1164,7 +1149,7 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
|
||||
fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {
|
||||
if (inst.base.isUnused())
|
||||
return CValue.none;
|
||||
|
||||
@@ -1176,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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user