authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-08 11:21:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-08 11:21:06-07:00
log9dbe68485489e576795fee04c83e69a2b6cf776a
treeade71b33286590a21600e113046c62e89a4954c7
parentfb16633ecb496f3f30cdac11987baad40b7793b2

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.

3 files changed, 241 insertions(+), 178 deletions(-)

src/codegen/c.zig+55-70
...@@ -846,15 +846,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi...@@ -846,15 +846,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
846 // TODO use a different strategy for add that communicates to the optimizer846 // TODO use a different strategy for add that communicates to the optimizer
847 // that wrapping is UB.847 // that wrapping is UB.
848 .add => try genBinOp(o, inst.castTag(.add).?, " + "),848 .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_"),
850 // TODO use a different strategy for sub that communicates to the optimizer850 // TODO use a different strategy for sub that communicates to the optimizer
851 // that wrapping is UB.851 // that wrapping is UB.
852 .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),852 .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_"),
854 // TODO use a different strategy for mul that communicates to the optimizer854 // TODO use a different strategy for mul that communicates to the optimizer
855 // that wrapping is UB.855 // that wrapping is UB.
856 .mul => try genBinOp(o, inst.castTag(.sub).?, " * "),856 .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_"),
858 // TODO use a different strategy for div that communicates to the optimizer858 // TODO use a different strategy for div that communicates to the optimizer
859 // that wrapping is UB.859 // that wrapping is UB.
860 .div => try genBinOp(o, inst.castTag(.div).?, " / "),860 .div => try genBinOp(o, inst.castTag(.div).?, " / "),
...@@ -1039,44 +1039,44 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {...@@ -1039,44 +1039,44 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue {
1039 return CValue.none;1039 return CValue.none;
1040}1040}
10411041
1042const WrappingOp = enum {1042fn genWrapOp(o: *Object, inst: *Inst.BinOp, str_op: [*:0]const u8, fn_op: [*:0]const u8) !CValue {
1043 add,
1044 sub,
1045 mul,
1046};
1047
1048fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
1049 if (inst.base.isUnused())1043 if (inst.base.isUnused())
1050 return CValue.none;1044 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
1054 // if it's an unsigned int with non-arbitrary bit size then we can just add1049 // 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) {1050 if (int_info.signedness == .unsigned) {
1056 return try genBinOp(o, inst, switch (op) {1051 const ok_bits = switch (bits) {
1057 .add => " + ",1052 8, 16, 32, 64, 128 => true,
1058 .sub => " - ",1053 else => false,
1059 .mul => " * ",1054 };
1060 });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", .{});
1061 }1062 }
10621063
1063 var min_buf: [80]u8 = undefined;1064 var min_buf: [80]u8 = undefined;
1064 const min = if (!is_signed)1065 const min = switch (int_info.signedness) {
1065 "0"1066 .unsigned => "0",
1066 else switch (inst.base.ty.tag()) {1067 else => switch (inst.base.ty.tag()) {
1067 .c_short => "SHRT_MIN",1068 .c_short => "SHRT_MIN",
1068 .c_int => "INT_MIN",1069 .c_int => "INT_MIN",
1069 .c_long => "LONG_MIN",1070 .c_long => "LONG_MIN",
1070 .c_longlong => "LLONG_MIN",1071 .c_longlong => "LLONG_MIN",
1071 .isize => "INTPTR_MIN",1072 .isize => "INTPTR_MIN",
1072 else => blk: {1073 else => blk: {
1073 // should be able to use undefined here since all the target specifics are handled1074 const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1));
1074 const bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;1075 break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) {
1075 assert(bits <= 64); // TODO: large integers1076 error.NoSpaceLeft => unreachable,
1076 const val = -1 * std.math.pow(i64, 2, @intCast(i64, bits - 1));1077 else => |e| return e,
1077 break :blk std.fmt.bufPrint(&min_buf, "{}", .{val}) catch |e|1078 };
1078 // doesn't fit in some upwards error set, but should never happen1079 },
1079 return if (e == error.NoSpaceLeft) unreachable else e;
1080 },1080 },
1081 };1081 };
10821082
...@@ -1093,13 +1093,15 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {...@@ -1093,13 +1093,15 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
1093 .isize => "INTPTR_MAX",1093 .isize => "INTPTR_MAX",
1094 .usize => "UINTPTR_MAX",1094 .usize => "UINTPTR_MAX",
1095 else => blk: {1095 else => blk: {
1096 // should be able to use undefined here since all the target specifics are handled1096 const pow_bits = switch (int_info.signedness) {
1097 const bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;1097 .signed => bits - 1,
1098 assert(bits <= 64); // TODO: large integers1098 .unsigned => bits,
1099 const val = std.math.pow(u64, 2, if (is_signed) (bits - 1) else bits) - 1;1099 };
1100 break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |e|1100 const val = std.math.pow(u64, 2, pow_bits) - 1;
1101 // doesn't fit in some upwards error set, but should never happen1101 break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) {
1102 return if (e == error.NoSpaceLeft) unreachable else e;1102 error.NoSpaceLeft => unreachable,
1103 else => |e| return e,
1104 };
1103 },1105 },
1104 };1106 };
11051107
...@@ -1108,45 +1110,28 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {...@@ -1108,45 +1110,28 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
1108 const w = o.writer();1110 const w = o.writer();
11091111
1110 const ret = try o.allocLocal(inst.base.ty, .Mut);1112 const ret = try o.allocLocal(inst.base.ty, .Mut);
1111 try w.writeAll(" = zig_");1113 try w.print(" = zig_{s}", .{fn_op});
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 });
11171114
1118 switch (inst.base.ty.tag()) {1115 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"),
1127 .isize => try w.writeAll("isize"),1116 .isize => try w.writeAll("isize"),
1128 .c_short => try w.writeAll("short"),1117 .c_short => try w.writeAll("short"),
1129 .c_int => try w.writeAll("int"),1118 .c_int => try w.writeAll("int"),
1130 .c_long => try w.writeAll("long"),1119 .c_long => try w.writeAll("long"),
1131 .c_longlong => try w.writeAll("longlong"),1120 .c_longlong => try w.writeAll("longlong"),
1132 .int_signed, .int_unsigned => {1121 else => {
1133 if (is_signed) {1122 const prefix_byte: u8 = switch (int_info.signedness) {
1134 try w.writeByte('i');1123 .signed => 'i',
1135 } else {1124 .unsigned => 'u',
1136 try w.writeByte('u');1125 };
1137 }1126 for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
11381127 if (bits <= nbits) {
1139 const info_bits = inst.base.ty.intInfo(@as(std.Target, undefined)).bits;1128 try w.print("{c}{d}", .{ prefix_byte, nbits });
1140 inline for (.{ 8, 16, 32, 64 }) |nbits| {
1141 if (info_bits <= nbits) {
1142 try w.print("{d}", .{nbits});
1143 break;1129 break;
1144 }1130 }
1145 } else {1131 } else {
1146 return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 64 bits", .{});1132 unreachable;
1147 }1133 }
1148 },1134 },
1149 else => unreachable,
1150 }1135 }
11511136
1152 try w.writeByte('(');1137 try w.writeByte('(');
...@@ -1154,7 +1139,7 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {...@@ -1154,7 +1139,7 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
1154 try w.writeAll(", ");1139 try w.writeAll(", ");
1155 try o.writeCValue(w, rhs);1140 try o.writeCValue(w, rhs);
11561141
1157 if (is_signed) {1142 if (int_info.signedness == .signed) {
1158 try w.print(", {s}", .{min});1143 try w.print(", {s}", .{min});
1159 }1144 }
11601145
...@@ -1164,7 +1149,7 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {...@@ -1164,7 +1149,7 @@ fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue {
1164 return ret;1149 return ret;
1165}1150}
11661151
1167fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {1152fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue {
1168 if (inst.base.isUnused())1153 if (inst.base.isUnused())
1169 return CValue.none;1154 return CValue.none;
11701155
...@@ -1176,7 +1161,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {...@@ -1176,7 +1161,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue {
11761161
1177 try writer.writeAll(" = ");1162 try writer.writeAll(" = ");
1178 try o.writeCValue(writer, lhs);1163 try o.writeCValue(writer, lhs);
1179 try writer.writeAll(operator);1164 try writer.print("{s}", .{operator});
1180 try o.writeCValue(writer, rhs);1165 try o.writeCValue(writer, rhs);
1181 try writer.writeAll(";\n");1166 try writer.writeAll(";\n");
11821167
src/link/C/zig.h+140-63
...@@ -60,51 +60,6 @@...@@ -60,51 +60,6 @@
60#define zig_breakpoint() raise(SIGTRAP)60#define zig_breakpoint() raise(SIGTRAP)
61#endif61#endif
6262
63
64#define ZIG_UADDW(Type, lhs, rhs, max) \
65 Type thresh = max - rhs; \
66 if (lhs > thresh) { \
67 return lhs - thresh - 1; \
68 } else { \
69 return lhs + rhs; \
70 }
71
72#define ZIG_SADDW(Type, lhs, rhs, min, max) \
73 if ((lhs > 0) && (rhs > 0)) { \
74 Type thresh = max - rhs; \
75 if (lhs > thresh) { \
76 return min + lhs - thresh - 1; \
77 } \
78 } else if ((lhs < 0) && (rhs < 0)) { \
79 Type thresh = min - rhs; \
80 if (lhs < thresh) { \
81 return max + lhs - thresh + 1; \
82 } \
83 } \
84 \
85 return lhs + rhs;
86
87#define ZIG_USUBW(lhs, rhs, max) \
88 if (lhs < rhs) { \
89 return max - rhs - lhs + 1; \
90 } else { \
91 return lhs - rhs; \
92 }
93
94#define ZIG_SSUBW(Type, lhs, rhs, min, max) \
95 if ((lhs > 0) && (rhs < 0)) { \
96 Type thresh = lhs - max; \
97 if (rhs < thresh) { \
98 return min + (thresh - rhs - 1); \
99 } \
100 } else if ((lhs < 0) && (rhs > 0)) { \
101 Type thresh = lhs - min; \
102 if (rhs > thresh) { \
103 return max - (rhs - thresh - 1); \
104 } \
105 } \
106 return lhs - rhs;
107
108#include <stdint.h>63#include <stdint.h>
109#include <stddef.h>64#include <stddef.h>
110#include <limits.h>65#include <limits.h>
...@@ -112,37 +67,100 @@...@@ -112,37 +67,100 @@
112#define uint128_t unsigned __int12867#define uint128_t unsigned __int128
113ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);68ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
11469
115/* Wrapping addition operators */
116static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {70static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
117 ZIG_UADDW(uint8_t, lhs, rhs, max);71 uint8_t thresh = max - rhs;
72 if (lhs > thresh) {
73 return lhs - thresh - 1;
74 } else {
75 return lhs + rhs;
76 }
118}77}
11978
120static inline int8_t zig_addw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {79static inline int8_t zig_addw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {
121 ZIG_SADDW(int8_t, lhs, rhs, min, 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;
122}92}
12393
124static inline uint16_t zig_addw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {94static inline uint16_t zig_addw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {
125 ZIG_UADDW(uint16_t, lhs, rhs, max);95 uint16_t thresh = max - rhs;
96 if (lhs > thresh) {
97 return lhs - thresh - 1;
98 } else {
99 return lhs + rhs;
100 }
126}101}
127102
128static inline int16_t zig_addw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {103static inline int16_t zig_addw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {
129 ZIG_SADDW(int16_t, lhs, rhs, min, 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;
130}116}
131117
132static inline uint32_t zig_addw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {118static inline uint32_t zig_addw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {
133 ZIG_UADDW(uint32_t, lhs, rhs, max);119 uint32_t thresh = max - rhs;
120 if (lhs > thresh) {
121 return lhs - thresh - 1;
122 } else {
123 return lhs + rhs;
124 }
134}125}
135126
136static inline int32_t zig_addw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {127static inline int32_t zig_addw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {
137 ZIG_SADDW(int32_t, lhs, rhs, min, 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;
138}140}
139141
140static inline uint64_t zig_addw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {142static inline uint64_t zig_addw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {
141 ZIG_UADDW(uint64_t, lhs, rhs, max);143 uint64_t thresh = max - rhs;
144 if (lhs > thresh) {
145 return lhs - thresh - 1;
146 } else {
147 return lhs + rhs;
148 }
142}149}
143150
144static inline int64_t zig_addw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {151static inline int64_t zig_addw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {
145 ZIG_SADDW(int64_t, lhs, rhs, min, 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;
146}164}
147165
148static inline intptr_t zig_addw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {166static inline intptr_t zig_addw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {
...@@ -165,37 +183,96 @@ static inline long long zig_addw_longlong(long long lhs, long long rhs, long lon...@@ -165,37 +183,96 @@ static inline long long zig_addw_longlong(long long lhs, long long rhs, long lon
165 return (long long)(((unsigned long long)lhs) + ((unsigned long long)rhs));183 return (long long)(((unsigned long long)lhs) + ((unsigned long long)rhs));
166}184}
167185
168/* Wrapping subtraction operators */
169static inline uint8_t zig_subw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {186static inline uint8_t zig_subw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
170 ZIG_USUBW(lhs, rhs, max);187 if (lhs < rhs) {
188 return max - rhs - lhs + 1;
189 } else {
190 return lhs - rhs;
191 }
171}192}
172193
173static inline int8_t zig_subw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {194static inline int8_t zig_subw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) {
174 ZIG_SSUBW(int8_t, lhs, rhs, min, 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;
175}207}
176208
177static inline uint16_t zig_subw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {209static inline uint16_t zig_subw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) {
178 ZIG_USUBW(lhs, rhs, max);210 if (lhs < rhs) {
211 return max - rhs - lhs + 1;
212 } else {
213 return lhs - rhs;
214 }
179}215}
180216
181static inline int16_t zig_subw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {217static inline int16_t zig_subw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) {
182 ZIG_SSUBW(int16_t, lhs, rhs, min, 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;
183}230}
184231
185static inline uint32_t zig_subw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {232static inline uint32_t zig_subw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) {
186 ZIG_USUBW(lhs, rhs, max);233 if (lhs < rhs) {
234 return max - rhs - lhs + 1;
235 } else {
236 return lhs - rhs;
237 }
187}238}
188239
189static inline int32_t zig_subw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {240static inline int32_t zig_subw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) {
190 ZIG_SSUBW(int32_t, lhs, rhs, min, 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;
191}253}
192254
193static inline uint64_t zig_subw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {255static inline uint64_t zig_subw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) {
194 ZIG_USUBW(lhs, rhs, max);256 if (lhs < rhs) {
257 return max - rhs - lhs + 1;
258 } else {
259 return lhs - rhs;
260 }
195}261}
196262
197static inline int64_t zig_subw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {263static inline int64_t zig_subw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) {
198 ZIG_SSUBW(int64_t, lhs, rhs, min, 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;
199}276}
200277
201static inline intptr_t zig_subw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {278static inline intptr_t zig_subw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) {
test/stage2/cbe.zig+46-45
...@@ -825,52 +825,53 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -825,52 +825,53 @@ pub fn addCases(ctx: *TestContext) !void {
825 }825 }
826826
827 {827 {
828 // TODO: move these cases into the programs themselves once stage 2 has array literals
829 // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64)828 // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64)
830 var case = ctx.exeFromCompiledC("Wrapping operations", .{});829 var case = ctx.exeFromCompiledC("add/sub wrapping operations", .{});
831 const programs = comptime blk: {830 case.addCompareOutput(
832 const cases = .{831 \\pub export fn main() c_int {
833 // Addition832 \\ // Addition
834 .{ u3, "+%", 1, 1, 2 },833 \\ if (!add_u3(1, 1, 2)) return 1;
835 .{ u3, "+%", 7, 1, 0 },834 \\ if (!add_u3(7, 1, 0)) return 1;
836 .{ i3, "+%", 1, 1, 2 },835 \\ if (!add_i3(1, 1, 2)) return 1;
837 .{ i3, "+%", 3, 2, -3 },836 \\ if (!add_i3(3, 2, -3)) return 1;
838 .{ i3, "+%", -3, -2, 3 },837 \\ if (!add_i3(-3, -2, 3)) return 1;
839 .{ c_int, "+%", 1, 1, 2 },838 \\ if (!add_c_int(1, 1, 2)) return 1;
840 .{ c_int, "+%", std.math.maxInt(c_int), 2, std.math.minInt(c_int) + 1 },839 \\ // TODO enable these when stage2 supports std.math.maxInt
841 .{ c_int, "+%", std.math.minInt(c_int) + 1, -2, std.math.maxInt(c_int) },840 \\ //if (!add_c_int(maxInt(c_int), 2, minInt(c_int) + 1)) return 1;
842841 \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1;
843 // Subtraction842 \\
844 .{ u3, "-%", 2, 1, 1 },843 \\ // Subtraction
845 .{ u3, "-%", 0, 1, 7 },844 \\ if (!sub_u3(2, 1, 1)) return 1;
846 .{ i3, "-%", 2, 1, 1 },845 \\ if (!sub_u3(0, 1, 7)) return 1;
847 .{ i3, "-%", 3, -2, -3 },846 \\ if (!sub_i3(2, 1, 1)) return 1;
848 .{ i3, "-%", -3, 2, 3 },847 \\ if (!sub_i3(3, -2, -3)) return 1;
849 .{ c_int, "-%", 2, 1, 1 },848 \\ if (!sub_i3(-3, 2, 3)) return 1;
850 .{ c_int, "-%", std.math.maxInt(c_int), -2, std.math.minInt(c_int) + 1 },849 \\ if (!sub_c_int(2, 1, 1)) return 1;
851 .{ c_int, "-%", std.math.minInt(c_int) + 1, 2, std.math.maxInt(c_int) },850 \\ // TODO enable these when stage2 supports std.math.maxInt
852 };851 \\ //if (!sub_c_int(maxInt(c_int), -2, minInt(c_int) + 1)) return 1;
853852 \\ //if (!sub_c_int(minInt(c_int) + 1, 2, maxInt(c_int))) return 1;
854 var ret: [cases.len][:0]const u8 = undefined;853 \\
855 for (cases) |c, i| ret[i] = std.fmt.comptimePrint(854 \\ return 0;
856 \\export fn main() i32 {{855 \\}
857 \\ var lhs: {0} = {2};856 \\fn add_u3(lhs: u3, rhs: u3, expected: u3) bool {
858 \\ var rhs: {0} = {3};857 \\ return expected == lhs +% rhs;
859 \\ var expected: {0} = {4};858 \\}
860 \\859 \\fn add_i3(lhs: i3, rhs: i3, expected: i3) bool {
861 \\ if (expected != lhs {1s} rhs) {{860 \\ return expected == lhs +% rhs;
862 \\ return 1;861 \\}
863 \\ }} else {{862 \\fn add_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
864 \\ return 0;863 \\ return expected == lhs +% rhs;
865 \\ }}864 \\}
866 \\}}865 \\fn sub_u3(lhs: u3, rhs: u3, expected: u3) bool {
867 \\866 \\ return expected == lhs -% rhs;
868 , c);867 \\}
869868 \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool {
870 break :blk ret;869 \\ return expected == lhs -% rhs;
871 };870 \\}
872871 \\fn sub_c_int(lhs: c_int, rhs: c_int, expected: c_int) bool {
873 inline for (programs) |prog| case.addCompareOutput(prog, "");872 \\ return expected == lhs -% rhs;
873 \\}
874 , "");
874 }875 }
875876
876 ctx.h("simple header", linux_x64,877 ctx.h("simple header", linux_x64,