authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-08 14:26:20-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-07-08 14:26:20-04:00
log7bdeda82aea82b9a71378462f51a708b8ad88161
treeade71b33286590a21600e113046c62e89a4954c7
parent62d27fcfb687e3ab1f10c72513e19529d8ffceed
parent9dbe68485489e576795fee04c83e69a2b6cf776a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8494 from mattnite/cbe-wrapping-addition

Wrapping add/sub for the C backend

3 files changed, 394 insertions(+), 8 deletions(-)

src/codegen/c.zig+115-8
......@@ -846,18 +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 // TODO make this do wrapping arithmetic for signed ints
850 .addwrap => try genBinOp(o, inst.castTag(.add).?, " + "),
849 .addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"),
851850 // TODO use a different strategy for sub that communicates to the optimizer
852851 // that wrapping is UB.
853852 .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
854 // TODO make this do wrapping arithmetic for signed ints
855 .subwrap => try genBinOp(o, inst.castTag(.sub).?, " - "),
853 .subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"),
856854 // TODO use a different strategy for mul that communicates to the optimizer
857855 // that wrapping is UB.
858856 .mul => try genBinOp(o, inst.castTag(.sub).?, " * "),
859 // TODO make this do wrapping multiplication for signed ints
860 .mulwrap => try genBinOp(o, inst.castTag(.sub).?, " * "),
857 .mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"),
861858 // TODO use a different strategy for div that communicates to the optimizer
862859 // that wrapping is UB.
863860 .div => try genBinOp(o, inst.castTag(.div).?, " / "),
......@@ -1042,7 +1039,117 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
10421039 return CValue.none;
10431040}
10441041
1045fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
1042fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue {
1043 if (inst.base.isUnused())
1044 return CValue.none;
1045
1046 const int_info = inst.base.ty.intInfo(o.dg.module.getTarget());
1047 const bits = int_info.bits;
1048
1049 // if it's an unsigned int with non-arbitrary bit size then we can just add
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", .{});
1062 }
1063
1064 var min_buf: [80]u8 = undefined;
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 },
1080 },
1081 };
1082
1083 var max_buf: [80]u8 = undefined;
1084 const max = switch (inst.base.ty.tag()) {
1085 .c_short => "SHRT_MAX",
1086 .c_ushort => "USHRT_MAX",
1087 .c_int => "INT_MAX",
1088 .c_uint => "UINT_MAX",
1089 .c_long => "LONG_MAX",
1090 .c_ulong => "ULONG_MAX",
1091 .c_longlong => "LLONG_MAX",
1092 .c_ulonglong => "ULLONG_MAX",
1093 .isize => "INTPTR_MAX",
1094 .usize => "UINTPTR_MAX",
1095 else => blk: {
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 };
1105 },
1106 };
1107
1108 const lhs = try o.resolveInst(inst.lhs);
1109 const rhs = try o.resolveInst(inst.rhs);
1110 const w = o.writer();
1111
1112 const ret = try o.allocLocal(inst.base.ty, .Mut);
1113 try w.print(" = zig_{s}", .{fn_op});
1114
1115 switch (inst.base.ty.tag()) {
1116 .isize => try w.writeAll("isize"),
1117 .c_short => try w.writeAll("short"),
1118 .c_int => try w.writeAll("int"),
1119 .c_long => try w.writeAll("long"),
1120 .c_longlong => try w.writeAll("longlong"),
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 });
1129 break;
1130 }
1131 } else {
1132 unreachable;
1133 }
1134 },
1135 }
1136
1137 try w.writeByte('(');
1138 try o.writeCValue(w, lhs);
1139 try w.writeAll(", ");
1140 try o.writeCValue(w, rhs);
1141
1142 if (int_info.signedness == .signed) {
1143 try w.print(", {s}", .{min});
1144 }
1145
1146 try w.print(", {s});", .{max});
1147 try o.indent_writer.insertNewline();
1148
1149 return ret;
1150}
1151
1152fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {
10461153 if (inst.base.isUnused())
10471154 return CValue.none;
10481155
......@@ -1054,7 +1161,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
10541161
10551162 try writer.writeAll(" = ");
10561163 try o.writeCValue(writer, lhs);
1057 try writer.writeAll(operator);
1164 try writer.print("{s}", .{operator});
10581165 try o.writeCValue(writer, rhs);
10591166 try writer.writeAll(";\n");
10601167
src/link/C/zig.h+229
......@@ -62,7 +62,236 @@
6262
6363#include <stdint.h>
6464#include <stddef.h>
65#include <limits.h>
6566#define int128_t __int128
6667#define uint128_t unsigned __int128
6768ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
6869
70static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
71 uint8_t thresh = max - rhs;
72 if (lhs > thresh) {
73 return lhs - thresh - 1;
74 } else {
75 return lhs + rhs;
76 }
77}
78
79static inline int8_t zig_addw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {
80 if ((lhs > 0) && (rhs > 0)) {
81 int8_t thresh = max - rhs;
82 if (lhs > thresh) {
83 return min + lhs - thresh - 1;
84 }
85 } else if ((lhs < 0) && (rhs < 0)) {
86 int8_t thresh = min - rhs;
87 if (lhs < thresh) {
88 return max + lhs - thresh + 1;
89 }
90 }
91 return lhs + rhs;
92}
93
94static inline uint16_t zig_addw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {
95 uint16_t thresh = max - rhs;
96 if (lhs > thresh) {
97 return lhs - thresh - 1;
98 } else {
99 return lhs + rhs;
100 }
101}
102
103static inline int16_t zig_addw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {
104 if ((lhs > 0) && (rhs > 0)) {
105 int16_t thresh = max - rhs;
106 if (lhs > thresh) {
107 return min + lhs - thresh - 1;
108 }
109 } else if ((lhs < 0) && (rhs < 0)) {
110 int16_t thresh = min - rhs;
111 if (lhs < thresh) {
112 return max + lhs - thresh + 1;
113 }
114 }
115 return lhs + rhs;
116}
117
118static inline uint32_t zig_addw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {
119 uint32_t thresh = max - rhs;
120 if (lhs > thresh) {
121 return lhs - thresh - 1;
122 } else {
123 return lhs + rhs;
124 }
125}
126
127static inline int32_t zig_addw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {
128 if ((lhs > 0) && (rhs > 0)) {
129 int32_t thresh = max - rhs;
130 if (lhs > thresh) {
131 return min + lhs - thresh - 1;
132 }
133 } else if ((lhs < 0) && (rhs < 0)) {
134 int32_t thresh = min - rhs;
135 if (lhs < thresh) {
136 return max + lhs - thresh + 1;
137 }
138 }
139 return lhs + rhs;
140}
141
142static inline uint64_t zig_addw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {
143 uint64_t thresh = max - rhs;
144 if (lhs > thresh) {
145 return lhs - thresh - 1;
146 } else {
147 return lhs + rhs;
148 }
149}
150
151static inline int64_t zig_addw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {
152 if ((lhs > 0) && (rhs > 0)) {
153 int64_t thresh = max - rhs;
154 if (lhs > thresh) {
155 return min + lhs - thresh - 1;
156 }
157 } else if ((lhs < 0) && (rhs < 0)) {
158 int64_t thresh = min - rhs;
159 if (lhs < thresh) {
160 return max + lhs - thresh + 1;
161 }
162 }
163 return lhs + rhs;
164}
165
166static inline intptr_t zig_addw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {
167 return (intptr_t)(((uintptr_t)lhs) + ((uintptr_t)rhs));
168}
169
170static inline short zig_addw_short(short lhs, short rhs, short min, short max) {
171 return (short)(((unsigned short)lhs) + ((unsigned short)rhs));
172}
173
174static inline int zig_addw_int(int lhs, int rhs, int min, int max) {
175 return (int)(((unsigned)lhs) + ((unsigned)rhs));
176}
177
178static inline long zig_addw_long(long lhs, long rhs, long min, long max) {
179 return (long)(((unsigned long)lhs) + ((unsigned long)rhs));
180}
181
182static inline long long zig_addw_longlong(long long lhs, long long rhs, long long min, long long max) {
183 return (long long)(((unsigned long long)lhs) + ((unsigned long long)rhs));
184}
185
186static inline uint8_t zig_subw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
187 if (lhs < rhs) {
188 return max - rhs - lhs + 1;
189 } else {
190 return lhs - rhs;
191 }
192}
193
194static inline int8_t zig_subw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {
195 if ((lhs > 0) && (rhs < 0)) {
196 int8_t thresh = lhs - max;
197 if (rhs < thresh) {
198 return min + (thresh - rhs - 1);
199 }
200 } else if ((lhs < 0) && (rhs > 0)) {
201 int8_t thresh = lhs - min;
202 if (rhs > thresh) {
203 return max - (rhs - thresh - 1);
204 }
205 }
206 return lhs - rhs;
207}
208
209static inline uint16_t zig_subw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {
210 if (lhs < rhs) {
211 return max - rhs - lhs + 1;
212 } else {
213 return lhs - rhs;
214 }
215}
216
217static inline int16_t zig_subw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {
218 if ((lhs > 0) && (rhs < 0)) {
219 int16_t thresh = lhs - max;
220 if (rhs < thresh) {
221 return min + (thresh - rhs - 1);
222 }
223 } else if ((lhs < 0) && (rhs > 0)) {
224 int16_t thresh = lhs - min;
225 if (rhs > thresh) {
226 return max - (rhs - thresh - 1);
227 }
228 }
229 return lhs - rhs;
230}
231
232static inline uint32_t zig_subw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {
233 if (lhs < rhs) {
234 return max - rhs - lhs + 1;
235 } else {
236 return lhs - rhs;
237 }
238}
239
240static inline int32_t zig_subw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {
241 if ((lhs > 0) && (rhs < 0)) {
242 int32_t thresh = lhs - max;
243 if (rhs < thresh) {
244 return min + (thresh - rhs - 1);
245 }
246 } else if ((lhs < 0) && (rhs > 0)) {
247 int32_t thresh = lhs - min;
248 if (rhs > thresh) {
249 return max - (rhs - thresh - 1);
250 }
251 }
252 return lhs - rhs;
253}
254
255static inline uint64_t zig_subw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {
256 if (lhs < rhs) {
257 return max - rhs - lhs + 1;
258 } else {
259 return lhs - rhs;
260 }
261}
262
263static inline int64_t zig_subw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {
264 if ((lhs > 0) && (rhs < 0)) {
265 int64_t thresh = lhs - max;
266 if (rhs < thresh) {
267 return min + (thresh - rhs - 1);
268 }
269 } else if ((lhs < 0) && (rhs > 0)) {
270 int64_t thresh = lhs - min;
271 if (rhs > thresh) {
272 return max - (rhs - thresh - 1);
273 }
274 }
275 return lhs - rhs;
276}
277
278static inline intptr_t zig_subw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {
279 return (intptr_t)(((uintptr_t)lhs) - ((uintptr_t)rhs));
280}
281
282static inline short zig_subw_short(short lhs, short rhs, short min, short max) {
283 return (short)(((unsigned short)lhs) - ((unsigned short)rhs));
284}
285
286static inline int zig_subw_int(int lhs, int rhs, int min, int max) {
287 return (int)(((unsigned)lhs) - ((unsigned)rhs));
288}
289
290static inline long zig_subw_long(long lhs, long rhs, long min, long max) {
291 return (long)(((unsigned long)lhs) - ((unsigned long)rhs));
292}
293
294static inline long long zig_subw_longlong(long long lhs, long long rhs, long long min, long long max) {
295 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
296}
297
test/stage2/cbe.zig+50
......@@ -824,6 +824,56 @@ pub fn addCases(ctx: *TestContext) !void {
824824 , "");
825825 }
826826
827 {
828 // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64)
829 var case = ctx.exeFromCompiledC("add/sub wrapping operations", .{});
830 case.addCompareOutput(
831 \\pub export fn main() c_int {
832 \\ // Addition
833 \\ if (!add_u3(1, 1, 2)) return 1;
834 \\ if (!add_u3(7, 1, 0)) return 1;
835 \\ if (!add_i3(1, 1, 2)) return 1;
836 \\ if (!add_i3(3, 2, -3)) return 1;
837 \\ if (!add_i3(-3, -2, 3)) return 1;
838 \\ if (!add_c_int(1, 1, 2)) return 1;
839 \\ // TODO enable these when stage2 supports std.math.maxInt
840 \\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1;
841 \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1;
842 \\
843 \\ // Subtraction
844 \\ if (!sub_u3(2, 1, 1)) return 1;
845 \\ if (!sub_u3(0, 1, 7)) return 1;
846 \\ if (!sub_i3(2, 1, 1)) return 1;
847 \\ if (!sub_i3(3, -2, -3)) return 1;
848 \\ if (!sub_i3(-3, 2, 3)) return 1;
849 \\ if (!sub_c_int(2, 1, 1)) return 1;
850 \\ // TODO enable these when stage2 supports std.math.maxInt
851 \\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1;
852 \\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1;
853 \\
854 \\ return 0;
855 \\}
856 \\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool {
857 \\ return expected == lhs +% rhs;
858 \\}
859 \\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool {
860 \\ return expected == lhs +% rhs;
861 \\}
862 \\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
863 \\ return expected == lhs +% rhs;
864 \\}
865 \\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool {
866 \\ return expected == lhs -% rhs;
867 \\}
868 \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool {
869 \\ return expected == lhs -% rhs;
870 \\}
871 \\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
872 \\ return expected == lhs -% rhs;
873 \\}
874 , "");
875 }
876
827877 ctx.h("simple header", linux_x64,
828878 \\export fn start() void{}
829879 ,