| author | |
| committer | |
| log | 9dbe68485489e576795fee04c83e69a2b6cf776a |
| tree | ade71b33286590a21600e113046c62e89a4954c7 |
| parent | fb16633ecb496f3f30cdac11987baad40b7793b2 |
* 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 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 | .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 optimizer | 850 | // 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 optimizer | 854 | // 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 optimizer | 858 | // 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 | } |
| 1041 | 1041 | ||
| 1042 | const WrappingOp = enum { | 1042 | fn 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 | |||
| 1048 | fn 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; |
| 1051 | 1045 | ||
| 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; | ||
| 1053 | 1048 | ||
| 1054 | // if it's an unsigned int with non-arbitrary bit size then we can just add | 1049 | // 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 | } |
| 1062 | 1063 | ||
| 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 handled | 1074 | 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 integers | 1076 | 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 happen | 1079 | }, |
| 1079 | return if (e == error.NoSpaceLeft) unreachable else e; | ||
| 1080 | }, | 1080 | }, |
| 1081 | }; | 1081 | }; |
| 1082 | 1082 | ||
| ... | @@ -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 handled | 1096 | 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 integers | 1098 | .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 happen | 1101 | 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 | }; |
| 1105 | 1107 | ||
| ... | @@ -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(); |
| 1109 | 1111 | ||
| 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 | }); | ||
| 1117 | 1114 | ||
| 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| { |
| 1138 | 1127 | 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 | } |
| 1151 | 1136 | ||
| 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); |
| 1156 | 1141 | ||
| 1157 | if (is_signed) { | 1142 | if (int_info.signedness == .signed) { |
| 1158 | try w.print(", {s}", .{min}); | 1143 | try w.print(", {s}", .{min}); |
| 1159 | } | 1144 | } |
| 1160 | 1145 | ||
| ... | @@ -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 | } |
| 1166 | 1151 | ||
| 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 { |
| 1168 | if (inst.base.isUnused()) | 1153 | if (inst.base.isUnused()) |
| 1169 | return CValue.none; | 1154 | return CValue.none; |
| 1170 | 1155 | ||
| ... | @@ -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 { |
| 1176 | 1161 | ||
| 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"); |
| 1182 | 1167 |
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 | #endif | 61 | #endif |
| 62 | 62 | ||
| 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 __int128 | 67 | #define uint128_t unsigned __int128 |
| 113 | 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); |
| 114 | 69 | ||
| 115 | /* Wrapping addition operators */ | ||
| 116 | static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { | 70 | static 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 | } |
| 119 | 78 | ||
| 120 | static inline int8_t zig_addw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) { | 79 | static 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 | } |
| 123 | 93 | ||
| 124 | static inline uint16_t zig_addw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) { | 94 | static 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 | } |
| 127 | 102 | ||
| 128 | static inline int16_t zig_addw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) { | 103 | static 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 | } |
| 131 | 117 | ||
| 132 | static inline uint32_t zig_addw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) { | 118 | static 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 | } |
| 135 | 126 | ||
| 136 | static inline int32_t zig_addw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) { | 127 | static 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 | } |
| 139 | 141 | ||
| 140 | static inline uint64_t zig_addw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) { | 142 | static 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 | } |
| 143 | 150 | ||
| 144 | static inline int64_t zig_addw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) { | 151 | static 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 | } |
| 147 | 165 | ||
| 148 | static inline intptr_t zig_addw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) { | 166 | static 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 | } |
| 167 | 185 | ||
| 168 | /* Wrapping subtraction operators */ | ||
| 169 | static inline uint8_t zig_subw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { | 186 | static 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 | } |
| 172 | 193 | ||
| 173 | static inline int8_t zig_subw_i8(int8_t lhs, int8_t rhs, int8_t min, int8_t max) { | 194 | static 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 | } |
| 176 | 208 | ||
| 177 | static inline uint16_t zig_subw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) { | 209 | static 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 | } |
| 180 | 216 | ||
| 181 | static inline int16_t zig_subw_i16(int16_t lhs, int16_t rhs, int16_t min, int16_t max) { | 217 | static 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 | } |
| 184 | 231 | ||
| 185 | static inline uint32_t zig_subw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) { | 232 | static 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 | } |
| 188 | 239 | ||
| 189 | static inline int32_t zig_subw_i32(int32_t lhs, int32_t rhs, int32_t min, int32_t max) { | 240 | static 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 | } |
| 192 | 254 | ||
| 193 | static inline uint64_t zig_subw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) { | 255 | static 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 | } |
| 196 | 262 | ||
| 197 | static inline int64_t zig_subw_i64(int64_t lhs, int64_t rhs, int64_t min, int64_t max) { | 263 | static 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 | } |
| 200 | 277 | ||
| 201 | static inline intptr_t zig_subw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) { | 278 | static 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 | } |
| 826 | 826 | ||
| 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 | // Addition | 832 | \\ // 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; |
| 842 | 841 | \\ //if (!add_c_int(maxInt(c_int) + 1, -2, maxInt(c_int))) return 1; | |
| 843 | // Subtraction | 842 | \\ |
| 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; |
| 853 | 852 | \\ //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 | \\} |
| 869 | 868 | \\fn sub_i3(lhs: i3, rhs: i3, expected: i3) bool { | |
| 870 | break :blk ret; | 869 | \\ return expected == lhs -% rhs; |
| 871 | }; | 870 | \\} |
| 872 | 871 | \\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 | } |
| 875 | 876 | ||
| 876 | ctx.h("simple header", linux_x64, | 877 | ctx.h("simple header", linux_x64, |