| author | |
| committer | |
| log | fb16633ecb496f3f30cdac11987baad40b7793b2 |
| tree | 96d0b726a2fac3faa5f13777652806d846916025 |
| parent | 62d27fcfb687e3ab1f10c72513e19529d8ffceed |
3 files changed, 329 insertions(+), 6 deletions(-)
src/codegen/c.zig+128-6| ... | @@ -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, .add, inst.castTag(.addwrap).?), |
| 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, .sub, inst.castTag(.subwrap).?), |
| 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, .mul, inst.castTag(.mulwrap).?), |
| 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,6 +1039,131 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { | ... | @@ -1042,6 +1039,131 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { |
| 1042 | return CValue.none; | 1039 | return CValue.none; |
| 1043 | } | 1040 | } |
| 1044 | 1041 | ||
| 1042 | const WrappingOp = enum { | ||
| 1043 | add, | ||
| 1044 | sub, | ||
| 1045 | mul, | ||
| 1046 | }; | ||
| 1047 | |||
| 1048 | fn genWrapOp(o: *Object, op: WrappingOp, inst: *Inst.BinOp) !CValue { | ||
| 1049 | if (inst.base.isUnused()) | ||
| 1050 | return CValue.none; | ||
| 1051 | |||
| 1052 | const is_signed = inst.base.ty.isSignedInt(); | ||
| 1053 | |||
| 1054 | // 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 | }); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | 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; | ||
| 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 | // 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; | ||
| 1103 | }, | ||
| 1104 | }; | ||
| 1105 | |||
| 1106 | const lhs = try o.resolveInst(inst.lhs); | ||
| 1107 | const rhs = try o.resolveInst(inst.rhs); | ||
| 1108 | const w = o.writer(); | ||
| 1109 | |||
| 1110 | 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 | }); | ||
| 1117 | |||
| 1118 | 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"), | ||
| 1128 | .c_short => try w.writeAll("short"), | ||
| 1129 | .c_int => try w.writeAll("int"), | ||
| 1130 | .c_long => try w.writeAll("long"), | ||
| 1131 | .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}); | ||
| 1143 | break; | ||
| 1144 | } | ||
| 1145 | } else { | ||
| 1146 | return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 64 bits", .{}); | ||
| 1147 | } | ||
| 1148 | }, | ||
| 1149 | else => unreachable, | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | try w.writeByte('('); | ||
| 1153 | try o.writeCValue(w, lhs); | ||
| 1154 | try w.writeAll(", "); | ||
| 1155 | try o.writeCValue(w, rhs); | ||
| 1156 | |||
| 1157 | if (is_signed) { | ||
| 1158 | try w.print(", {s}", .{min}); | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | try w.print(", {s});", .{max}); | ||
| 1162 | try o.indent_writer.insertNewline(); | ||
| 1163 | |||
| 1164 | return ret; | ||
| 1165 | } | ||
| 1166 | |||
| 1045 | fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { | 1167 | fn genBinOp(o: *Object, inst: *Inst.BinOp, operator: []const u8) !CValue { |
| 1046 | if (inst.base.isUnused()) | 1168 | if (inst.base.isUnused()) |
| 1047 | return CValue.none; | 1169 | return CValue.none; |
src/link/C/zig.h+152| ... | @@ -60,9 +60,161 @@ | ... | @@ -60,9 +60,161 @@ |
| 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 | |||
| 63 | #include <stdint.h> | 108 | #include <stdint.h> |
| 64 | #include <stddef.h> | 109 | #include <stddef.h> |
| 110 | #include <limits.h> | ||
| 65 | #define int128_t __int128 | 111 | #define int128_t __int128 |
| 66 | #define uint128_t unsigned __int128 | 112 | #define uint128_t unsigned __int128 |
| 67 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); | 113 | ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t); |
| 68 | 114 | ||
| 115 | /* Wrapping addition operators */ | ||
| 116 | static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { | ||
| 117 | ZIG_UADDW(uint8_t, lhs, rhs, max); | ||
| 118 | } | ||
| 119 | |||
| 120 | 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); | ||
| 122 | } | ||
| 123 | |||
| 124 | static inline uint16_t zig_addw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) { | ||
| 125 | ZIG_UADDW(uint16_t, lhs, rhs, max); | ||
| 126 | } | ||
| 127 | |||
| 128 | 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); | ||
| 130 | } | ||
| 131 | |||
| 132 | static inline uint32_t zig_addw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) { | ||
| 133 | ZIG_UADDW(uint32_t, lhs, rhs, max); | ||
| 134 | } | ||
| 135 | |||
| 136 | 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); | ||
| 138 | } | ||
| 139 | |||
| 140 | static inline uint64_t zig_addw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) { | ||
| 141 | ZIG_UADDW(uint64_t, lhs, rhs, max); | ||
| 142 | } | ||
| 143 | |||
| 144 | 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); | ||
| 146 | } | ||
| 147 | |||
| 148 | static inline intptr_t zig_addw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) { | ||
| 149 | return (intptr_t)(((uintptr_t)lhs) + ((uintptr_t)rhs)); | ||
| 150 | } | ||
| 151 | |||
| 152 | static inline short zig_addw_short(short lhs, short rhs, short min, short max) { | ||
| 153 | return (short)(((unsigned short)lhs) + ((unsigned short)rhs)); | ||
| 154 | } | ||
| 155 | |||
| 156 | static inline int zig_addw_int(int lhs, int rhs, int min, int max) { | ||
| 157 | return (int)(((unsigned)lhs) + ((unsigned)rhs)); | ||
| 158 | } | ||
| 159 | |||
| 160 | static inline long zig_addw_long(long lhs, long rhs, long min, long max) { | ||
| 161 | return (long)(((unsigned long)lhs) + ((unsigned long)rhs)); | ||
| 162 | } | ||
| 163 | |||
| 164 | static inline long long zig_addw_longlong(long long lhs, long long rhs, long long min, long long max) { | ||
| 165 | return (long long)(((unsigned long long)lhs) + ((unsigned long long)rhs)); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Wrapping subtraction operators */ | ||
| 169 | static inline uint8_t zig_subw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) { | ||
| 170 | ZIG_USUBW(lhs, rhs, max); | ||
| 171 | } | ||
| 172 | |||
| 173 | 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); | ||
| 175 | } | ||
| 176 | |||
| 177 | static inline uint16_t zig_subw_u16(uint16_t lhs, uint16_t rhs, uint16_t max) { | ||
| 178 | ZIG_USUBW(lhs, rhs, max); | ||
| 179 | } | ||
| 180 | |||
| 181 | 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); | ||
| 183 | } | ||
| 184 | |||
| 185 | static inline uint32_t zig_subw_u32(uint32_t lhs, uint32_t rhs, uint32_t max) { | ||
| 186 | ZIG_USUBW(lhs, rhs, max); | ||
| 187 | } | ||
| 188 | |||
| 189 | 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); | ||
| 191 | } | ||
| 192 | |||
| 193 | static inline uint64_t zig_subw_u64(uint64_t lhs, uint64_t rhs, uint64_t max) { | ||
| 194 | ZIG_USUBW(lhs, rhs, max); | ||
| 195 | } | ||
| 196 | |||
| 197 | 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); | ||
| 199 | } | ||
| 200 | |||
| 201 | static inline intptr_t zig_subw_isize(intptr_t lhs, intptr_t rhs, intptr_t min, intptr_t max) { | ||
| 202 | return (intptr_t)(((uintptr_t)lhs) - ((uintptr_t)rhs)); | ||
| 203 | } | ||
| 204 | |||
| 205 | static inline short zig_subw_short(short lhs, short rhs, short min, short max) { | ||
| 206 | return (short)(((unsigned short)lhs) - ((unsigned short)rhs)); | ||
| 207 | } | ||
| 208 | |||
| 209 | static inline int zig_subw_int(int lhs, int rhs, int min, int max) { | ||
| 210 | return (int)(((unsigned)lhs) - ((unsigned)rhs)); | ||
| 211 | } | ||
| 212 | |||
| 213 | static inline long zig_subw_long(long lhs, long rhs, long min, long max) { | ||
| 214 | return (long)(((unsigned long)lhs) - ((unsigned long)rhs)); | ||
| 215 | } | ||
| 216 | |||
| 217 | static inline long long zig_subw_longlong(long long lhs, long long rhs, long long min, long long max) { | ||
| 218 | return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs)); | ||
| 219 | } | ||
| 220 |
test/stage2/cbe.zig+49| ... | @@ -824,6 +824,55 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -824,6 +824,55 @@ pub fn addCases(ctx: *TestContext) !void { |
| 824 | , ""); | 824 | , ""); |
| 825 | } | 825 | } |
| 826 | 826 | ||
| 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) | ||
| 830 | var case = ctx.exeFromCompiledC("Wrapping operations", .{}); | ||
| 831 | const programs = comptime blk: { | ||
| 832 | const cases = .{ | ||
| 833 | // Addition | ||
| 834 | .{ u3, "+%", 1, 1, 2 }, | ||
| 835 | .{ u3, "+%", 7, 1, 0 }, | ||
| 836 | .{ i3, "+%", 1, 1, 2 }, | ||
| 837 | .{ i3, "+%", 3, 2, -3 }, | ||
| 838 | .{ i3, "+%", -3, -2, 3 }, | ||
| 839 | .{ c_int, "+%", 1, 1, 2 }, | ||
| 840 | .{ c_int, "+%", std.math.maxInt(c_int), 2, std.math.minInt(c_int) + 1 }, | ||
| 841 | .{ c_int, "+%", std.math.minInt(c_int) + 1, -2, std.math.maxInt(c_int) }, | ||
| 842 | |||
| 843 | // Subtraction | ||
| 844 | .{ u3, "-%", 2, 1, 1 }, | ||
| 845 | .{ u3, "-%", 0, 1, 7 }, | ||
| 846 | .{ i3, "-%", 2, 1, 1 }, | ||
| 847 | .{ i3, "-%", 3, -2, -3 }, | ||
| 848 | .{ i3, "-%", -3, 2, 3 }, | ||
| 849 | .{ c_int, "-%", 2, 1, 1 }, | ||
| 850 | .{ c_int, "-%", std.math.maxInt(c_int), -2, std.math.minInt(c_int) + 1 }, | ||
| 851 | .{ c_int, "-%", std.math.minInt(c_int) + 1, 2, std.math.maxInt(c_int) }, | ||
| 852 | }; | ||
| 853 | |||
| 854 | var ret: [cases.len][:0]const u8 = undefined; | ||
| 855 | for (cases) |c, i| ret[i] = std.fmt.comptimePrint( | ||
| 856 | \\export fn main() i32 {{ | ||
| 857 | \\ var lhs: {0} = {2}; | ||
| 858 | \\ var rhs: {0} = {3}; | ||
| 859 | \\ var expected: {0} = {4}; | ||
| 860 | \\ | ||
| 861 | \\ if (expected != lhs {1s} rhs) {{ | ||
| 862 | \\ return 1; | ||
| 863 | \\ }} else {{ | ||
| 864 | \\ return 0; | ||
| 865 | \\ }} | ||
| 866 | \\}} | ||
| 867 | \\ | ||
| 868 | , c); | ||
| 869 | |||
| 870 | break :blk ret; | ||
| 871 | }; | ||
| 872 | |||
| 873 | inline for (programs) |prog| case.addCompareOutput(prog, ""); | ||
| 874 | } | ||
| 875 | |||
| 827 | ctx.h("simple header", linux_x64, | 876 | ctx.h("simple header", linux_x64, |
| 828 | \\export fn start() void{} | 877 | \\export fn start() void{} |
| 829 | , | 878 | , |