| author | |
| committer | |
| log | 7bdeda82aea82b9a71378462f51a708b8ad88161 |
| tree | ade71b33286590a21600e113046c62e89a4954c7 |
| parent | 62d27fcfb687e3ab1f10c72513e19529d8ffceed |
| parent | 9dbe68485489e576795fee04c83e69a2b6cf776a |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Wrapping add/sub for the C backend3 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 | ... | @@ -846,18 +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 optimizer | 846 | // 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 | // TODO make this do wrapping arithmetic for signed ints | 849 | .addwrap => try genWrapOp(o, inst.castTag(.addwrap).?, " + ", "addw_"), |
| 850 | .addwrap => try genBinOp(o, inst.castTag(.add).?, " + "), | ||
| 851 | // TODO use a different strategy for sub that communicates to the optimizer | 850 | // TODO use a different strategy for sub that communicates to the optimizer |
| 852 | // that wrapping is UB. | 851 | // that wrapping is UB. |
| 853 | .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), | 852 | .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), |
| 854 | // TODO make this do wrapping arithmetic for signed ints | 853 | .subwrap => try genWrapOp(o, inst.castTag(.subwrap).?, " - ", "subw_"), |
| 855 | .subwrap => try genBinOp(o, inst.castTag(.sub).?, " - "), | ||
| 856 | // TODO use a different strategy for mul that communicates to the optimizer | 854 | // TODO use a different strategy for mul that communicates to the optimizer |
| 857 | // that wrapping is UB. | 855 | // that wrapping is UB. |
| 858 | .mul => try genBinOp(o, inst.castTag(.sub).?, " * "), | 856 | .mul => try genBinOp(o, inst.castTag(.sub).?, " * "), |
| 859 | // TODO make this do wrapping multiplication for signed ints | 857 | .mulwrap => try genWrapOp(o, inst.castTag(.mulwrap).?, " * ", "mulw_"), |
| 860 | .mulwrap => try genBinOp(o, inst.castTag(.sub).?, " * "), | ||
| 861 | // TODO use a different strategy for div that communicates to the optimizer | 858 | // TODO use a different strategy for div that communicates to the optimizer |
| 862 | // that wrapping is UB. | 859 | // that wrapping is UB. |
| 863 | .div => try genBinOp(o, inst.castTag(.div).?, " / "), | 860 | .div => try genBinOp(o, inst.castTag(.div).?, " / "), |
| ... | @@ -1042,7 +1039,117 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { | ... | @@ -1042,7 +1039,117 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { |
| 1042 | return CValue.none; | 1039 | return CValue.none; |
| 1043 | } | 1040 | } |
| 1044 | 1041 | ||
| 1045 | fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { | 1042 | fn 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 | |||
| 1152 | fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: [*:0]const u8) !CValue { | ||
| 1046 | if (inst.base.isUnused()) | 1153 | if (inst.base.isUnused()) |
| 1047 | return CValue.none; | 1154 | return CValue.none; |
| 1048 | 1155 | ||
| ... | @@ -1054,7 +1161,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { | ... | @@ -1054,7 +1161,7 @@ fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { |
| 1054 | 1161 | ||
| 1055 | try writer.writeAll(" = "); | 1162 | try writer.writeAll(" = "); |
| 1056 | try o.writeCValue(writer, lhs); | 1163 | try o.writeCValue(writer, lhs); |
| 1057 | try writer.writeAll(operator); | 1164 | try writer.print("{s}", .{operator}); |
| 1058 | try o.writeCValue(writer, rhs); | 1165 | try o.writeCValue(writer, rhs); |
| 1059 | try writer.writeAll(";\n"); | 1166 | try writer.writeAll(";\n"); |
| 1060 | 1167 |
src/link/C/zig.h+229| ... | @@ -62,7 +62,236 @@ | ... | @@ -62,7 +62,236 @@ |
| 62 | 62 | ||
| 63 | #include <stdint.h> | 63 | #include <stdint.h> |
| 64 | #include <stddef.h> | 64 | #include <stddef.h> |
| 65 | #include <limits.h> | ||
| 65 | #define int128_t __int128 | 66 | #define int128_t __int128 |
| 66 | #define uint128_t unsigned __int128 | 67 | #define uint128_t unsigned __int128 |
| 67 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); | 68 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); |
| 68 | 69 | ||
| 70 | static 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 | |||
| 79 | static 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 | |||
| 94 | static 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 | |||
| 103 | static 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 | |||
| 118 | static 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 | |||
| 127 | static 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 | |||
| 142 | static 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 | |||
| 151 | static 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 | |||
| 166 | static 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 | |||
| 170 | static inline short zig_addw_short(short lhs, short rhs, short min, short max) { | ||
| 171 | return (short)(((unsigned short)lhs) + ((unsigned short)rhs)); | ||
| 172 | } | ||
| 173 | |||
| 174 | static inline int zig_addw_int(int lhs, int rhs, int min, int max) { | ||
| 175 | return (int)(((unsigned)lhs) + ((unsigned)rhs)); | ||
| 176 | } | ||
| 177 | |||
| 178 | static inline long zig_addw_long(long lhs, long rhs, long min, long max) { | ||
| 179 | return (long)(((unsigned long)lhs) + ((unsigned long)rhs)); | ||
| 180 | } | ||
| 181 | |||
| 182 | static 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 | |||
| 186 | static 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 | |||
| 194 | static 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 | |||
| 209 | static 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 | |||
| 217 | static 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 | |||
| 232 | static 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 | |||
| 240 | static 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 | |||
| 255 | static 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 | |||
| 263 | static 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 | |||
| 278 | static 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 | |||
| 282 | static inline short zig_subw_short(short lhs, short rhs, short min, short max) { | ||
| 283 | return (short)(((unsigned short)lhs) - ((unsigned short)rhs)); | ||
| 284 | } | ||
| 285 | |||
| 286 | static inline int zig_subw_int(int lhs, int rhs, int min, int max) { | ||
| 287 | return (int)(((unsigned)lhs) - ((unsigned)rhs)); | ||
| 288 | } | ||
| 289 | |||
| 290 | static inline long zig_subw_long(long lhs, long rhs, long min, long max) { | ||
| 291 | return (long)(((unsigned long)lhs) - ((unsigned long)rhs)); | ||
| 292 | } | ||
| 293 | |||
| 294 | static 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 { | ... | @@ -824,6 +824,56 @@ pub fn addCases(ctx: *TestContext) !void { |
| 824 | , ""); | 824 | , ""); |
| 825 | } | 825 | } |
| 826 | 826 | ||
| 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 | |||
| 827 | ctx.h("simple header", linux_x64, | 877 | ctx.h("simple header", linux_x64, |
| 828 | \\export fn start() void{} | 878 | \\export fn start() void{} |
| 829 | , | 879 | , |