Skip to content

Commit 9dbe684

Browse files
committed
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.
1 parent fb16633 commit 9dbe684

File tree

3 files changed

+241
-178
lines changed

3 files changed

+241
-178
lines changed

src/codegen/c.zig

Lines changed: 55 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -846,15 +846,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
846846
// TODO use a different strategy for add that communicates to the optimizer
847847
// that wrapping is UB.
848848
.add => try genBinOp(o, inst.castTag(.add).?, " + "),
849-
.addwrap => try genWrapOp(o, .add, inst.castTag(.addwrap).?),
849+
.addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"),
850850
// TODO use a different strategy for sub that communicates to the optimizer
851851
// that wrapping is UB.
852852
.sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
853-
.subwrap => try genWrapOp(o, .sub, inst.castTag(.subwrap).?),
853+
.subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"),
854854
// TODO use a different strategy for mul that communicates to the optimizer
855855
// that wrapping is UB.
856856
.mul => try genBinOp(o, inst.castTag(.sub).?, " * "),
857-
.mulwrap => try genWrapOp(o, .mul, inst.castTag(.mulwrap).?),
857+
.mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"),
858858
// TODO use a different strategy for div that communicates to the optimizer
859859
// that wrapping is UB.
860860
.div => try genBinOp(o, inst.castTag(.div).?, " / "),
@@ -1039,44 +1039,44 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
10391039
return CValue.none;
10401040
}
10411041

1042-
const WrappingOp = enum {
1043-
add,
1044-
sub,
1045-
mul,
1046-
};
1047-
1048-
fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
1042+
fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue {
10491043
if (inst.base.isUnused())
10501044
return CValue.none;
10511045

1052-
const is_signed = inst.base.ty.isSignedInt();
1046+
const int_info = inst.base.ty.intInfo(o.dg.module.getTarget());
1047+
const bits = int_info.bits;
10531048

10541049
// if it's an unsigned int with non-arbitrary bit size then we can just add
1055-
if (!is_signed and inst.base.ty.tag() != .int_unsigned) {
1056-
return try genBinOp(o, inst, switch (op) {
1057-
.add => " + ",
1058-
.sub => " - ",
1059-
.mul => " * ",
1060-
});
1050+
if (int_info.signedness == .unsigned) {
1051+
const ok_bits = switch (bits) {
1052+
8, 16, 32, 64, 128 => true,
1053+
else => false,
1054+
};
1055+
if (ok_bits or inst.base.ty.tag() != .int_unsigned) {
1056+
return try genBinOp(o, inst, str_op);
1057+
}
1058+
}
1059+
1060+
if (bits > 64) {
1061+
return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: genWrapOp for large integers", .{});
10611062
}
10621063

10631064
var min_buf: [80]u8 = undefined;
1064-
const min = if (!is_signed)
1065-
"0"
1066-
else switch (inst.base.ty.tag()) {
1067-
.c_short => "SHRT_MIN",
1068-
.c_int => "INT_MIN",
1069-
.c_long => "LONG_MIN",
1070-
.c_longlong => "LLONG_MIN",
1071-
.isize => "INTPTR_MIN",
1072-
else => blk: {
1073-
// should be able to use undefined here since all the target specifics are handled
1074-
const bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;
1075-
assert(bits <= 64); // TODO: large integers
1076-
const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1));
1077-
break :blk std.fmt.bufPrint(&min_buf, "{}", .{val}) catch |e|
1078-
// doesn't fit in some upwards error set, but should never happen
1079-
return if (e == error.NoSpaceLeft) unreachable else e;
1065+
const min = switch (int_info.signedness) {
1066+
.unsigned => "0",
1067+
else => switch (inst.base.ty.tag()) {
1068+
.c_short => "SHRT_MIN",
1069+
.c_int => "INT_MIN",
1070+
.c_long => "LONG_MIN",
1071+
.c_longlong => "LLONG_MIN",
1072+
.isize => "INTPTR_MIN",
1073+
else => blk: {
1074+
const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1));
1075+
break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) {
1076+
error.NoSpaceLeft => unreachable,
1077+
else => |e| return e,
1078+
};
1079+
},
10801080
},
10811081
};
10821082

@@ -1093,13 +1093,15 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
10931093
.isize => "INTPTR_MAX",
10941094
.usize => "UINTPTR_MAX",
10951095
else => blk: {
1096-
// should be able to use undefined here since all the target specifics are handled
1097-
const bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;
1098-
assert(bits <= 64); // TODO: large integers
1099-
const val = std.math.pow(u64, 2, if (is_signed) (bits - 1) else bits) - 1;
1100-
break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |e|
1101-
// doesn't fit in some upwards error set, but should never happen
1102-
return if (e == error.NoSpaceLeft) unreachable else e;
1096+
const pow_bits = switch (int_info.signedness) {
1097+
.signed => bits - 1,
1098+
.unsigned => bits,
1099+
};
1100+
const val = std.math.pow(u64, 2, pow_bits) - 1;
1101+
break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) {
1102+
error.NoSpaceLeft => unreachable,
1103+
else => |e| return e,
1104+
};
11031105
},
11041106
};
11051107

@@ -1108,53 +1110,36 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
11081110
const w = o.writer();
11091111

11101112
const ret = try o.allocLocal(inst.base.ty, .Mut);
1111-
try w.writeAll(" = zig_");
1112-
try w.writeAll(switch (op) {
1113-
.add => "addw_",
1114-
.sub => "subw_",
1115-
.mul => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement wrapping multiplication operator", .{}),
1116-
});
1113+
try w.print(" = zig_{s}", .{fn_op});
11171114

11181115
switch (inst.base.ty.tag()) {
1119-
.u8 => try w.writeAll("u8"),
1120-
.i8 => try w.writeAll("i8"),
1121-
.u16 => try w.writeAll("u16"),
1122-
.i16 => try w.writeAll("i16"),
1123-
.u32 => try w.writeAll("u32"),
1124-
.i32 => try w.writeAll("i32"),
1125-
.u64 => try w.writeAll("u64"),
1126-
.i64 => try w.writeAll("i64"),
11271116
.isize => try w.writeAll("isize"),
11281117
.c_short => try w.writeAll("short"),
11291118
.c_int => try w.writeAll("int"),
11301119
.c_long => try w.writeAll("long"),
11311120
.c_longlong => try w.writeAll("longlong"),
1132-
.int_signed, .int_unsigned => {
1133-
if (is_signed) {
1134-
try w.writeByte('i');
1135-
} else {
1136-
try w.writeByte('u');
1137-
}
1138-
1139-
const info_bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;
1140-
inline for (.{ 8, 16, 32, 64 }) |nbits| {
1141-
if (info_bits <= nbits) {
1142-
try w.print("{d}", .{nbits});
1121+
else => {
1122+
const prefix_byte: u8 = switch (int_info.signedness) {
1123+
.signed => 'i',
1124+
.unsigned => 'u',
1125+
};
1126+
for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
1127+
if (bits <= nbits) {
1128+
try w.print("{c}{d}", .{ prefix_byte, nbits });
11431129
break;
11441130
}
11451131
} else {
1146-
return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 64 bits", .{});
1132+
unreachable;
11471133
}
11481134
},
1149-
else => unreachable,
11501135
}
11511136

11521137
try w.writeByte('(');
11531138
try o.writeCValue(w, lhs);
11541139
try w.writeAll(", ");
11551140
try o.writeCValue(w, rhs);
11561141

1157-
if (is_signed) {
1142+
if (int_info.signedness == .signed) {
11581143
try w.print(", {s}", .{min});
11591144
}
11601145

@@ -1164,7 +1149,7 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
11641149
return ret;
11651150
}
11661151

1167-
fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
1152+
fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {
11681153
if (inst.base.isUnused())
11691154
return CValue.none;
11701155

@@ -1176,7 +1161,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
11761161

11771162
try writer.writeAll(" = ");
11781163
try o.writeCValue(writer, lhs);
1179-
try writer.writeAll(operator);
1164+
try writer.print("{s}", .{operator});
11801165
try o.writeCValue(writer, rhs);
11811166
try writer.writeAll(";\n");
11821167

0 commit comments

Comments
 (0)