| ... | ... | @@ -157,40 +157,6 @@ const MCValue = union(enum) { |
| 157 | 157 | condition_flags: Condition, |
| 158 | 158 | /// The value is a function argument passed via the stack. |
| 159 | 159 | stack_argument_offset: u32, |
| 160 | | |
| 161 | | fn isMemory(mcv: MCValue) bool { |
| 162 | | return switch (mcv) { |
| 163 | | .memory, .stack_offset, .stack_argument_offset => true, |
| 164 | | else => false, |
| 165 | | }; |
| 166 | | } |
| 167 | | |
| 168 | | fn isImmediate(mcv: MCValue) bool { |
| 169 | | return switch (mcv) { |
| 170 | | .immediate => true, |
| 171 | | else => false, |
| 172 | | }; |
| 173 | | } |
| 174 | | |
| 175 | | fn isMutable(mcv: MCValue) bool { |
| 176 | | return switch (mcv) { |
| 177 | | .none => unreachable, |
| 178 | | .unreach => unreachable, |
| 179 | | .dead => unreachable, |
| 180 | | |
| 181 | | .immediate, |
| 182 | | .memory, |
| 183 | | .condition_flags, |
| 184 | | .ptr_stack_offset, |
| 185 | | .undef, |
| 186 | | .stack_argument_offset, |
| 187 | | => false, |
| 188 | | |
| 189 | | .register, |
| 190 | | .stack_offset, |
| 191 | | => true, |
| 192 | | }; |
| 193 | | } |
| 194 | 160 | }; |
| 195 | 161 | |
| 196 | 162 | const Branch = struct { |
| ... | ... | @@ -414,11 +380,9 @@ fn gen(self: *Self) !void { |
| 414 | 380 | // to the stack. |
| 415 | 381 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 416 | 382 | const ptr_bytes = @divExact(ptr_bits, 8); |
| 417 | | const ret_ptr_reg = registerAlias(.x0, ptr_bytes); |
| 383 | const ret_ptr_reg = self.registerAlias(.x0, Type.usize); |
| 418 | 384 | |
| 419 | | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, ptr_bytes) + ptr_bytes; |
| 420 | | self.next_stack_offset = stack_offset; |
| 421 | | self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); |
| 385 | const stack_offset = try self.allocMem(ptr_bytes, ptr_bytes, null); |
| 422 | 386 | |
| 423 | 387 | try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg }); |
| 424 | 388 | self.ret_mcv = MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -879,17 +843,30 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { |
| 879 | 843 | } |
| 880 | 844 | } |
| 881 | 845 | |
| 882 | | fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { |
| 846 | fn allocMem( |
| 847 | self: *Self, |
| 848 | abi_size: u32, |
| 849 | abi_align: u32, |
| 850 | maybe_inst: ?Air.Inst.Index, |
| 851 | ) !u32 { |
| 852 | assert(abi_size > 0); |
| 853 | assert(abi_align > 0); |
| 854 | |
| 883 | 855 | if (abi_align > self.stack_align) |
| 884 | 856 | self.stack_align = abi_align; |
| 857 | |
| 885 | 858 | // TODO find a free slot instead of always appending |
| 886 | 859 | const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; |
| 887 | 860 | self.next_stack_offset = offset; |
| 888 | 861 | self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); |
| 889 | | try self.stack.putNoClobber(self.gpa, offset, .{ |
| 890 | | .inst = inst, |
| 891 | | .size = abi_size, |
| 892 | | }); |
| 862 | |
| 863 | if (maybe_inst) |inst| { |
| 864 | try self.stack.putNoClobber(self.gpa, offset, .{ |
| 865 | .inst = inst, |
| 866 | .size = abi_size, |
| 867 | }); |
| 868 | } |
| 869 | |
| 893 | 870 | return offset; |
| 894 | 871 | } |
| 895 | 872 | |
| ... | ... | @@ -910,40 +887,41 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 910 | 887 | }; |
| 911 | 888 | // TODO swap this for inst.ty.ptrAlign |
| 912 | 889 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 913 | | return self.allocMem(inst, abi_size, abi_align); |
| 890 | |
| 891 | return self.allocMem(abi_size, abi_align, inst); |
| 914 | 892 | } |
| 915 | 893 | |
| 916 | | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 917 | | const elem_ty = self.air.typeOfIndex(inst); |
| 894 | fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { |
| 918 | 895 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { |
| 919 | 896 | const mod = self.bin_file.options.module.?; |
| 920 | 897 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); |
| 921 | 898 | }; |
| 922 | 899 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 923 | | if (abi_align > self.stack_align) |
| 924 | | self.stack_align = abi_align; |
| 925 | 900 | |
| 926 | 901 | if (reg_ok) { |
| 927 | 902 | // Make sure the type can fit in a register before we try to allocate one. |
| 928 | 903 | if (abi_size <= 8) { |
| 929 | | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { |
| 930 | | return MCValue{ .register = registerAlias(reg, abi_size) }; |
| 904 | if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| { |
| 905 | return MCValue{ .register = self.registerAlias(reg, elem_ty) }; |
| 931 | 906 | } |
| 932 | 907 | } |
| 933 | 908 | } |
| 934 | | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 909 | |
| 910 | const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst); |
| 935 | 911 | return MCValue{ .stack_offset = stack_offset }; |
| 936 | 912 | } |
| 937 | 913 | |
| 938 | 914 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 939 | | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 915 | const stack_mcv = try self.allocRegOrMem(self.air.typeOfIndex(inst), false, inst); |
| 940 | 916 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 917 | |
| 941 | 918 | const reg_mcv = self.getResolvedInstValue(inst); |
| 942 | 919 | switch (reg_mcv) { |
| 943 | 920 | .register => |r| assert(reg.id() == r.id()), |
| 944 | 921 | .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()), |
| 945 | 922 | else => unreachable, // not a register |
| 946 | 923 | } |
| 924 | |
| 947 | 925 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 948 | 926 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 949 | 927 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| ... | ... | @@ -953,10 +931,11 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 953 | 931 | /// occupied |
| 954 | 932 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 955 | 933 | if (self.condition_flags_inst) |inst_to_save| { |
| 934 | const ty = self.air.typeOfIndex(inst_to_save); |
| 956 | 935 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 957 | 936 | const new_mcv = switch (mcv) { |
| 958 | | .condition_flags => try self.allocRegOrMem(inst_to_save, true), |
| 959 | | .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), |
| 937 | .condition_flags => try self.allocRegOrMem(ty, true, inst_to_save), |
| 938 | .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save), |
| 960 | 939 | else => unreachable, // mcv doesn't occupy the compare flags |
| 961 | 940 | }; |
| 962 | 941 | |
| ... | ... | @@ -982,7 +961,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 982 | 961 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 983 | 962 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 984 | 963 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 985 | | const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); |
| 964 | const reg = self.registerAlias(raw_reg, ty); |
| 986 | 965 | try self.genSetReg(ty, reg, mcv); |
| 987 | 966 | return reg; |
| 988 | 967 | } |
| ... | ... | @@ -993,7 +972,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 993 | 972 | fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { |
| 994 | 973 | const raw_reg = try self.register_manager.allocReg(reg_owner, gp); |
| 995 | 974 | const ty = self.air.typeOfIndex(reg_owner); |
| 996 | | const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); |
| 975 | const reg = self.registerAlias(raw_reg, ty); |
| 997 | 976 | try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); |
| 998 | 977 | return MCValue{ .register = reg }; |
| 999 | 978 | } |
| ... | ... | @@ -1031,7 +1010,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1031 | 1010 | const operand_info = operand_ty.intInfo(self.target.*); |
| 1032 | 1011 | |
| 1033 | 1012 | const dest_ty = self.air.typeOfIndex(inst); |
| 1034 | | const dest_abi_size = dest_ty.abiSize(self.target.*); |
| 1035 | 1013 | const dest_info = dest_ty.intInfo(self.target.*); |
| 1036 | 1014 | |
| 1037 | 1015 | const result: MCValue = result: { |
| ... | ... | @@ -1042,19 +1020,19 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1042 | 1020 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 1043 | 1021 | |
| 1044 | 1022 | const truncated: MCValue = switch (operand_mcv) { |
| 1045 | | .register => |r| MCValue{ .register = registerAlias(r, dest_abi_size) }, |
| 1023 | .register => |r| MCValue{ .register = self.registerAlias(r, dest_ty) }, |
| 1046 | 1024 | else => operand_mcv, |
| 1047 | 1025 | }; |
| 1048 | 1026 | |
| 1049 | 1027 | if (dest_info.bits > operand_info.bits) { |
| 1050 | | const dest_mcv = try self.allocRegOrMem(inst, true); |
| 1028 | const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst); |
| 1051 | 1029 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); |
| 1052 | 1030 | break :result dest_mcv; |
| 1053 | 1031 | } else { |
| 1054 | 1032 | if (self.reuseOperand(inst, operand, 0, truncated)) { |
| 1055 | 1033 | break :result truncated; |
| 1056 | 1034 | } else { |
| 1057 | | const dest_mcv = try self.allocRegOrMem(inst, true); |
| 1035 | const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst); |
| 1058 | 1036 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); |
| 1059 | 1037 | break :result dest_mcv; |
| 1060 | 1038 | } |
| ... | ... | @@ -1117,7 +1095,7 @@ fn trunc( |
| 1117 | 1095 | else => operand_reg: { |
| 1118 | 1096 | if (info_a.bits <= 64) { |
| 1119 | 1097 | const raw_reg = try self.copyToTmpRegister(operand_ty, operand); |
| 1120 | | break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); |
| 1098 | break :operand_reg self.registerAlias(raw_reg, operand_ty); |
| 1121 | 1099 | } else { |
| 1122 | 1100 | return self.fail("TODO load least significant word into register", .{}); |
| 1123 | 1101 | } |
| ... | ... | @@ -1130,14 +1108,14 @@ fn trunc( |
| 1130 | 1108 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1131 | 1109 | |
| 1132 | 1110 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { |
| 1133 | | break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); |
| 1111 | break :blk self.registerAlias(operand_reg, dest_ty); |
| 1134 | 1112 | } else { |
| 1135 | 1113 | const raw_reg = try self.register_manager.allocReg(inst, gp); |
| 1136 | | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); |
| 1114 | break :blk self.registerAlias(raw_reg, dest_ty); |
| 1137 | 1115 | } |
| 1138 | 1116 | } else blk: { |
| 1139 | 1117 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1140 | | break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); |
| 1118 | break :blk self.registerAlias(raw_reg, dest_ty); |
| 1141 | 1119 | }; |
| 1142 | 1120 | |
| 1143 | 1121 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); |
| ... | ... | @@ -1194,7 +1172,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1194 | 1172 | } |
| 1195 | 1173 | |
| 1196 | 1174 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1197 | | break :blk raw_reg.to32(); |
| 1175 | break :blk self.registerAlias(raw_reg, operand_ty); |
| 1198 | 1176 | }; |
| 1199 | 1177 | |
| 1200 | 1178 | _ = try self.addInst(.{ |
| ... | ... | @@ -1227,7 +1205,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1227 | 1205 | } |
| 1228 | 1206 | |
| 1229 | 1207 | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1230 | | break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); |
| 1208 | break :blk self.registerAlias(raw_reg, operand_ty); |
| 1231 | 1209 | }; |
| 1232 | 1210 | |
| 1233 | 1211 | _ = try self.addInst(.{ |
| ... | ... | @@ -1279,7 +1257,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1279 | 1257 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 1280 | 1258 | const ptr_bytes = @divExact(ptr_bits, 8); |
| 1281 | 1259 | |
| 1282 | | const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); |
| 1260 | const stack_offset = try self.allocMem(ptr_bytes * 2, ptr_bytes * 2, inst); |
| 1283 | 1261 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 1284 | 1262 | try self.genSetStack(len_ty, stack_offset - ptr_bytes, len); |
| 1285 | 1263 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -1287,101 +1265,266 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1287 | 1265 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1288 | 1266 | } |
| 1289 | 1267 | |
| 1290 | | /// Don't call this function directly. Use binOp instead. |
| 1268 | /// An argument to a Mir instruction which is read (and possibly also |
| 1269 | /// written to) by the respective instruction |
| 1270 | const ReadArg = struct { |
| 1271 | ty: Type, |
| 1272 | bind: Bind, |
| 1273 | class: RegisterManager.RegisterBitSet, |
| 1274 | reg: *Register, |
| 1275 | |
| 1276 | const Bind = union(enum) { |
| 1277 | inst: Air.Inst.Ref, |
| 1278 | mcv: MCValue, |
| 1279 | |
| 1280 | fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue { |
| 1281 | return switch (bind) { |
| 1282 | .inst => |inst| try function.resolveInst(inst), |
| 1283 | .mcv => |mcv| mcv, |
| 1284 | }; |
| 1285 | } |
| 1286 | |
| 1287 | fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u64 { |
| 1288 | switch (bind) { |
| 1289 | .inst => |inst| { |
| 1290 | // TODO resolve independently of inst_table |
| 1291 | const mcv = try function.resolveInst(inst); |
| 1292 | switch (mcv) { |
| 1293 | .immediate => |imm| return imm, |
| 1294 | else => return null, |
| 1295 | } |
| 1296 | }, |
| 1297 | .mcv => |mcv| { |
| 1298 | switch (mcv) { |
| 1299 | .immediate => |imm| return imm, |
| 1300 | else => return null, |
| 1301 | } |
| 1302 | }, |
| 1303 | } |
| 1304 | } |
| 1305 | }; |
| 1306 | }; |
| 1307 | |
| 1308 | /// An argument to a Mir instruction which is written to (but not read |
| 1309 | /// from) by the respective instruction |
| 1310 | const WriteArg = struct { |
| 1311 | ty: Type, |
| 1312 | bind: Bind, |
| 1313 | class: RegisterManager.RegisterBitSet, |
| 1314 | reg: *Register, |
| 1315 | |
| 1316 | const Bind = union(enum) { |
| 1317 | reg: Register, |
| 1318 | none: void, |
| 1319 | }; |
| 1320 | }; |
| 1321 | |
| 1322 | /// Holds all data necessary for enabling the potential reuse of |
| 1323 | /// operand registers as destinations |
| 1324 | const ReuseMetadata = struct { |
| 1325 | corresponding_inst: Air.Inst.Index, |
| 1326 | |
| 1327 | /// Maps every element index of read_args to the corresponding |
| 1328 | /// index in the Air instruction |
| 1329 | /// |
| 1330 | /// When the order of read_args corresponds exactly to the order |
| 1331 | /// of the inputs of the Air instruction, this would be e.g. |
| 1332 | /// &.{ 0, 1 }. However, when the order is not the same or some |
| 1333 | /// inputs to the Air instruction are omitted (e.g. when they can |
| 1334 | /// be represented as immediates to the Mir instruction), |
| 1335 | /// operand_mapping should reflect that fact. |
| 1336 | operand_mapping: []const Liveness.OperandInt, |
| 1337 | }; |
| 1338 | |
| 1339 | /// Allocate a set of registers for use as arguments for a Mir |
| 1340 | /// instruction |
| 1291 | 1341 | /// |
| 1292 | | /// Calling this function signals an intention to generate a Mir |
| 1293 | | /// instruction of the form |
| 1342 | /// If the Mir instruction these registers are allocated for |
| 1343 | /// corresponds exactly to a single Air instruction, populate |
| 1344 | /// reuse_metadata in order to enable potential reuse of an operand as |
| 1345 | /// the destination (provided that that operand dies in this |
| 1346 | /// instruction). |
| 1294 | 1347 | /// |
| 1295 | | /// op dest, lhs, rhs |
| 1348 | /// Reusing an operand register as destination is the only time two |
| 1349 | /// arguments may share the same register. In all other cases, |
| 1350 | /// allocRegs guarantees that a register will never be allocated to |
| 1351 | /// more than one argument. |
| 1296 | 1352 | /// |
| 1297 | | /// Asserts that generating an instruction of that form is possible. |
| 1298 | | fn binOpRegister( |
| 1353 | /// Furthermore, allocReg guarantees that all arguments which are |
| 1354 | /// already bound to registers before calling allocRegs will not |
| 1355 | /// change their register binding. This is done by locking these |
| 1356 | /// registers. |
| 1357 | fn allocRegs( |
| 1299 | 1358 | self: *Self, |
| 1300 | | mir_tag: Mir.Inst.Tag, |
| 1301 | | lhs: MCValue, |
| 1302 | | rhs: MCValue, |
| 1303 | | lhs_ty: Type, |
| 1304 | | rhs_ty: Type, |
| 1305 | | metadata: ?BinOpMetadata, |
| 1306 | | ) !MCValue { |
| 1307 | | const lhs_is_register = lhs == .register; |
| 1308 | | const rhs_is_register = rhs == .register; |
| 1359 | read_args: []const ReadArg, |
| 1360 | write_args: []const WriteArg, |
| 1361 | reuse_metadata: ?ReuseMetadata, |
| 1362 | ) InnerError!void { |
| 1363 | // Air instructions have exactly one output |
| 1364 | assert(!(reuse_metadata != null and write_args.len != 1)); // see note above |
| 1365 | |
| 1366 | // The operand mapping is a 1:1 mapping of read args to their |
| 1367 | // corresponding operand index in the Air instruction |
| 1368 | assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above |
| 1369 | |
| 1370 | const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len); |
| 1371 | defer self.gpa.free(locks); |
| 1372 | const read_locks = locks[0..read_args.len]; |
| 1373 | const write_locks = locks[read_args.len..]; |
| 1374 | |
| 1375 | std.mem.set(?RegisterLock, locks, null); |
| 1376 | defer for (locks) |lock| { |
| 1377 | if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg); |
| 1378 | }; |
| 1309 | 1379 | |
| 1310 | | if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*))); |
| 1311 | | if (rhs_is_register) assert(rhs.register == registerAlias(rhs.register, rhs_ty.abiSize(self.target.*))); |
| 1380 | // When we reuse a read_arg as a destination, the corresponding |
| 1381 | // MCValue of the read_arg will be set to .dead. In that case, we |
| 1382 | // skip allocating this read_arg. |
| 1383 | var reused_read_arg: ?usize = null; |
| 1312 | 1384 | |
| 1313 | | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 1314 | | self.register_manager.lockReg(lhs.register) |
| 1315 | | else |
| 1316 | | null; |
| 1317 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1385 | // Lock all args which are already allocated to registers |
| 1386 | for (read_args) |arg, i| { |
| 1387 | const mcv = try arg.bind.resolveToMcv(self); |
| 1388 | if (mcv == .register) { |
| 1389 | read_locks[i] = self.register_manager.lockReg(mcv.register); |
| 1390 | } |
| 1391 | } |
| 1318 | 1392 | |
| 1319 | | const rhs_lock: ?RegisterLock = if (rhs_is_register) |
| 1320 | | self.register_manager.lockReg(rhs.register) |
| 1321 | | else |
| 1322 | | null; |
| 1323 | | defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1393 | for (write_args) |arg, i| { |
| 1394 | if (arg.bind == .reg) { |
| 1395 | write_locks[i] = self.register_manager.lockReg(arg.bind.reg); |
| 1396 | } |
| 1397 | } |
| 1324 | 1398 | |
| 1325 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1399 | // Allocate registers for all args which aren't allocated to |
| 1400 | // registers yet |
| 1401 | for (read_args) |arg, i| { |
| 1402 | const mcv = try arg.bind.resolveToMcv(self); |
| 1403 | if (mcv == .register) { |
| 1404 | const raw_reg = mcv.register; |
| 1405 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1406 | } else { |
| 1407 | const track_inst: ?Air.Inst.Index = switch (arg.bind) { |
| 1408 | .inst => |inst| Air.refToIndex(inst).?, |
| 1409 | else => null, |
| 1410 | }; |
| 1411 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1412 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1413 | read_locks[i] = self.register_manager.lockReg(arg.reg.*); |
| 1414 | } |
| 1415 | } |
| 1326 | 1416 | |
| 1327 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1328 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1329 | | break :inst Air.refToIndex(md.lhs).?; |
| 1330 | | } else null; |
| 1417 | if (reuse_metadata != null) { |
| 1418 | const inst = reuse_metadata.?.corresponding_inst; |
| 1419 | const operand_mapping = reuse_metadata.?.operand_mapping; |
| 1420 | const arg = write_args[0]; |
| 1421 | if (arg.bind == .reg) { |
| 1422 | const raw_reg = arg.bind.reg; |
| 1423 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1424 | } else { |
| 1425 | reuse_operand: for (read_args) |read_arg, i| { |
| 1426 | if (read_arg.bind == .inst) { |
| 1427 | const operand = read_arg.bind.inst; |
| 1428 | const mcv = try self.resolveInst(operand); |
| 1429 | if (mcv == .register and |
| 1430 | std.meta.eql(arg.class, read_arg.class) and |
| 1431 | self.reuseOperand(inst, operand, operand_mapping[i], mcv)) |
| 1432 | { |
| 1433 | const raw_reg = mcv.register; |
| 1434 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1435 | write_locks[0] = null; |
| 1436 | reused_read_arg = i; |
| 1437 | break :reuse_operand; |
| 1438 | } |
| 1439 | } |
| 1440 | } else { |
| 1441 | const raw_reg = try self.register_manager.allocReg(inst, arg.class); |
| 1442 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1443 | write_locks[0] = self.register_manager.lockReg(arg.reg.*); |
| 1444 | } |
| 1445 | } |
| 1446 | } else { |
| 1447 | for (write_args) |arg, i| { |
| 1448 | if (arg.bind == .reg) { |
| 1449 | const raw_reg = arg.bind.reg; |
| 1450 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1451 | } else { |
| 1452 | const raw_reg = try self.register_manager.allocReg(null, arg.class); |
| 1453 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1454 | write_locks[i] = self.register_manager.lockReg(arg.reg.*); |
| 1455 | } |
| 1456 | } |
| 1457 | } |
| 1331 | 1458 | |
| 1332 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1333 | | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1459 | // For all read_args which need to be moved from non-register to |
| 1460 | // register, perform the move |
| 1461 | for (read_args) |arg, i| { |
| 1462 | if (reused_read_arg) |j| { |
| 1463 | // Check whether this read_arg was reused |
| 1464 | if (i == j) continue; |
| 1465 | } |
| 1334 | 1466 | |
| 1335 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1467 | const mcv = try arg.bind.resolveToMcv(self); |
| 1468 | if (mcv != .register) { |
| 1469 | if (arg.bind == .inst) { |
| 1470 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1471 | const inst = Air.refToIndex(arg.bind.inst).?; |
| 1336 | 1472 | |
| 1337 | | break :blk reg; |
| 1338 | | }; |
| 1339 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 1340 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1341 | | |
| 1342 | | const rhs_reg = if (rhs_is_register) |
| 1343 | | // lhs is almost always equal to rhs, except in shifts. In |
| 1344 | | // order to guarantee that registers will have equal sizes, we |
| 1345 | | // use the register alias of rhs corresponding to the size of |
| 1346 | | // lhs. |
| 1347 | | registerAlias(rhs.register, lhs_ty.abiSize(self.target.*)) |
| 1348 | | else blk: { |
| 1349 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1350 | | break :inst Air.refToIndex(md.rhs).?; |
| 1351 | | } else null; |
| 1473 | // Overwrite the MCValue associated with this inst |
| 1474 | branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* }); |
| 1352 | 1475 | |
| 1353 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1476 | // If the previous MCValue occupied some space we track, we |
| 1477 | // need to make sure it is marked as free now. |
| 1478 | switch (mcv) { |
| 1479 | .condition_flags => { |
| 1480 | assert(self.condition_flags_inst.? == inst); |
| 1481 | self.condition_flags_inst = null; |
| 1482 | }, |
| 1483 | .register => |prev_reg| { |
| 1484 | assert(!self.register_manager.isRegFree(prev_reg)); |
| 1485 | self.register_manager.freeReg(prev_reg); |
| 1486 | }, |
| 1487 | else => {}, |
| 1488 | } |
| 1489 | } |
| 1354 | 1490 | |
| 1355 | | // Here, we deliberately use lhs as lhs and rhs may differ in |
| 1356 | | // the case of shifts. See comment above. |
| 1357 | | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1491 | try self.genSetReg(arg.ty, arg.reg.*, mcv); |
| 1492 | } |
| 1493 | } |
| 1494 | } |
| 1358 | 1495 | |
| 1359 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1496 | /// Wrapper around allocRegs and addInst tailored for specific Mir |
| 1497 | /// instructions which are binary operations acting on two registers |
| 1498 | /// |
| 1499 | /// Returns the destination register |
| 1500 | fn binOpRegister( |
| 1501 | self: *Self, |
| 1502 | mir_tag: Mir.Inst.Tag, |
| 1503 | lhs_bind: ReadArg.Bind, |
| 1504 | rhs_bind: ReadArg.Bind, |
| 1505 | lhs_ty: Type, |
| 1506 | rhs_ty: Type, |
| 1507 | maybe_inst: ?Air.Inst.Index, |
| 1508 | ) !MCValue { |
| 1509 | var lhs_reg: Register = undefined; |
| 1510 | var rhs_reg: Register = undefined; |
| 1511 | var dest_reg: Register = undefined; |
| 1360 | 1512 | |
| 1361 | | break :blk reg; |
| 1513 | const read_args = [_]ReadArg{ |
| 1514 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 1515 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 1362 | 1516 | }; |
| 1363 | | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); |
| 1364 | | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1365 | | |
| 1366 | | const dest_reg = switch (mir_tag) { |
| 1367 | | .cmp_shifted_register => undefined, // cmp has no destination register |
| 1368 | | else => if (metadata) |md| blk: { |
| 1369 | | if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { |
| 1370 | | break :blk lhs_reg; |
| 1371 | | } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { |
| 1372 | | break :blk rhs_reg; |
| 1373 | | } else { |
| 1374 | | const raw_reg = try self.register_manager.allocReg(md.inst, gp); |
| 1375 | | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1376 | | } |
| 1377 | | } else blk: { |
| 1378 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1379 | | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1380 | | }, |
| 1517 | const write_args = [_]WriteArg{ |
| 1518 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 1381 | 1519 | }; |
| 1382 | | |
| 1383 | | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1384 | | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1520 | try self.allocRegs( |
| 1521 | &read_args, |
| 1522 | &write_args, |
| 1523 | if (maybe_inst) |inst| .{ |
| 1524 | .corresponding_inst = inst, |
| 1525 | .operand_mapping = &.{ 0, 1 }, |
| 1526 | } else null, |
| 1527 | ); |
| 1385 | 1528 | |
| 1386 | 1529 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1387 | 1530 | .add_shifted_register, |
| ... | ... | @@ -1395,12 +1538,6 @@ fn binOpRegister( |
| 1395 | 1538 | .imm6 = 0, |
| 1396 | 1539 | .shift = .lsl, |
| 1397 | 1540 | } }, |
| 1398 | | .cmp_shifted_register => .{ .rr_imm6_shift = .{ |
| 1399 | | .rn = lhs_reg, |
| 1400 | | .rm = rhs_reg, |
| 1401 | | .imm6 = 0, |
| 1402 | | .shift = .lsl, |
| 1403 | | } }, |
| 1404 | 1541 | .mul, |
| 1405 | 1542 | .lsl_register, |
| 1406 | 1543 | .asr_register, |
| ... | ... | @@ -1415,7 +1552,7 @@ fn binOpRegister( |
| 1415 | 1552 | .smull, |
| 1416 | 1553 | .umull, |
| 1417 | 1554 | => .{ .rrr = .{ |
| 1418 | | .rd = dest_reg.to64(), |
| 1555 | .rd = dest_reg.toX(), |
| 1419 | 1556 | .rn = lhs_reg, |
| 1420 | 1557 | .rm = rhs_reg, |
| 1421 | 1558 | } }, |
| ... | ... | @@ -1440,77 +1577,38 @@ fn binOpRegister( |
| 1440 | 1577 | return MCValue{ .register = dest_reg }; |
| 1441 | 1578 | } |
| 1442 | 1579 | |
| 1443 | | /// Don't call this function directly. Use binOp instead. |
| 1444 | | /// |
| 1445 | | /// Calling this function signals an intention to generate a Mir |
| 1446 | | /// instruction of the form |
| 1580 | /// Wrapper around allocRegs and addInst tailored for specific Mir |
| 1581 | /// instructions which are binary operations acting on a register and |
| 1582 | /// an immediate |
| 1447 | 1583 | /// |
| 1448 | | /// op dest, lhs, #rhs_imm |
| 1449 | | /// |
| 1450 | | /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to |
| 1451 | | /// rhs and vice versa. This parameter is only used when maybe_inst != |
| 1452 | | /// null. |
| 1453 | | /// |
| 1454 | | /// Asserts that generating an instruction of that form is possible. |
| 1584 | /// Returns the destination register |
| 1455 | 1585 | fn binOpImmediate( |
| 1456 | 1586 | self: *Self, |
| 1457 | 1587 | mir_tag: Mir.Inst.Tag, |
| 1458 | | lhs: MCValue, |
| 1459 | | rhs: MCValue, |
| 1588 | lhs_bind: ReadArg.Bind, |
| 1589 | rhs_immediate: u64, |
| 1460 | 1590 | lhs_ty: Type, |
| 1461 | 1591 | lhs_and_rhs_swapped: bool, |
| 1462 | | metadata: ?BinOpMetadata, |
| 1592 | maybe_inst: ?Air.Inst.Index, |
| 1463 | 1593 | ) !MCValue { |
| 1464 | | const lhs_is_register = lhs == .register; |
| 1465 | | |
| 1466 | | if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*))); |
| 1594 | var lhs_reg: Register = undefined; |
| 1595 | var dest_reg: Register = undefined; |
| 1467 | 1596 | |
| 1468 | | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 1469 | | self.register_manager.lockReg(lhs.register) |
| 1470 | | else |
| 1471 | | null; |
| 1472 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1473 | | |
| 1474 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1475 | | |
| 1476 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1477 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1478 | | break :inst Air.refToIndex( |
| 1479 | | if (lhs_and_rhs_swapped) md.rhs else md.lhs, |
| 1480 | | ).?; |
| 1481 | | } else null; |
| 1482 | | |
| 1483 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1484 | | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1485 | | |
| 1486 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1487 | | |
| 1488 | | break :blk reg; |
| 1597 | const read_args = [_]ReadArg{ |
| 1598 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 1489 | 1599 | }; |
| 1490 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 1491 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1492 | | |
| 1493 | | const dest_reg = switch (mir_tag) { |
| 1494 | | .cmp_immediate => undefined, // cmp has no destination register |
| 1495 | | else => if (metadata) |md| blk: { |
| 1496 | | if (lhs_is_register and self.reuseOperand( |
| 1497 | | md.inst, |
| 1498 | | if (lhs_and_rhs_swapped) md.rhs else md.lhs, |
| 1499 | | if (lhs_and_rhs_swapped) 1 else 0, |
| 1500 | | lhs, |
| 1501 | | )) { |
| 1502 | | break :blk lhs_reg; |
| 1503 | | } else { |
| 1504 | | const raw_reg = try self.register_manager.allocReg(md.inst, gp); |
| 1505 | | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1506 | | } |
| 1507 | | } else blk: { |
| 1508 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 1509 | | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1510 | | }, |
| 1600 | const write_args = [_]WriteArg{ |
| 1601 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 1511 | 1602 | }; |
| 1512 | | |
| 1513 | | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1603 | const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0}; |
| 1604 | try self.allocRegs( |
| 1605 | &read_args, |
| 1606 | &write_args, |
| 1607 | if (maybe_inst) |inst| .{ |
| 1608 | .corresponding_inst = inst, |
| 1609 | .operand_mapping = operand_mapping, |
| 1610 | } else null, |
| 1611 | ); |
| 1514 | 1612 | |
| 1515 | 1613 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1516 | 1614 | .add_immediate, |
| ... | ... | @@ -1520,7 +1618,7 @@ fn binOpImmediate( |
| 1520 | 1618 | => .{ .rr_imm12_sh = .{ |
| 1521 | 1619 | .rd = dest_reg, |
| 1522 | 1620 | .rn = lhs_reg, |
| 1523 | | .imm12 = @intCast(u12, rhs.immediate), |
| 1621 | .imm12 = @intCast(u12, rhs_immediate), |
| 1524 | 1622 | } }, |
| 1525 | 1623 | .lsl_immediate, |
| 1526 | 1624 | .asr_immediate, |
| ... | ... | @@ -1528,11 +1626,7 @@ fn binOpImmediate( |
| 1528 | 1626 | => .{ .rr_shift = .{ |
| 1529 | 1627 | .rd = dest_reg, |
| 1530 | 1628 | .rn = lhs_reg, |
| 1531 | | .shift = @intCast(u6, rhs.immediate), |
| 1532 | | } }, |
| 1533 | | .cmp_immediate => .{ .r_imm12_sh = .{ |
| 1534 | | .rn = lhs_reg, |
| 1535 | | .imm12 = @intCast(u12, rhs.immediate), |
| 1629 | .shift = @intCast(u6, rhs_immediate), |
| 1536 | 1630 | } }, |
| 1537 | 1631 | else => unreachable, |
| 1538 | 1632 | }; |
| ... | ... | @@ -1545,428 +1639,527 @@ fn binOpImmediate( |
| 1545 | 1639 | return MCValue{ .register = dest_reg }; |
| 1546 | 1640 | } |
| 1547 | 1641 | |
| 1548 | | const BinOpMetadata = struct { |
| 1549 | | inst: Air.Inst.Index, |
| 1550 | | lhs: Air.Inst.Ref, |
| 1551 | | rhs: Air.Inst.Ref, |
| 1552 | | }; |
| 1553 | | |
| 1554 | | /// For all your binary operation needs, this function will generate |
| 1555 | | /// the corresponding Mir instruction(s). Returns the location of the |
| 1556 | | /// result. |
| 1557 | | /// |
| 1558 | | /// If the binary operation itself happens to be an Air instruction, |
| 1559 | | /// pass the corresponding index in the inst parameter. That helps |
| 1560 | | /// this function do stuff like reusing operands. |
| 1561 | | /// |
| 1562 | | /// This function does not do any lowering to Mir itself, but instead |
| 1563 | | /// looks at the lhs and rhs and determines which kind of lowering |
| 1564 | | /// would be best suitable and then delegates the lowering to other |
| 1565 | | /// functions. |
| 1566 | | fn binOp( |
| 1642 | fn addSub( |
| 1567 | 1643 | self: *Self, |
| 1568 | 1644 | tag: Air.Inst.Tag, |
| 1569 | | lhs: MCValue, |
| 1570 | | rhs: MCValue, |
| 1645 | lhs_bind: ReadArg.Bind, |
| 1646 | rhs_bind: ReadArg.Bind, |
| 1571 | 1647 | lhs_ty: Type, |
| 1572 | 1648 | rhs_ty: Type, |
| 1573 | | metadata: ?BinOpMetadata, |
| 1649 | maybe_inst: ?Air.Inst.Index, |
| 1574 | 1650 | ) InnerError!MCValue { |
| 1575 | 1651 | const mod = self.bin_file.options.module.?; |
| 1576 | | switch (tag) { |
| 1577 | | .add, |
| 1578 | | .sub, |
| 1579 | | .cmp_eq, |
| 1580 | | => { |
| 1581 | | switch (lhs_ty.zigTypeTag()) { |
| 1582 | | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1583 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1584 | | .Int => { |
| 1585 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1586 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1587 | | if (int_info.bits <= 64) { |
| 1588 | | // Only say yes if the operation is |
| 1589 | | // commutative, i.e. we can swap both of the |
| 1590 | | // operands |
| 1591 | | const lhs_immediate_ok = switch (tag) { |
| 1592 | | .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 1593 | | .sub, .cmp_eq => false, |
| 1594 | | else => unreachable, |
| 1595 | | }; |
| 1596 | | const rhs_immediate_ok = switch (tag) { |
| 1597 | | .add, |
| 1598 | | .sub, |
| 1599 | | .cmp_eq, |
| 1600 | | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 1601 | | else => unreachable, |
| 1602 | | }; |
| 1652 | switch (lhs_ty.zigTypeTag()) { |
| 1653 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1654 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1655 | .Int => { |
| 1656 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1657 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1658 | if (int_info.bits <= 64) { |
| 1659 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); |
| 1660 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| 1661 | |
| 1662 | // Only say yes if the operation is |
| 1663 | // commutative, i.e. we can swap both of the |
| 1664 | // operands |
| 1665 | const lhs_immediate_ok = switch (tag) { |
| 1666 | .add => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, |
| 1667 | .sub => false, |
| 1668 | else => unreachable, |
| 1669 | }; |
| 1670 | const rhs_immediate_ok = switch (tag) { |
| 1671 | .add, |
| 1672 | .sub, |
| 1673 | => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, |
| 1674 | else => unreachable, |
| 1675 | }; |
| 1603 | 1676 | |
| 1604 | | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1605 | | .add => .add_shifted_register, |
| 1606 | | .sub => .sub_shifted_register, |
| 1607 | | .cmp_eq => .cmp_shifted_register, |
| 1608 | | else => unreachable, |
| 1609 | | }; |
| 1610 | | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 1611 | | .add => .add_immediate, |
| 1612 | | .sub => .sub_immediate, |
| 1613 | | .cmp_eq => .cmp_immediate, |
| 1614 | | else => unreachable, |
| 1615 | | }; |
| 1677 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1678 | .add => .add_shifted_register, |
| 1679 | .sub => .sub_shifted_register, |
| 1680 | else => unreachable, |
| 1681 | }; |
| 1682 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 1683 | .add => .add_immediate, |
| 1684 | .sub => .sub_immediate, |
| 1685 | else => unreachable, |
| 1686 | }; |
| 1616 | 1687 | |
| 1617 | | if (rhs_immediate_ok) { |
| 1618 | | return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); |
| 1619 | | } else if (lhs_immediate_ok) { |
| 1620 | | // swap lhs and rhs |
| 1621 | | return try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, metadata); |
| 1622 | | } else { |
| 1623 | | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1624 | | } |
| 1625 | | } else { |
| 1626 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1627 | | } |
| 1628 | | }, |
| 1629 | | else => unreachable, |
| 1688 | if (rhs_immediate_ok) { |
| 1689 | return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); |
| 1690 | } else if (lhs_immediate_ok) { |
| 1691 | // swap lhs and rhs |
| 1692 | return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); |
| 1693 | } else { |
| 1694 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1695 | } |
| 1696 | } else { |
| 1697 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1630 | 1698 | } |
| 1631 | 1699 | }, |
| 1632 | | .mul => { |
| 1633 | | switch (lhs_ty.zigTypeTag()) { |
| 1634 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1635 | | .Int => { |
| 1636 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1637 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1638 | | if (int_info.bits <= 64) { |
| 1639 | | // TODO add optimisations for multiplication |
| 1640 | | // with immediates, for example a * 2 can be |
| 1641 | | // lowered to a << 1 |
| 1642 | | return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1643 | | } else { |
| 1644 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1645 | | } |
| 1646 | | }, |
| 1647 | | else => unreachable, |
| 1700 | else => unreachable, |
| 1701 | } |
| 1702 | } |
| 1703 | |
| 1704 | fn mul( |
| 1705 | self: *Self, |
| 1706 | lhs_bind: ReadArg.Bind, |
| 1707 | rhs_bind: ReadArg.Bind, |
| 1708 | lhs_ty: Type, |
| 1709 | rhs_ty: Type, |
| 1710 | maybe_inst: ?Air.Inst.Index, |
| 1711 | ) InnerError!MCValue { |
| 1712 | const mod = self.bin_file.options.module.?; |
| 1713 | switch (lhs_ty.zigTypeTag()) { |
| 1714 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1715 | .Int => { |
| 1716 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1717 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1718 | if (int_info.bits <= 64) { |
| 1719 | // TODO add optimisations for multiplication |
| 1720 | // with immediates, for example a * 2 can be |
| 1721 | // lowered to a << 1 |
| 1722 | return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1723 | } else { |
| 1724 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1648 | 1725 | } |
| 1649 | 1726 | }, |
| 1650 | | .div_float => { |
| 1651 | | switch (lhs_ty.zigTypeTag()) { |
| 1652 | | .Float => return self.fail("TODO div_float", .{}), |
| 1653 | | .Vector => return self.fail("TODO div_float on vectors", .{}), |
| 1654 | | else => unreachable, |
| 1727 | else => unreachable, |
| 1728 | } |
| 1729 | } |
| 1730 | |
| 1731 | fn divFloat( |
| 1732 | self: *Self, |
| 1733 | lhs_bind: ReadArg.Bind, |
| 1734 | rhs_bind: ReadArg.Bind, |
| 1735 | lhs_ty: Type, |
| 1736 | rhs_ty: Type, |
| 1737 | maybe_inst: ?Air.Inst.Index, |
| 1738 | ) InnerError!MCValue { |
| 1739 | _ = lhs_bind; |
| 1740 | _ = rhs_bind; |
| 1741 | _ = rhs_ty; |
| 1742 | _ = maybe_inst; |
| 1743 | |
| 1744 | switch (lhs_ty.zigTypeTag()) { |
| 1745 | .Float => return self.fail("TODO div_float", .{}), |
| 1746 | .Vector => return self.fail("TODO div_float on vectors", .{}), |
| 1747 | else => unreachable, |
| 1748 | } |
| 1749 | } |
| 1750 | |
| 1751 | fn divTrunc( |
| 1752 | self: *Self, |
| 1753 | lhs_bind: ReadArg.Bind, |
| 1754 | rhs_bind: ReadArg.Bind, |
| 1755 | lhs_ty: Type, |
| 1756 | rhs_ty: Type, |
| 1757 | maybe_inst: ?Air.Inst.Index, |
| 1758 | ) InnerError!MCValue { |
| 1759 | const mod = self.bin_file.options.module.?; |
| 1760 | switch (lhs_ty.zigTypeTag()) { |
| 1761 | .Float => return self.fail("TODO div on floats", .{}), |
| 1762 | .Vector => return self.fail("TODO div on vectors", .{}), |
| 1763 | .Int => { |
| 1764 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1765 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1766 | if (int_info.bits <= 64) { |
| 1767 | switch (int_info.signedness) { |
| 1768 | .signed => { |
| 1769 | // TODO optimize integer division by constants |
| 1770 | return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1771 | }, |
| 1772 | .unsigned => { |
| 1773 | // TODO optimize integer division by constants |
| 1774 | return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1775 | }, |
| 1776 | } |
| 1777 | } else { |
| 1778 | return self.fail("TODO integer division for ints with bits > 64", .{}); |
| 1655 | 1779 | } |
| 1656 | 1780 | }, |
| 1657 | | .div_trunc, .div_floor, .div_exact => { |
| 1658 | | switch (lhs_ty.zigTypeTag()) { |
| 1659 | | .Float => return self.fail("TODO div on floats", .{}), |
| 1660 | | .Vector => return self.fail("TODO div on vectors", .{}), |
| 1661 | | .Int => { |
| 1662 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1663 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1664 | | if (int_info.bits <= 64) { |
| 1665 | | switch (int_info.signedness) { |
| 1666 | | .signed => { |
| 1667 | | switch (tag) { |
| 1668 | | .div_trunc, .div_exact => { |
| 1669 | | // TODO optimize integer division by constants |
| 1670 | | return try self.binOpRegister(.sdiv, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1671 | | }, |
| 1672 | | .div_floor => return self.fail("TODO div_floor on signed integers", .{}), |
| 1673 | | else => unreachable, |
| 1674 | | } |
| 1675 | | }, |
| 1676 | | .unsigned => { |
| 1677 | | // TODO optimize integer division by constants |
| 1678 | | return try self.binOpRegister(.udiv, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1679 | | }, |
| 1680 | | } |
| 1681 | | } else { |
| 1682 | | return self.fail("TODO integer division for ints with bits > 64", .{}); |
| 1683 | | } |
| 1684 | | }, |
| 1685 | | else => unreachable, |
| 1781 | else => unreachable, |
| 1782 | } |
| 1783 | } |
| 1784 | |
| 1785 | fn divFloor( |
| 1786 | self: *Self, |
| 1787 | lhs_bind: ReadArg.Bind, |
| 1788 | rhs_bind: ReadArg.Bind, |
| 1789 | lhs_ty: Type, |
| 1790 | rhs_ty: Type, |
| 1791 | maybe_inst: ?Air.Inst.Index, |
| 1792 | ) InnerError!MCValue { |
| 1793 | const mod = self.bin_file.options.module.?; |
| 1794 | switch (lhs_ty.zigTypeTag()) { |
| 1795 | .Float => return self.fail("TODO div on floats", .{}), |
| 1796 | .Vector => return self.fail("TODO div on vectors", .{}), |
| 1797 | .Int => { |
| 1798 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1799 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1800 | if (int_info.bits <= 64) { |
| 1801 | switch (int_info.signedness) { |
| 1802 | .signed => { |
| 1803 | return self.fail("TODO div_floor on signed integers", .{}); |
| 1804 | }, |
| 1805 | .unsigned => { |
| 1806 | // TODO optimize integer division by constants |
| 1807 | return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1808 | }, |
| 1809 | } |
| 1810 | } else { |
| 1811 | return self.fail("TODO integer division for ints with bits > 64", .{}); |
| 1686 | 1812 | } |
| 1687 | 1813 | }, |
| 1688 | | .rem, .mod => { |
| 1689 | | switch (lhs_ty.zigTypeTag()) { |
| 1690 | | .Float => return self.fail("TODO rem/mod on floats", .{}), |
| 1691 | | .Vector => return self.fail("TODO rem/mod on vectors", .{}), |
| 1692 | | .Int => { |
| 1693 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1694 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1695 | | if (int_info.bits <= 64) { |
| 1696 | | if (int_info.signedness == .signed and tag == .mod) { |
| 1697 | | return self.fail("TODO mod on signed integers", .{}); |
| 1698 | | } else { |
| 1699 | | const lhs_is_register = lhs == .register; |
| 1700 | | const rhs_is_register = rhs == .register; |
| 1701 | | |
| 1702 | | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 1703 | | self.register_manager.lockReg(lhs.register) |
| 1704 | | else |
| 1705 | | null; |
| 1706 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1707 | | |
| 1708 | | const rhs_lock: ?RegisterLock = if (rhs_is_register) |
| 1709 | | self.register_manager.lockReg(rhs.register) |
| 1710 | | else |
| 1711 | | null; |
| 1712 | | defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1713 | | |
| 1714 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1715 | | |
| 1716 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1717 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1718 | | break :inst Air.refToIndex(md.lhs).?; |
| 1719 | | } else null; |
| 1720 | | |
| 1721 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1722 | | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1723 | | |
| 1724 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1814 | else => unreachable, |
| 1815 | } |
| 1816 | } |
| 1725 | 1817 | |
| 1726 | | break :blk reg; |
| 1727 | | }; |
| 1728 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 1729 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1818 | fn divExact( |
| 1819 | self: *Self, |
| 1820 | lhs_bind: ReadArg.Bind, |
| 1821 | rhs_bind: ReadArg.Bind, |
| 1822 | lhs_ty: Type, |
| 1823 | rhs_ty: Type, |
| 1824 | maybe_inst: ?Air.Inst.Index, |
| 1825 | ) InnerError!MCValue { |
| 1826 | const mod = self.bin_file.options.module.?; |
| 1827 | switch (lhs_ty.zigTypeTag()) { |
| 1828 | .Float => return self.fail("TODO div on floats", .{}), |
| 1829 | .Vector => return self.fail("TODO div on vectors", .{}), |
| 1830 | .Int => { |
| 1831 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1832 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1833 | if (int_info.bits <= 64) { |
| 1834 | switch (int_info.signedness) { |
| 1835 | .signed => { |
| 1836 | // TODO optimize integer division by constants |
| 1837 | return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1838 | }, |
| 1839 | .unsigned => { |
| 1840 | // TODO optimize integer division by constants |
| 1841 | return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1842 | }, |
| 1843 | } |
| 1844 | } else { |
| 1845 | return self.fail("TODO integer division for ints with bits > 64", .{}); |
| 1846 | } |
| 1847 | }, |
| 1848 | else => unreachable, |
| 1849 | } |
| 1850 | } |
| 1730 | 1851 | |
| 1731 | | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 1732 | | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 1733 | | break :inst Air.refToIndex(md.rhs).?; |
| 1734 | | } else null; |
| 1852 | fn rem( |
| 1853 | self: *Self, |
| 1854 | lhs_bind: ReadArg.Bind, |
| 1855 | rhs_bind: ReadArg.Bind, |
| 1856 | lhs_ty: Type, |
| 1857 | rhs_ty: Type, |
| 1858 | maybe_inst: ?Air.Inst.Index, |
| 1859 | ) InnerError!MCValue { |
| 1860 | _ = maybe_inst; |
| 1735 | 1861 | |
| 1736 | | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1737 | | const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); |
| 1862 | const mod = self.bin_file.options.module.?; |
| 1863 | switch (lhs_ty.zigTypeTag()) { |
| 1864 | .Float => return self.fail("TODO rem/mod on floats", .{}), |
| 1865 | .Vector => return self.fail("TODO rem/mod on vectors", .{}), |
| 1866 | .Int => { |
| 1867 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1868 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1869 | if (int_info.bits <= 64) { |
| 1870 | var lhs_reg: Register = undefined; |
| 1871 | var rhs_reg: Register = undefined; |
| 1872 | var quotient_reg: Register = undefined; |
| 1873 | var remainder_reg: Register = undefined; |
| 1874 | |
| 1875 | const read_args = [_]ReadArg{ |
| 1876 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 1877 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 1878 | }; |
| 1879 | const write_args = [_]WriteArg{ |
| 1880 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &quotient_reg }, |
| 1881 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &remainder_reg }, |
| 1882 | }; |
| 1883 | try self.allocRegs( |
| 1884 | &read_args, |
| 1885 | &write_args, |
| 1886 | null, |
| 1887 | ); |
| 1738 | 1888 | |
| 1739 | | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1889 | _ = try self.addInst(.{ |
| 1890 | .tag = switch (int_info.signedness) { |
| 1891 | .signed => .sdiv, |
| 1892 | .unsigned => .udiv, |
| 1893 | }, |
| 1894 | .data = .{ .rrr = .{ |
| 1895 | .rd = quotient_reg, |
| 1896 | .rn = lhs_reg, |
| 1897 | .rm = rhs_reg, |
| 1898 | } }, |
| 1899 | }); |
| 1740 | 1900 | |
| 1741 | | break :blk reg; |
| 1742 | | }; |
| 1743 | | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); |
| 1744 | | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1745 | | |
| 1746 | | const dest_regs: [2]Register = blk: { |
| 1747 | | const raw_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); |
| 1748 | | const abi_size = lhs_ty.abiSize(self.target.*); |
| 1749 | | break :blk .{ |
| 1750 | | registerAlias(raw_regs[0], abi_size), |
| 1751 | | registerAlias(raw_regs[1], abi_size), |
| 1752 | | }; |
| 1753 | | }; |
| 1754 | | const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs); |
| 1755 | | defer for (dest_regs_locks) |reg| { |
| 1756 | | self.register_manager.unlockReg(reg); |
| 1757 | | }; |
| 1758 | | const quotient_reg = dest_regs[0]; |
| 1759 | | const remainder_reg = dest_regs[1]; |
| 1901 | _ = try self.addInst(.{ |
| 1902 | .tag = .msub, |
| 1903 | .data = .{ .rrrr = .{ |
| 1904 | .rd = remainder_reg, |
| 1905 | .rn = quotient_reg, |
| 1906 | .rm = rhs_reg, |
| 1907 | .ra = lhs_reg, |
| 1908 | } }, |
| 1909 | }); |
| 1760 | 1910 | |
| 1761 | | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1762 | | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1911 | return MCValue{ .register = remainder_reg }; |
| 1912 | } else { |
| 1913 | return self.fail("TODO rem/mod for integers with bits > 64", .{}); |
| 1914 | } |
| 1915 | }, |
| 1916 | else => unreachable, |
| 1917 | } |
| 1918 | } |
| 1763 | 1919 | |
| 1764 | | _ = try self.addInst(.{ |
| 1765 | | .tag = switch (int_info.signedness) { |
| 1766 | | .signed => .sdiv, |
| 1767 | | .unsigned => .udiv, |
| 1768 | | }, |
| 1769 | | .data = .{ .rrr = .{ |
| 1770 | | .rd = quotient_reg, |
| 1771 | | .rn = lhs_reg, |
| 1772 | | .rm = rhs_reg, |
| 1773 | | } }, |
| 1774 | | }); |
| 1920 | fn modulo( |
| 1921 | self: *Self, |
| 1922 | lhs_bind: ReadArg.Bind, |
| 1923 | rhs_bind: ReadArg.Bind, |
| 1924 | lhs_ty: Type, |
| 1925 | rhs_ty: Type, |
| 1926 | maybe_inst: ?Air.Inst.Index, |
| 1927 | ) InnerError!MCValue { |
| 1928 | _ = lhs_bind; |
| 1929 | _ = rhs_bind; |
| 1930 | _ = rhs_ty; |
| 1931 | _ = maybe_inst; |
| 1932 | |
| 1933 | switch (lhs_ty.zigTypeTag()) { |
| 1934 | .Float => return self.fail("TODO mod on floats", .{}), |
| 1935 | .Vector => return self.fail("TODO mod on vectors", .{}), |
| 1936 | .Int => return self.fail("TODO mod on ints", .{}), |
| 1937 | else => unreachable, |
| 1938 | } |
| 1939 | } |
| 1775 | 1940 | |
| 1776 | | _ = try self.addInst(.{ |
| 1777 | | .tag = .msub, |
| 1778 | | .data = .{ .rrrr = .{ |
| 1779 | | .rd = remainder_reg, |
| 1780 | | .rn = quotient_reg, |
| 1781 | | .rm = rhs_reg, |
| 1782 | | .ra = lhs_reg, |
| 1783 | | } }, |
| 1784 | | }); |
| 1941 | fn wrappingArithmetic( |
| 1942 | self: *Self, |
| 1943 | tag: Air.Inst.Tag, |
| 1944 | lhs_bind: ReadArg.Bind, |
| 1945 | rhs_bind: ReadArg.Bind, |
| 1946 | lhs_ty: Type, |
| 1947 | rhs_ty: Type, |
| 1948 | maybe_inst: ?Air.Inst.Index, |
| 1949 | ) InnerError!MCValue { |
| 1950 | switch (lhs_ty.zigTypeTag()) { |
| 1951 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1952 | .Int => { |
| 1953 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1954 | if (int_info.bits <= 64) { |
| 1955 | // Generate an add/sub/mul |
| 1956 | const result: MCValue = switch (tag) { |
| 1957 | .addwrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 1958 | .subwrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 1959 | .mulwrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 1960 | else => unreachable, |
| 1961 | }; |
| 1785 | 1962 | |
| 1786 | | return MCValue{ .register = remainder_reg }; |
| 1787 | | } |
| 1788 | | } else { |
| 1789 | | return self.fail("TODO rem/mod for integers with bits > 64", .{}); |
| 1790 | | } |
| 1791 | | }, |
| 1792 | | else => unreachable, |
| 1963 | // Truncate if necessary |
| 1964 | const result_reg = result.register; |
| 1965 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 1966 | return result; |
| 1967 | } else { |
| 1968 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 1793 | 1969 | } |
| 1794 | 1970 | }, |
| 1795 | | .addwrap, |
| 1796 | | .subwrap, |
| 1797 | | .mulwrap, |
| 1798 | | => { |
| 1799 | | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1800 | | .addwrap => .add, |
| 1801 | | .subwrap => .sub, |
| 1802 | | .mulwrap => .mul, |
| 1803 | | else => unreachable, |
| 1804 | | }; |
| 1971 | else => unreachable, |
| 1972 | } |
| 1973 | } |
| 1805 | 1974 | |
| 1806 | | // Generate an add/sub/mul |
| 1807 | | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1975 | fn bitwise( |
| 1976 | self: *Self, |
| 1977 | tag: Air.Inst.Tag, |
| 1978 | lhs_bind: ReadArg.Bind, |
| 1979 | rhs_bind: ReadArg.Bind, |
| 1980 | lhs_ty: Type, |
| 1981 | rhs_ty: Type, |
| 1982 | maybe_inst: ?Air.Inst.Index, |
| 1983 | ) InnerError!MCValue { |
| 1984 | const mod = self.bin_file.options.module.?; |
| 1985 | switch (lhs_ty.zigTypeTag()) { |
| 1986 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1987 | .Int => { |
| 1988 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1989 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1990 | if (int_info.bits <= 64) { |
| 1991 | // TODO implement bitwise operations with immediates |
| 1992 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1993 | .bit_and => .and_shifted_register, |
| 1994 | .bit_or => .orr_shifted_register, |
| 1995 | .xor => .eor_shifted_register, |
| 1996 | else => unreachable, |
| 1997 | }; |
| 1808 | 1998 | |
| 1809 | | // Truncate if necessary |
| 1810 | | switch (lhs_ty.zigTypeTag()) { |
| 1811 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1812 | | .Int => { |
| 1813 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1814 | | if (int_info.bits <= 64) { |
| 1815 | | const result_reg = result.register; |
| 1816 | | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 1817 | | return result; |
| 1818 | | } else { |
| 1819 | | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 1820 | | } |
| 1821 | | }, |
| 1822 | | else => unreachable, |
| 1999 | return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 2000 | } else { |
| 2001 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1823 | 2002 | } |
| 1824 | 2003 | }, |
| 1825 | | .bit_and, |
| 1826 | | .bit_or, |
| 1827 | | .xor, |
| 1828 | | => { |
| 1829 | | switch (lhs_ty.zigTypeTag()) { |
| 1830 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1831 | | .Int => { |
| 1832 | | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1833 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1834 | | if (int_info.bits <= 64) { |
| 1835 | | // TODO implement bitwise operations with immediates |
| 1836 | | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1837 | | .bit_and => .and_shifted_register, |
| 1838 | | .bit_or => .orr_shifted_register, |
| 1839 | | .xor => .eor_shifted_register, |
| 1840 | | else => unreachable, |
| 1841 | | }; |
| 2004 | else => unreachable, |
| 2005 | } |
| 2006 | } |
| 1842 | 2007 | |
| 1843 | | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1844 | | } else { |
| 1845 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1846 | | } |
| 1847 | | }, |
| 1848 | | else => unreachable, |
| 1849 | | } |
| 1850 | | }, |
| 1851 | | .shl_exact, |
| 1852 | | .shr_exact, |
| 1853 | | => { |
| 1854 | | switch (lhs_ty.zigTypeTag()) { |
| 1855 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1856 | | .Int => { |
| 1857 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1858 | | if (int_info.bits <= 64) { |
| 1859 | | const rhs_immediate_ok = rhs == .immediate; |
| 2008 | fn shiftExact( |
| 2009 | self: *Self, |
| 2010 | tag: Air.Inst.Tag, |
| 2011 | lhs_bind: ReadArg.Bind, |
| 2012 | rhs_bind: ReadArg.Bind, |
| 2013 | lhs_ty: Type, |
| 2014 | rhs_ty: Type, |
| 2015 | maybe_inst: ?Air.Inst.Index, |
| 2016 | ) InnerError!MCValue { |
| 2017 | _ = rhs_ty; |
| 1860 | 2018 | |
| 1861 | | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1862 | | .shl_exact => .lsl_register, |
| 1863 | | .shr_exact => switch (int_info.signedness) { |
| 1864 | | .signed => Mir.Inst.Tag.asr_register, |
| 1865 | | .unsigned => Mir.Inst.Tag.lsr_register, |
| 1866 | | }, |
| 1867 | | else => unreachable, |
| 1868 | | }; |
| 1869 | | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 1870 | | .shl_exact => .lsl_immediate, |
| 1871 | | .shr_exact => switch (int_info.signedness) { |
| 1872 | | .signed => Mir.Inst.Tag.asr_immediate, |
| 1873 | | .unsigned => Mir.Inst.Tag.lsr_immediate, |
| 1874 | | }, |
| 1875 | | else => unreachable, |
| 1876 | | }; |
| 2019 | switch (lhs_ty.zigTypeTag()) { |
| 2020 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2021 | .Int => { |
| 2022 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2023 | if (int_info.bits <= 64) { |
| 2024 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| 2025 | |
| 2026 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 2027 | .shl_exact => .lsl_register, |
| 2028 | .shr_exact => switch (int_info.signedness) { |
| 2029 | .signed => Mir.Inst.Tag.asr_register, |
| 2030 | .unsigned => Mir.Inst.Tag.lsr_register, |
| 2031 | }, |
| 2032 | else => unreachable, |
| 2033 | }; |
| 2034 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 2035 | .shl_exact => .lsl_immediate, |
| 2036 | .shr_exact => switch (int_info.signedness) { |
| 2037 | .signed => Mir.Inst.Tag.asr_immediate, |
| 2038 | .unsigned => Mir.Inst.Tag.lsr_immediate, |
| 2039 | }, |
| 2040 | else => unreachable, |
| 2041 | }; |
| 1877 | 2042 | |
| 1878 | | if (rhs_immediate_ok) { |
| 1879 | | return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); |
| 1880 | | } else { |
| 1881 | | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1882 | | } |
| 1883 | | } else { |
| 1884 | | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1885 | | } |
| 1886 | | }, |
| 1887 | | else => unreachable, |
| 2043 | if (rhs_immediate) |imm| { |
| 2044 | return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst); |
| 2045 | } else { |
| 2046 | // We intentionally pass lhs_ty here in order to |
| 2047 | // prevent using the 32-bit register alias when |
| 2048 | // lhs_ty is > 32 bits. |
| 2049 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst); |
| 2050 | } |
| 2051 | } else { |
| 2052 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1888 | 2053 | } |
| 1889 | 2054 | }, |
| 1890 | | .shl, |
| 1891 | | .shr, |
| 1892 | | => { |
| 1893 | | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1894 | | .shl => .shl_exact, |
| 1895 | | .shr => .shr_exact, |
| 1896 | | else => unreachable, |
| 1897 | | }; |
| 2055 | else => unreachable, |
| 2056 | } |
| 2057 | } |
| 1898 | 2058 | |
| 1899 | | // Generate a shl_exact/shr_exact |
| 1900 | | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 2059 | fn shiftNormal( |
| 2060 | self: *Self, |
| 2061 | tag: Air.Inst.Tag, |
| 2062 | lhs_bind: ReadArg.Bind, |
| 2063 | rhs_bind: ReadArg.Bind, |
| 2064 | lhs_ty: Type, |
| 2065 | rhs_ty: Type, |
| 2066 | maybe_inst: ?Air.Inst.Index, |
| 2067 | ) InnerError!MCValue { |
| 2068 | switch (lhs_ty.zigTypeTag()) { |
| 2069 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 2070 | .Int => { |
| 2071 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2072 | if (int_info.bits <= 64) { |
| 2073 | // Generate a shl_exact/shr_exact |
| 2074 | const result: MCValue = switch (tag) { |
| 2075 | .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 2076 | .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), |
| 2077 | else => unreachable, |
| 2078 | }; |
| 1901 | 2079 | |
| 1902 | | // Truncate if necessary |
| 1903 | | switch (tag) { |
| 1904 | | .shr => return result, |
| 1905 | | .shl => switch (lhs_ty.zigTypeTag()) { |
| 1906 | | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1907 | | .Int => { |
| 1908 | | const int_info = lhs_ty.intInfo(self.target.*); |
| 1909 | | if (int_info.bits <= 64) { |
| 1910 | | const result_reg = result.register; |
| 1911 | | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 1912 | | return result; |
| 1913 | | } else { |
| 1914 | | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 1915 | | } |
| 2080 | // Truncate if necessary |
| 2081 | switch (tag) { |
| 2082 | .shr => return result, |
| 2083 | .shl => { |
| 2084 | const result_reg = result.register; |
| 2085 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 2086 | return result; |
| 1916 | 2087 | }, |
| 1917 | 2088 | else => unreachable, |
| 1918 | | }, |
| 1919 | | else => unreachable, |
| 2089 | } |
| 2090 | } else { |
| 2091 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 1920 | 2092 | } |
| 1921 | 2093 | }, |
| 1922 | | .bool_and, |
| 1923 | | .bool_or, |
| 1924 | | => { |
| 1925 | | switch (lhs_ty.zigTypeTag()) { |
| 1926 | | .Bool => { |
| 1927 | | assert(lhs != .immediate); // should have been handled by Sema |
| 1928 | | assert(rhs != .immediate); // should have been handled by Sema |
| 1929 | | |
| 1930 | | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1931 | | .bool_and => .and_shifted_register, |
| 1932 | | .bool_or => .orr_shifted_register, |
| 1933 | | else => unreachable, |
| 1934 | | }; |
| 2094 | else => unreachable, |
| 2095 | } |
| 2096 | } |
| 1935 | 2097 | |
| 1936 | | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1937 | | }, |
| 2098 | fn booleanOp( |
| 2099 | self: *Self, |
| 2100 | tag: Air.Inst.Tag, |
| 2101 | lhs_bind: ReadArg.Bind, |
| 2102 | rhs_bind: ReadArg.Bind, |
| 2103 | lhs_ty: Type, |
| 2104 | rhs_ty: Type, |
| 2105 | maybe_inst: ?Air.Inst.Index, |
| 2106 | ) InnerError!MCValue { |
| 2107 | switch (lhs_ty.zigTypeTag()) { |
| 2108 | .Bool => { |
| 2109 | assert((try lhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema |
| 2110 | assert((try rhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema |
| 2111 | |
| 2112 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 2113 | .bool_and => .and_shifted_register, |
| 2114 | .bool_or => .orr_shifted_register, |
| 1938 | 2115 | else => unreachable, |
| 1939 | | } |
| 2116 | }; |
| 2117 | |
| 2118 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1940 | 2119 | }, |
| 1941 | | .ptr_add, |
| 1942 | | .ptr_sub, |
| 1943 | | => { |
| 1944 | | switch (lhs_ty.zigTypeTag()) { |
| 1945 | | .Pointer => { |
| 1946 | | const ptr_ty = lhs_ty; |
| 1947 | | const elem_ty = switch (ptr_ty.ptrSize()) { |
| 1948 | | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| 1949 | | else => ptr_ty.childType(), |
| 1950 | | }; |
| 1951 | | const elem_size = elem_ty.abiSize(self.target.*); |
| 2120 | else => unreachable, |
| 2121 | } |
| 2122 | } |
| 1952 | 2123 | |
| 1953 | | if (elem_size == 1) { |
| 1954 | | const base_tag: Mir.Inst.Tag = switch (tag) { |
| 1955 | | .ptr_add => .add_shifted_register, |
| 1956 | | .ptr_sub => .sub_shifted_register, |
| 1957 | | else => unreachable, |
| 1958 | | }; |
| 2124 | fn ptrArithmetic( |
| 2125 | self: *Self, |
| 2126 | tag: Air.Inst.Tag, |
| 2127 | lhs_bind: ReadArg.Bind, |
| 2128 | rhs_bind: ReadArg.Bind, |
| 2129 | lhs_ty: Type, |
| 2130 | rhs_ty: Type, |
| 2131 | maybe_inst: ?Air.Inst.Index, |
| 2132 | ) InnerError!MCValue { |
| 2133 | switch (lhs_ty.zigTypeTag()) { |
| 2134 | .Pointer => { |
| 2135 | const mod = self.bin_file.options.module.?; |
| 2136 | assert(rhs_ty.eql(Type.usize, mod)); |
| 1959 | 2137 | |
| 1960 | | return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); |
| 1961 | | } else { |
| 1962 | | // convert the offset into a byte offset by |
| 1963 | | // multiplying it with elem_size |
| 1964 | | const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); |
| 1965 | | const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); |
| 1966 | | return addr; |
| 1967 | | } |
| 1968 | | }, |
| 2138 | const ptr_ty = lhs_ty; |
| 2139 | const elem_ty = switch (ptr_ty.ptrSize()) { |
| 2140 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| 2141 | else => ptr_ty.childType(), |
| 2142 | }; |
| 2143 | const elem_size = elem_ty.abiSize(self.target.*); |
| 2144 | |
| 2145 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 2146 | .ptr_add => .add, |
| 2147 | .ptr_sub => .sub, |
| 1969 | 2148 | else => unreachable, |
| 2149 | }; |
| 2150 | |
| 2151 | if (elem_size == 1) { |
| 2152 | return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst); |
| 2153 | } else { |
| 2154 | // convert the offset into a byte offset by |
| 2155 | // multiplying it with elem_size |
| 2156 | const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } }; |
| 2157 | |
| 2158 | const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null); |
| 2159 | const offset_bind = ReadArg.Bind{ .mcv = offset }; |
| 2160 | |
| 2161 | const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null); |
| 2162 | return addr; |
| 1970 | 2163 | } |
| 1971 | 2164 | }, |
| 1972 | 2165 | else => unreachable, |
| ... | ... | @@ -1975,38 +2168,66 @@ fn binOp( |
| 1975 | 2168 | |
| 1976 | 2169 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 1977 | 2170 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1978 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 1979 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 1980 | 2171 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1981 | 2172 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1982 | 2173 | |
| 1983 | | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1984 | | .dead |
| 1985 | | else |
| 1986 | | try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ |
| 1987 | | .inst = inst, |
| 1988 | | .lhs = bin_op.lhs, |
| 1989 | | .rhs = bin_op.rhs, |
| 1990 | | }); |
| 2174 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2175 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; |
| 2176 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 2177 | |
| 2178 | break :result switch (tag) { |
| 2179 | .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2180 | .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2181 | |
| 2182 | .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2183 | |
| 2184 | .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2185 | |
| 2186 | .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2187 | |
| 2188 | .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2189 | |
| 2190 | .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2191 | |
| 2192 | .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2193 | |
| 2194 | .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2195 | |
| 2196 | .addwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2197 | .subwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2198 | .mulwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2199 | |
| 2200 | .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2201 | .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2202 | .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2203 | |
| 2204 | .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2205 | .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2206 | |
| 2207 | .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2208 | .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2209 | |
| 2210 | .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2211 | .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 2212 | |
| 2213 | else => unreachable, |
| 2214 | }; |
| 2215 | }; |
| 1991 | 2216 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1992 | 2217 | } |
| 1993 | 2218 | |
| 1994 | 2219 | fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 1995 | 2220 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1996 | 2221 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1997 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 1998 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 1999 | 2222 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 2000 | 2223 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 2001 | 2224 | |
| 2002 | | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 2003 | | .dead |
| 2004 | | else |
| 2005 | | try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ |
| 2006 | | .inst = inst, |
| 2007 | | .lhs = bin_op.lhs, |
| 2008 | | .rhs = bin_op.rhs, |
| 2009 | | }); |
| 2225 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2226 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; |
| 2227 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 2228 | |
| 2229 | break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); |
| 2230 | }; |
| 2010 | 2231 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2011 | 2232 | } |
| 2012 | 2233 | |
| ... | ... | @@ -2033,8 +2254,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2033 | 2254 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2034 | 2255 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2035 | 2256 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2036 | | const lhs = try self.resolveInst(extra.lhs); |
| 2037 | | const rhs = try self.resolveInst(extra.rhs); |
| 2257 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2258 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2038 | 2259 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2039 | 2260 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2040 | 2261 | |
| ... | ... | @@ -2051,7 +2272,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2051 | 2272 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2052 | 2273 | switch (int_info.bits) { |
| 2053 | 2274 | 1...31, 33...63 => { |
| 2054 | | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 2275 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 2055 | 2276 | |
| 2056 | 2277 | try self.spillCompareFlagsIfOccupied(); |
| 2057 | 2278 | self.condition_flags_inst = null; |
| ... | ... | @@ -2061,13 +2282,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2061 | 2282 | .sub_with_overflow => .sub, |
| 2062 | 2283 | else => unreachable, |
| 2063 | 2284 | }; |
| 2064 | | const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); |
| 2285 | const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); |
| 2065 | 2286 | const dest_reg = dest.register; |
| 2066 | 2287 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2067 | 2288 | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2068 | 2289 | |
| 2069 | 2290 | const raw_truncated_reg = try self.register_manager.allocReg(null, gp); |
| 2070 | | const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*)); |
| 2291 | const truncated_reg = self.registerAlias(raw_truncated_reg, lhs_ty); |
| 2071 | 2292 | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); |
| 2072 | 2293 | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 2073 | 2294 | |
| ... | ... | @@ -2075,7 +2296,15 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2075 | 2296 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2076 | 2297 | |
| 2077 | 2298 | // cmp dest, truncated |
| 2078 | | _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, lhs_ty, lhs_ty, null); |
| 2299 | _ = try self.addInst(.{ |
| 2300 | .tag = .cmp_shifted_register, |
| 2301 | .data = .{ .rr_imm6_shift = .{ |
| 2302 | .rn = dest_reg, |
| 2303 | .rm = truncated_reg, |
| 2304 | .imm6 = 0, |
| 2305 | .shift = .lsl, |
| 2306 | } }, |
| 2307 | }); |
| 2079 | 2308 | |
| 2080 | 2309 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2081 | 2310 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| ... | ... | @@ -2083,18 +2312,21 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2083 | 2312 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2084 | 2313 | }, |
| 2085 | 2314 | 32, 64 => { |
| 2315 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); |
| 2316 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| 2317 | |
| 2086 | 2318 | // Only say yes if the operation is |
| 2087 | 2319 | // commutative, i.e. we can swap both of the |
| 2088 | 2320 | // operands |
| 2089 | 2321 | const lhs_immediate_ok = switch (tag) { |
| 2090 | | .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| 2322 | .add_with_overflow => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, |
| 2091 | 2323 | .sub_with_overflow => false, |
| 2092 | 2324 | else => unreachable, |
| 2093 | 2325 | }; |
| 2094 | 2326 | const rhs_immediate_ok = switch (tag) { |
| 2095 | 2327 | .add_with_overflow, |
| 2096 | 2328 | .sub_with_overflow, |
| 2097 | | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| 2329 | => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, |
| 2098 | 2330 | else => unreachable, |
| 2099 | 2331 | }; |
| 2100 | 2332 | |
| ... | ... | @@ -2114,12 +2346,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2114 | 2346 | |
| 2115 | 2347 | const dest = blk: { |
| 2116 | 2348 | if (rhs_immediate_ok) { |
| 2117 | | break :blk try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, null); |
| 2349 | break :blk try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, null); |
| 2118 | 2350 | } else if (lhs_immediate_ok) { |
| 2119 | 2351 | // swap lhs and rhs |
| 2120 | | break :blk try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, null); |
| 2352 | break :blk try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, null); |
| 2121 | 2353 | } else { |
| 2122 | | break :blk try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, null); |
| 2354 | break :blk try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); |
| 2123 | 2355 | } |
| 2124 | 2356 | }; |
| 2125 | 2357 | |
| ... | ... | @@ -2150,8 +2382,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2150 | 2382 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2151 | 2383 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2152 | 2384 | const result: MCValue = result: { |
| 2153 | | const lhs = try self.resolveInst(extra.lhs); |
| 2154 | | const rhs = try self.resolveInst(extra.rhs); |
| 2385 | const mod = self.bin_file.options.module.?; |
| 2386 | |
| 2387 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2388 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2155 | 2389 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2156 | 2390 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2157 | 2391 | |
| ... | ... | @@ -2163,20 +2397,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2163 | 2397 | switch (lhs_ty.zigTypeTag()) { |
| 2164 | 2398 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| 2165 | 2399 | .Int => { |
| 2400 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2166 | 2401 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2167 | | |
| 2168 | 2402 | if (int_info.bits <= 32) { |
| 2169 | | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 2403 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 2170 | 2404 | |
| 2171 | 2405 | try self.spillCompareFlagsIfOccupied(); |
| 2172 | | self.condition_flags_inst = null; |
| 2173 | 2406 | |
| 2174 | 2407 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| 2175 | 2408 | .signed => .smull, |
| 2176 | 2409 | .unsigned => .umull, |
| 2177 | 2410 | }; |
| 2178 | 2411 | |
| 2179 | | const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); |
| 2412 | const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); |
| 2180 | 2413 | const dest_reg = dest.register; |
| 2181 | 2414 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2182 | 2415 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -2186,8 +2419,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2186 | 2419 | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 2187 | 2420 | |
| 2188 | 2421 | try self.truncRegister( |
| 2189 | | dest_reg.to32(), |
| 2190 | | truncated_reg.to32(), |
| 2422 | dest_reg.toW(), |
| 2423 | truncated_reg.toW(), |
| 2191 | 2424 | int_info.signedness, |
| 2192 | 2425 | int_info.bits, |
| 2193 | 2426 | ); |
| ... | ... | @@ -2197,8 +2430,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2197 | 2430 | _ = try self.addInst(.{ |
| 2198 | 2431 | .tag = .cmp_extended_register, |
| 2199 | 2432 | .data = .{ .rr_extend_shift = .{ |
| 2200 | | .rn = dest_reg.to64(), |
| 2201 | | .rm = truncated_reg.to32(), |
| 2433 | .rn = dest_reg.toX(), |
| 2434 | .rm = truncated_reg.toW(), |
| 2202 | 2435 | .ext_type = .sxtw, |
| 2203 | 2436 | .imm3 = 0, |
| 2204 | 2437 | } }, |
| ... | ... | @@ -2208,8 +2441,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2208 | 2441 | _ = try self.addInst(.{ |
| 2209 | 2442 | .tag = .cmp_extended_register, |
| 2210 | 2443 | .data = .{ .rr_extend_shift = .{ |
| 2211 | | .rn = dest_reg.to64(), |
| 2212 | | .rm = truncated_reg.to32(), |
| 2444 | .rn = dest_reg.toX(), |
| 2445 | .rm = truncated_reg.toW(), |
| 2213 | 2446 | .ext_type = .uxtw, |
| 2214 | 2447 | .imm3 = 0, |
| 2215 | 2448 | } }, |
| ... | ... | @@ -2222,53 +2455,30 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2222 | 2455 | |
| 2223 | 2456 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2224 | 2457 | } else if (int_info.bits <= 64) { |
| 2225 | | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 2458 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 2226 | 2459 | |
| 2227 | 2460 | try self.spillCompareFlagsIfOccupied(); |
| 2228 | | self.condition_flags_inst = null; |
| 2229 | | |
| 2230 | | // TODO this should really be put in a helper similar to `binOpRegister` |
| 2231 | | const lhs_is_register = lhs == .register; |
| 2232 | | const rhs_is_register = rhs == .register; |
| 2233 | | |
| 2234 | | const lhs_lock: ?RegisterLock = if (lhs_is_register) |
| 2235 | | self.register_manager.lockRegAssumeUnused(lhs.register) |
| 2236 | | else |
| 2237 | | null; |
| 2238 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2239 | | |
| 2240 | | const rhs_lock: ?RegisterLock = if (rhs_is_register) |
| 2241 | | self.register_manager.lockRegAssumeUnused(rhs.register) |
| 2242 | | else |
| 2243 | | null; |
| 2244 | | defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2245 | | |
| 2246 | | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 2247 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2248 | | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 2249 | | break :blk reg; |
| 2250 | | }; |
| 2251 | | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 2252 | | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2253 | | |
| 2254 | | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 2255 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2256 | | const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); |
| 2257 | | break :blk reg; |
| 2258 | | }; |
| 2259 | | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); |
| 2260 | | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2261 | 2461 | |
| 2262 | | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2263 | | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2462 | var lhs_reg: Register = undefined; |
| 2463 | var rhs_reg: Register = undefined; |
| 2464 | var dest_reg: Register = undefined; |
| 2465 | var dest_high_reg: Register = undefined; |
| 2466 | var truncated_reg: Register = undefined; |
| 2264 | 2467 | |
| 2265 | | const dest_reg = blk: { |
| 2266 | | const raw_reg = try self.register_manager.allocReg(null, gp); |
| 2267 | | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 2268 | | break :blk reg; |
| 2468 | const read_args = [_]ReadArg{ |
| 2469 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2470 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 2269 | 2471 | }; |
| 2270 | | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2271 | | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2472 | const write_args = [_]WriteArg{ |
| 2473 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 2474 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_high_reg }, |
| 2475 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg }, |
| 2476 | }; |
| 2477 | try self.allocRegs( |
| 2478 | &read_args, |
| 2479 | &write_args, |
| 2480 | null, |
| 2481 | ); |
| 2272 | 2482 | |
| 2273 | 2483 | switch (int_info.signedness) { |
| 2274 | 2484 | .signed => { |
| ... | ... | @@ -2282,10 +2492,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2282 | 2492 | } }, |
| 2283 | 2493 | }); |
| 2284 | 2494 | |
| 2285 | | const dest_high_reg = try self.register_manager.allocReg(null, gp); |
| 2286 | | const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); |
| 2287 | | defer self.register_manager.unlockReg(dest_high_reg_lock); |
| 2288 | | |
| 2289 | 2495 | // smulh dest_high, lhs, rhs |
| 2290 | 2496 | _ = try self.addInst(.{ |
| 2291 | 2497 | .tag = .smulh, |
| ... | ... | @@ -2332,10 +2538,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2332 | 2538 | } |
| 2333 | 2539 | }, |
| 2334 | 2540 | .unsigned => { |
| 2335 | | const dest_high_reg = try self.register_manager.allocReg(null, gp); |
| 2336 | | const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg); |
| 2337 | | defer self.register_manager.unlockReg(dest_high_reg_lock); |
| 2338 | | |
| 2339 | 2541 | // umulh dest_high, lhs, rhs |
| 2340 | 2542 | _ = try self.addInst(.{ |
| 2341 | 2543 | .tag = .umulh, |
| ... | ... | @@ -2356,14 +2558,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2356 | 2558 | } }, |
| 2357 | 2559 | }); |
| 2358 | 2560 | |
| 2359 | | _ = try self.binOp( |
| 2360 | | .cmp_eq, |
| 2361 | | .{ .register = dest_high_reg }, |
| 2362 | | .{ .immediate = 0 }, |
| 2363 | | Type.usize, |
| 2364 | | Type.usize, |
| 2365 | | null, |
| 2366 | | ); |
| 2561 | _ = try self.addInst(.{ |
| 2562 | .tag = .cmp_immediate, |
| 2563 | .data = .{ .r_imm12_sh = .{ |
| 2564 | .rn = dest_high_reg, |
| 2565 | .imm12 = 0, |
| 2566 | } }, |
| 2567 | }); |
| 2367 | 2568 | |
| 2368 | 2569 | if (int_info.bits < 64) { |
| 2369 | 2570 | // lsr dest_high, dest, #shift |
| ... | ... | @@ -2376,22 +2577,17 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2376 | 2577 | } }, |
| 2377 | 2578 | }); |
| 2378 | 2579 | |
| 2379 | | _ = try self.binOp( |
| 2380 | | .cmp_eq, |
| 2381 | | .{ .register = dest_high_reg }, |
| 2382 | | .{ .immediate = 0 }, |
| 2383 | | Type.usize, |
| 2384 | | Type.usize, |
| 2385 | | null, |
| 2386 | | ); |
| 2580 | _ = try self.addInst(.{ |
| 2581 | .tag = .cmp_immediate, |
| 2582 | .data = .{ .r_imm12_sh = .{ |
| 2583 | .rn = dest_high_reg, |
| 2584 | .imm12 = 0, |
| 2585 | } }, |
| 2586 | }); |
| 2387 | 2587 | } |
| 2388 | 2588 | }, |
| 2389 | 2589 | } |
| 2390 | 2590 | |
| 2391 | | const truncated_reg = try self.register_manager.allocReg(null, gp); |
| 2392 | | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); |
| 2393 | | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 2394 | | |
| 2395 | 2591 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2396 | 2592 | |
| 2397 | 2593 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| ... | ... | @@ -2411,8 +2607,8 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2411 | 2607 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2412 | 2608 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2413 | 2609 | const result: MCValue = result: { |
| 2414 | | const lhs = try self.resolveInst(extra.lhs); |
| 2415 | | const rhs = try self.resolveInst(extra.rhs); |
| 2610 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2611 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2416 | 2612 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2417 | 2613 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 2418 | 2614 | |
| ... | ... | @@ -2426,35 +2622,112 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2426 | 2622 | .Int => { |
| 2427 | 2623 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2428 | 2624 | if (int_info.bits <= 64) { |
| 2429 | | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| 2430 | | |
| 2431 | | const lhs_lock: ?RegisterLock = if (lhs == .register) |
| 2432 | | self.register_manager.lockRegAssumeUnused(lhs.register) |
| 2433 | | else |
| 2434 | | null; |
| 2435 | | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2625 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 2436 | 2626 | |
| 2437 | 2627 | try self.spillCompareFlagsIfOccupied(); |
| 2438 | | self.condition_flags_inst = null; |
| 2439 | 2628 | |
| 2440 | | // lsl dest, lhs, rhs |
| 2441 | | const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); |
| 2442 | | const dest_reg = dest.register; |
| 2443 | | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2444 | | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2629 | var lhs_reg: Register = undefined; |
| 2630 | var rhs_reg: Register = undefined; |
| 2631 | var dest_reg: Register = undefined; |
| 2632 | var reconstructed_reg: Register = undefined; |
| 2633 | |
| 2634 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| 2635 | if (rhs_immediate) |imm| { |
| 2636 | const read_args = [_]ReadArg{ |
| 2637 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2638 | }; |
| 2639 | const write_args = [_]WriteArg{ |
| 2640 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 2641 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, |
| 2642 | }; |
| 2643 | try self.allocRegs( |
| 2644 | &read_args, |
| 2645 | &write_args, |
| 2646 | null, |
| 2647 | ); |
| 2648 | |
| 2649 | // lsl dest, lhs, rhs |
| 2650 | _ = try self.addInst(.{ |
| 2651 | .tag = .lsl_immediate, |
| 2652 | .data = .{ .rr_shift = .{ |
| 2653 | .rd = dest_reg, |
| 2654 | .rn = lhs_reg, |
| 2655 | .shift = @intCast(u6, imm), |
| 2656 | } }, |
| 2657 | }); |
| 2658 | |
| 2659 | try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); |
| 2660 | |
| 2661 | // asr/lsr reconstructed, dest, rhs |
| 2662 | _ = try self.addInst(.{ |
| 2663 | .tag = switch (int_info.signedness) { |
| 2664 | .signed => Mir.Inst.Tag.asr_immediate, |
| 2665 | .unsigned => Mir.Inst.Tag.lsr_immediate, |
| 2666 | }, |
| 2667 | .data = .{ .rr_shift = .{ |
| 2668 | .rd = reconstructed_reg, |
| 2669 | .rn = dest_reg, |
| 2670 | .shift = @intCast(u6, imm), |
| 2671 | } }, |
| 2672 | }); |
| 2673 | } else { |
| 2674 | const read_args = [_]ReadArg{ |
| 2675 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2676 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 2677 | }; |
| 2678 | const write_args = [_]WriteArg{ |
| 2679 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 2680 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, |
| 2681 | }; |
| 2682 | try self.allocRegs( |
| 2683 | &read_args, |
| 2684 | &write_args, |
| 2685 | null, |
| 2686 | ); |
| 2687 | |
| 2688 | // lsl dest, lhs, rhs |
| 2689 | _ = try self.addInst(.{ |
| 2690 | .tag = .lsl_register, |
| 2691 | .data = .{ .rrr = .{ |
| 2692 | .rd = dest_reg, |
| 2693 | .rn = lhs_reg, |
| 2694 | .rm = rhs_reg, |
| 2695 | } }, |
| 2696 | }); |
| 2445 | 2697 | |
| 2446 | | // asr/lsr reconstructed, dest, rhs |
| 2447 | | const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null); |
| 2698 | try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); |
| 2699 | |
| 2700 | // asr/lsr reconstructed, dest, rhs |
| 2701 | _ = try self.addInst(.{ |
| 2702 | .tag = switch (int_info.signedness) { |
| 2703 | .signed => Mir.Inst.Tag.asr_register, |
| 2704 | .unsigned => Mir.Inst.Tag.lsr_register, |
| 2705 | }, |
| 2706 | .data = .{ .rrr = .{ |
| 2707 | .rd = reconstructed_reg, |
| 2708 | .rn = dest_reg, |
| 2709 | .rm = rhs_reg, |
| 2710 | } }, |
| 2711 | }); |
| 2712 | } |
| 2448 | 2713 | |
| 2449 | 2714 | // cmp lhs, reconstructed |
| 2450 | | _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); |
| 2715 | _ = try self.addInst(.{ |
| 2716 | .tag = .cmp_shifted_register, |
| 2717 | .data = .{ .rr_imm6_shift = .{ |
| 2718 | .rn = lhs_reg, |
| 2719 | .rm = reconstructed_reg, |
| 2720 | .imm6 = 0, |
| 2721 | .shift = .lsl, |
| 2722 | } }, |
| 2723 | }); |
| 2451 | 2724 | |
| 2452 | | try self.genSetStack(lhs_ty, stack_offset, dest); |
| 2725 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg }); |
| 2453 | 2726 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2454 | 2727 | |
| 2455 | 2728 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2456 | 2729 | } else { |
| 2457 | | return self.fail("TODO overflow operations on integers > u64/i64", .{}); |
| 2730 | return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); |
| 2458 | 2731 | } |
| 2459 | 2732 | }, |
| 2460 | 2733 | else => unreachable, |
| ... | ... | @@ -2712,63 +2985,59 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2712 | 2985 | } |
| 2713 | 2986 | |
| 2714 | 2987 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2715 | | const is_volatile = false; // TODO |
| 2716 | 2988 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2717 | | |
| 2718 | | if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2719 | | const result: MCValue = result: { |
| 2720 | | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2721 | | const elem_ty = slice_ty.childType(); |
| 2722 | | const elem_size = elem_ty.abiSize(self.target.*); |
| 2723 | | const slice_mcv = try self.resolveInst(bin_op.lhs); |
| 2724 | | |
| 2725 | | // TODO optimize for the case where the index is a constant, |
| 2726 | | // i.e. index_mcv == .immediate |
| 2727 | | const index_mcv = try self.resolveInst(bin_op.rhs); |
| 2728 | | const index_is_register = index_mcv == .register; |
| 2729 | | |
| 2989 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2990 | const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { |
| 2730 | 2991 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2731 | | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); |
| 2732 | | |
| 2733 | | const index_lock: ?RegisterLock = if (index_is_register) |
| 2734 | | self.register_manager.lockRegAssumeUnused(index_mcv.register) |
| 2735 | | else |
| 2736 | | null; |
| 2737 | | defer if (index_lock) |reg| self.register_manager.unlockReg(reg); |
| 2992 | const ptr_ty = slice_ty.slicePtrFieldType(&buf); |
| 2738 | 2993 | |
| 2994 | const slice_mcv = try self.resolveInst(bin_op.lhs); |
| 2739 | 2995 | const base_mcv = slicePtr(slice_mcv); |
| 2740 | 2996 | |
| 2741 | | switch (elem_size) { |
| 2742 | | else => { |
| 2743 | | const base_reg = switch (base_mcv) { |
| 2744 | | .register => |r| r, |
| 2745 | | else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv), |
| 2746 | | }; |
| 2747 | | const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg); |
| 2748 | | defer self.register_manager.unlockReg(base_reg_lock); |
| 2749 | | |
| 2750 | | const dest = try self.allocRegOrMem(inst, true); |
| 2751 | | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); |
| 2752 | | try self.load(dest, addr, slice_ptr_field_type); |
| 2997 | const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; |
| 2998 | const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 2753 | 2999 | |
| 2754 | | break :result dest; |
| 2755 | | }, |
| 2756 | | } |
| 3000 | break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); |
| 2757 | 3001 | }; |
| 2758 | 3002 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2759 | 3003 | } |
| 2760 | 3004 | |
| 3005 | fn ptrElemVal( |
| 3006 | self: *Self, |
| 3007 | ptr_bind: ReadArg.Bind, |
| 3008 | index_bind: ReadArg.Bind, |
| 3009 | ptr_ty: Type, |
| 3010 | maybe_inst: ?Air.Inst.Index, |
| 3011 | ) !MCValue { |
| 3012 | const elem_ty = ptr_ty.childType(); |
| 3013 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 3014 | |
| 3015 | // TODO optimize for elem_sizes of 1, 2, 4, 8 |
| 3016 | switch (elem_size) { |
| 3017 | else => { |
| 3018 | const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null); |
| 3019 | |
| 3020 | const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst); |
| 3021 | try self.load(dest, addr, ptr_ty); |
| 3022 | return dest; |
| 3023 | }, |
| 3024 | } |
| 3025 | } |
| 3026 | |
| 2761 | 3027 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2762 | 3028 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2763 | 3029 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2764 | 3030 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2765 | 3031 | const slice_mcv = try self.resolveInst(extra.lhs); |
| 2766 | | const index_mcv = try self.resolveInst(extra.rhs); |
| 2767 | 3032 | const base_mcv = slicePtr(slice_mcv); |
| 2768 | 3033 | |
| 3034 | const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; |
| 3035 | const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 3036 | |
| 2769 | 3037 | const slice_ty = self.air.typeOf(extra.lhs); |
| 3038 | const index_ty = self.air.typeOf(extra.rhs); |
| 2770 | 3039 | |
| 2771 | | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null); |
| 3040 | const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null); |
| 2772 | 3041 | break :result addr; |
| 2773 | 3042 | }; |
| 2774 | 3043 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| ... | ... | @@ -2791,12 +3060,13 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2791 | 3060 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2792 | 3061 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2793 | 3062 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2794 | | const ptr_mcv = try self.resolveInst(extra.lhs); |
| 2795 | | const index_mcv = try self.resolveInst(extra.rhs); |
| 3063 | const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 3064 | const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2796 | 3065 | |
| 2797 | 3066 | const ptr_ty = self.air.typeOf(extra.lhs); |
| 3067 | const index_ty = self.air.typeOf(extra.rhs); |
| 2798 | 3068 | |
| 2799 | | const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null); |
| 3069 | const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null); |
| 2800 | 3070 | break :result addr; |
| 2801 | 3071 | }; |
| 2802 | 3072 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| ... | ... | @@ -2853,7 +3123,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 2853 | 3123 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 2854 | 3124 | } |
| 2855 | 3125 | |
| 2856 | | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { |
| 3126 | fn reuseOperand( |
| 3127 | self: *Self, |
| 3128 | inst: Air.Inst.Index, |
| 3129 | operand: Air.Inst.Ref, |
| 3130 | op_index: Liveness.OperandInt, |
| 3131 | mcv: MCValue, |
| 3132 | ) bool { |
| 2857 | 3133 | if (!self.liveness.operandDies(inst, op_index)) |
| 2858 | 3134 | return false; |
| 2859 | 3135 | |
| ... | ... | @@ -2912,7 +3188,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2912 | 3188 | .stack_offset => |off| { |
| 2913 | 3189 | if (elem_size <= 8) { |
| 2914 | 3190 | const raw_tmp_reg = try self.register_manager.allocReg(null, gp); |
| 2915 | | const tmp_reg = registerAlias(raw_tmp_reg, elem_size); |
| 3191 | const tmp_reg = self.registerAlias(raw_tmp_reg, elem_ty); |
| 2916 | 3192 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 2917 | 3193 | defer self.register_manager.unlockReg(tmp_reg_lock); |
| 2918 | 3194 | |
| ... | ... | @@ -3050,11 +3326,11 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3050 | 3326 | if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { |
| 3051 | 3327 | // The MCValue that holds the pointer can be re-used as the value. |
| 3052 | 3328 | break :blk switch (ptr) { |
| 3053 | | .register => |r| MCValue{ .register = registerAlias(r, elem_size) }, |
| 3329 | .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) }, |
| 3054 | 3330 | else => ptr, |
| 3055 | 3331 | }; |
| 3056 | 3332 | } else { |
| 3057 | | break :blk try self.allocRegOrMem(inst, true); |
| 3333 | break :blk try self.allocRegOrMem(elem_ty, true, inst); |
| 3058 | 3334 | } |
| 3059 | 3335 | }; |
| 3060 | 3336 | try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); |
| ... | ... | @@ -3136,7 +3412,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3136 | 3412 | else => { |
| 3137 | 3413 | if (abi_size <= 8) { |
| 3138 | 3414 | const raw_tmp_reg = try self.register_manager.allocReg(null, gp); |
| 3139 | | const tmp_reg = registerAlias(raw_tmp_reg, abi_size); |
| 3415 | const tmp_reg = self.registerAlias(raw_tmp_reg, value_ty); |
| 3140 | 3416 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 3141 | 3417 | defer self.register_manager.unlockReg(tmp_reg_lock); |
| 3142 | 3418 | |
| ... | ... | @@ -3229,26 +3505,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3229 | 3505 | break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; |
| 3230 | 3506 | }, |
| 3231 | 3507 | else => { |
| 3232 | | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ |
| 3233 | | .immediate = struct_field_offset, |
| 3234 | | }); |
| 3235 | | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 3236 | | defer self.register_manager.unlockReg(offset_reg_lock); |
| 3237 | | |
| 3238 | | const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); |
| 3239 | | const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); |
| 3240 | | defer self.register_manager.unlockReg(addr_reg_lock); |
| 3241 | | |
| 3242 | | const dest = try self.binOp( |
| 3243 | | .add, |
| 3244 | | .{ .register = addr_reg }, |
| 3245 | | .{ .register = offset_reg }, |
| 3246 | | Type.usize, |
| 3247 | | Type.usize, |
| 3248 | | null, |
| 3249 | | ); |
| 3508 | const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; |
| 3509 | const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; |
| 3250 | 3510 | |
| 3251 | | break :result dest; |
| 3511 | break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null); |
| 3252 | 3512 | }, |
| 3253 | 3513 | } |
| 3254 | 3514 | }; |
| ... | ... | @@ -3295,7 +3555,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3295 | 3555 | } else { |
| 3296 | 3556 | // Copy to new register |
| 3297 | 3557 | const raw_dest_reg = try self.register_manager.allocReg(null, gp); |
| 3298 | | const dest_reg = registerAlias(raw_dest_reg, struct_field_ty.abiSize(self.target.*)); |
| 3558 | const dest_reg = self.registerAlias(raw_dest_reg, struct_field_ty); |
| 3299 | 3559 | try self.genSetReg(struct_field_ty, dest_reg, field); |
| 3300 | 3560 | |
| 3301 | 3561 | break :result MCValue{ .register = dest_reg }; |
| ... | ... | @@ -3330,7 +3590,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 3330 | 3590 | return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); |
| 3331 | 3591 | }; |
| 3332 | 3592 | const abi_align = ty.abiAlignment(self.target.*); |
| 3333 | | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 3593 | const stack_offset = try self.allocMem(abi_size, abi_align, inst); |
| 3334 | 3594 | try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 3335 | 3595 | |
| 3336 | 3596 | break :blk MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -3408,11 +3668,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3408 | 3668 | const ret_ty = fn_ty.fnReturnType(); |
| 3409 | 3669 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 3410 | 3670 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| 3411 | | const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align); |
| 3671 | const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst); |
| 3412 | 3672 | |
| 3413 | | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 3414 | | const ptr_bytes = @divExact(ptr_bits, 8); |
| 3415 | | const ret_ptr_reg = registerAlias(.x0, ptr_bytes); |
| 3673 | const ret_ptr_reg = self.registerAlias(.x0, Type.usize); |
| 3416 | 3674 | |
| 3417 | 3675 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 3418 | 3676 | .base = .{ .tag = .single_mut_pointer }, |
| ... | ... | @@ -3636,14 +3894,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3636 | 3894 | const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 3637 | 3895 | const abi_align = ret_ty.abiAlignment(self.target.*); |
| 3638 | 3896 | |
| 3639 | | // This is essentially allocMem without the |
| 3640 | | // instruction tracking |
| 3641 | | if (abi_align > self.stack_align) |
| 3642 | | self.stack_align = abi_align; |
| 3643 | | // TODO find a free slot instead of always appending |
| 3644 | | const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; |
| 3645 | | self.next_stack_offset = offset; |
| 3646 | | self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); |
| 3897 | const offset = try self.allocMem(abi_size, abi_align, null); |
| 3647 | 3898 | |
| 3648 | 3899 | const tmp_mcv = MCValue{ .stack_offset = offset }; |
| 3649 | 3900 | try self.load(tmp_mcv, ptr, ptr_ty); |
| ... | ... | @@ -3660,54 +3911,100 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3660 | 3911 | |
| 3661 | 3912 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3662 | 3913 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3663 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3664 | | const lhs = try self.resolveInst(bin_op.lhs); |
| 3665 | | const rhs = try self.resolveInst(bin_op.rhs); |
| 3666 | | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 3667 | | |
| 3668 | | var int_buffer: Type.Payload.Bits = undefined; |
| 3669 | | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| 3670 | | .Vector => return self.fail("TODO AArch64 cmp vectors", .{}), |
| 3671 | | .Enum => lhs_ty.intTagType(&int_buffer), |
| 3672 | | .Int => lhs_ty, |
| 3673 | | .Bool => Type.initTag(.u1), |
| 3674 | | .Pointer => Type.usize, |
| 3675 | | .ErrorSet => Type.initTag(.u16), |
| 3676 | | .Optional => blk: { |
| 3677 | | var opt_buffer: Type.Payload.ElemType = undefined; |
| 3678 | | const payload_ty = lhs_ty.optionalChild(&opt_buffer); |
| 3679 | | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3680 | | break :blk Type.initTag(.u1); |
| 3681 | | } else if (lhs_ty.isPtrLikeOptional()) { |
| 3682 | | break :blk Type.usize; |
| 3683 | | } else { |
| 3684 | | return self.fail("TODO AArch64 cmp non-pointer optionals", .{}); |
| 3685 | | } |
| 3686 | | }, |
| 3687 | | .Float => return self.fail("TODO AArch64 cmp floats", .{}), |
| 3688 | | else => unreachable, |
| 3689 | | }; |
| 3914 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 3690 | 3915 | |
| 3691 | | const int_info = int_ty.intInfo(self.target.*); |
| 3692 | | if (int_info.bits <= 64) { |
| 3693 | | _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ |
| 3694 | | .inst = inst, |
| 3695 | | .lhs = bin_op.lhs, |
| 3696 | | .rhs = bin_op.rhs, |
| 3697 | | }); |
| 3916 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 3917 | break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op); |
| 3918 | }; |
| 3919 | |
| 3920 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3921 | } |
| 3922 | |
| 3923 | fn cmp( |
| 3924 | self: *Self, |
| 3925 | lhs: ReadArg.Bind, |
| 3926 | rhs: ReadArg.Bind, |
| 3927 | lhs_ty: Type, |
| 3928 | op: math.CompareOperator, |
| 3929 | ) !MCValue { |
| 3930 | var int_buffer: Type.Payload.Bits = undefined; |
| 3931 | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| 3932 | .Optional => blk: { |
| 3933 | var opt_buffer: Type.Payload.ElemType = undefined; |
| 3934 | const payload_ty = lhs_ty.optionalChild(&opt_buffer); |
| 3935 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3936 | break :blk Type.initTag(.u1); |
| 3937 | } else if (lhs_ty.isPtrLikeOptional()) { |
| 3938 | break :blk Type.usize; |
| 3939 | } else { |
| 3940 | return self.fail("TODO ARM cmp non-pointer optionals", .{}); |
| 3941 | } |
| 3942 | }, |
| 3943 | .Float => return self.fail("TODO ARM cmp floats", .{}), |
| 3944 | .Enum => lhs_ty.intTagType(&int_buffer), |
| 3945 | .Int => lhs_ty, |
| 3946 | .Bool => Type.initTag(.u1), |
| 3947 | .Pointer => Type.usize, |
| 3948 | .ErrorSet => Type.initTag(.u16), |
| 3949 | else => unreachable, |
| 3950 | }; |
| 3698 | 3951 | |
| 3699 | | try self.spillCompareFlagsIfOccupied(); |
| 3700 | | self.condition_flags_inst = inst; |
| 3952 | const int_info = int_ty.intInfo(self.target.*); |
| 3953 | if (int_info.bits <= 64) { |
| 3954 | try self.spillCompareFlagsIfOccupied(); |
| 3701 | 3955 | |
| 3702 | | break :result switch (int_info.signedness) { |
| 3703 | | .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) }, |
| 3704 | | .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) }, |
| 3956 | var lhs_reg: Register = undefined; |
| 3957 | var rhs_reg: Register = undefined; |
| 3958 | |
| 3959 | const rhs_immediate = try rhs.resolveToImmediate(self); |
| 3960 | const rhs_immediate_ok = if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false; |
| 3961 | |
| 3962 | if (rhs_immediate_ok) { |
| 3963 | const read_args = [_]ReadArg{ |
| 3964 | .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, |
| 3705 | 3965 | }; |
| 3966 | try self.allocRegs( |
| 3967 | &read_args, |
| 3968 | &.{}, |
| 3969 | null, // we won't be able to reuse a register as there are no write_regs |
| 3970 | ); |
| 3971 | |
| 3972 | _ = try self.addInst(.{ |
| 3973 | .tag = .cmp_immediate, |
| 3974 | .data = .{ .r_imm12_sh = .{ |
| 3975 | .rn = lhs_reg, |
| 3976 | .imm12 = @intCast(u12, rhs_immediate.?), |
| 3977 | } }, |
| 3978 | }); |
| 3706 | 3979 | } else { |
| 3707 | | return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); |
| 3980 | const read_args = [_]ReadArg{ |
| 3981 | .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, |
| 3982 | .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg }, |
| 3983 | }; |
| 3984 | try self.allocRegs( |
| 3985 | &read_args, |
| 3986 | &.{}, |
| 3987 | null, // we won't be able to reuse a register as there are no write_regs |
| 3988 | ); |
| 3989 | |
| 3990 | _ = try self.addInst(.{ |
| 3991 | .tag = .cmp_shifted_register, |
| 3992 | .data = .{ .rr_imm6_shift = .{ |
| 3993 | .rn = lhs_reg, |
| 3994 | .rm = rhs_reg, |
| 3995 | .imm6 = 0, |
| 3996 | .shift = .lsl, |
| 3997 | } }, |
| 3998 | }); |
| 3708 | 3999 | } |
| 3709 | | }; |
| 3710 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 4000 | |
| 4001 | return switch (int_info.signedness) { |
| 4002 | .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) }, |
| 4003 | .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) }, |
| 4004 | }; |
| 4005 | } else { |
| 4006 | return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); |
| 4007 | } |
| 3711 | 4008 | } |
| 3712 | 4009 | |
| 3713 | 4010 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3952,15 +4249,13 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 3952 | 4249 | |
| 3953 | 4250 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3954 | 4251 | const error_type = ty.errorUnionSet(); |
| 3955 | | const error_int_type = Type.initTag(.u16); |
| 3956 | 4252 | |
| 3957 | 4253 | if (error_type.errorSetIsEmpty()) { |
| 3958 | 4254 | return MCValue{ .immediate = 0 }; // always false |
| 3959 | 4255 | } |
| 3960 | 4256 | |
| 3961 | 4257 | const error_mcv = try self.errUnionErr(operand, ty); |
| 3962 | | _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null); |
| 3963 | | return MCValue{ .condition_flags = .hi }; |
| 4258 | return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); |
| 3964 | 4259 | } |
| 3965 | 4260 | |
| 3966 | 4261 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| ... | ... | @@ -3991,15 +4286,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3991 | 4286 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 3992 | 4287 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3993 | 4288 | const operand_ptr = try self.resolveInst(un_op); |
| 3994 | | const operand: MCValue = blk: { |
| 3995 | | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 3996 | | // The MCValue that holds the pointer can be re-used as the value. |
| 3997 | | break :blk operand_ptr; |
| 3998 | | } else { |
| 3999 | | break :blk try self.allocRegOrMem(inst, true); |
| 4000 | | } |
| 4001 | | }; |
| 4002 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 4289 | const ptr_ty = self.air.typeOf(un_op); |
| 4290 | const elem_ty = ptr_ty.elemType(); |
| 4291 | |
| 4292 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4293 | try self.load(operand, operand_ptr, ptr_ty); |
| 4294 | |
| 4003 | 4295 | break :result try self.isNull(operand); |
| 4004 | 4296 | }; |
| 4005 | 4297 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| ... | ... | @@ -4018,15 +4310,12 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4018 | 4310 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4019 | 4311 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4020 | 4312 | const operand_ptr = try self.resolveInst(un_op); |
| 4021 | | const operand: MCValue = blk: { |
| 4022 | | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 4023 | | // The MCValue that holds the pointer can be re-used as the value. |
| 4024 | | break :blk operand_ptr; |
| 4025 | | } else { |
| 4026 | | break :blk try self.allocRegOrMem(inst, true); |
| 4027 | | } |
| 4028 | | }; |
| 4029 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 4313 | const ptr_ty = self.air.typeOf(un_op); |
| 4314 | const elem_ty = ptr_ty.elemType(); |
| 4315 | |
| 4316 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4317 | try self.load(operand, operand_ptr, ptr_ty); |
| 4318 | |
| 4030 | 4319 | break :result try self.isNonNull(operand); |
| 4031 | 4320 | }; |
| 4032 | 4321 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| ... | ... | @@ -4047,16 +4336,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4047 | 4336 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4048 | 4337 | const operand_ptr = try self.resolveInst(un_op); |
| 4049 | 4338 | const ptr_ty = self.air.typeOf(un_op); |
| 4050 | | const operand: MCValue = blk: { |
| 4051 | | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 4052 | | // The MCValue that holds the pointer can be re-used as the value. |
| 4053 | | break :blk operand_ptr; |
| 4054 | | } else { |
| 4055 | | break :blk try self.allocRegOrMem(inst, true); |
| 4056 | | } |
| 4057 | | }; |
| 4058 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 4059 | | break :result try self.isErr(ptr_ty.elemType(), operand); |
| 4339 | const elem_ty = ptr_ty.elemType(); |
| 4340 | |
| 4341 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4342 | try self.load(operand, operand_ptr, ptr_ty); |
| 4343 | |
| 4344 | break :result try self.isErr(elem_ty, operand); |
| 4060 | 4345 | }; |
| 4061 | 4346 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4062 | 4347 | } |
| ... | ... | @@ -4076,16 +4361,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4076 | 4361 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4077 | 4362 | const operand_ptr = try self.resolveInst(un_op); |
| 4078 | 4363 | const ptr_ty = self.air.typeOf(un_op); |
| 4079 | | const operand: MCValue = blk: { |
| 4080 | | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { |
| 4081 | | // The MCValue that holds the pointer can be re-used as the value. |
| 4082 | | break :blk operand_ptr; |
| 4083 | | } else { |
| 4084 | | break :blk try self.allocRegOrMem(inst, true); |
| 4085 | | } |
| 4086 | | }; |
| 4087 | | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); |
| 4088 | | break :result try self.isNonErr(ptr_ty.elemType(), operand); |
| 4364 | const elem_ty = ptr_ty.elemType(); |
| 4365 | |
| 4366 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4367 | try self.load(operand, operand_ptr, ptr_ty); |
| 4368 | |
| 4369 | break :result try self.isNonErr(elem_ty, operand); |
| 4089 | 4370 | }; |
| 4090 | 4371 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4091 | 4372 | } |
| ... | ... | @@ -4178,7 +4459,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 4178 | 4459 | .none, .dead, .unreach => unreachable, |
| 4179 | 4460 | .register, .stack_offset, .memory => operand_mcv, |
| 4180 | 4461 | .immediate, .stack_argument_offset, .condition_flags => blk: { |
| 4181 | | const new_mcv = try self.allocRegOrMem(block, true); |
| 4462 | const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block); |
| 4182 | 4463 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 4183 | 4464 | break :blk new_mcv; |
| 4184 | 4465 | }, |
| ... | ... | @@ -4376,7 +4657,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4376 | 4657 | 4, 8 => .str_stack, |
| 4377 | 4658 | else => unreachable, // unexpected abi size |
| 4378 | 4659 | }; |
| 4379 | | const rt = registerAlias(reg, abi_size); |
| 4660 | const rt = self.registerAlias(reg, ty); |
| 4380 | 4661 | |
| 4381 | 4662 | _ = try self.addInst(.{ |
| 4382 | 4663 | .tag = tag, |
| ... | ... | @@ -4399,10 +4680,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4399 | 4680 | const overflow_bit_ty = ty.structFieldType(1); |
| 4400 | 4681 | const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); |
| 4401 | 4682 | const raw_cond_reg = try self.register_manager.allocReg(null, gp); |
| 4402 | | const cond_reg = registerAlias( |
| 4403 | | raw_cond_reg, |
| 4404 | | @intCast(u32, overflow_bit_ty.abiSize(self.target.*)), |
| 4405 | | ); |
| 4683 | const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty); |
| 4406 | 4684 | |
| 4407 | 4685 | _ = try self.addInst(.{ |
| 4408 | 4686 | .tag = .cset, |
| ... | ... | @@ -4515,16 +4793,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4515 | 4793 | } |
| 4516 | 4794 | }, |
| 4517 | 4795 | .ptr_stack_offset => |off| { |
| 4518 | | // TODO: maybe addressing from sp instead of fp |
| 4519 | | const imm12 = math.cast(u12, off) orelse |
| 4520 | | return self.fail("TODO larger stack offsets", .{}); |
| 4521 | | |
| 4522 | 4796 | _ = try self.addInst(.{ |
| 4523 | | .tag = .sub_immediate, |
| 4524 | | .data = .{ .rr_imm12_sh = .{ |
| 4525 | | .rd = reg, |
| 4526 | | .rn = .x29, |
| 4527 | | .imm12 = imm12, |
| 4797 | .tag = .ldr_ptr_stack, |
| 4798 | .data = .{ .load_store_stack = .{ |
| 4799 | .rt = reg, |
| 4800 | .offset = @intCast(u32, off), |
| 4528 | 4801 | } }, |
| 4529 | 4802 | }); |
| 4530 | 4803 | }, |
| ... | ... | @@ -4599,8 +4872,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4599 | 4872 | .memory => |addr| { |
| 4600 | 4873 | // The value is in memory at a hard-coded address. |
| 4601 | 4874 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 4602 | | try self.genSetReg(ty, reg.to64(), .{ .immediate = addr }); |
| 4603 | | try self.genLdrRegister(reg, reg.to64(), ty); |
| 4875 | try self.genSetReg(ty, reg.toX(), .{ .immediate = addr }); |
| 4876 | try self.genLdrRegister(reg, reg.toX(), ty); |
| 4604 | 4877 | }, |
| 4605 | 4878 | .stack_offset => |off| { |
| 4606 | 4879 | const abi_size = ty.abiSize(self.target.*); |
| ... | ... | @@ -4679,7 +4952,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4679 | 4952 | 4, 8 => .str_immediate, |
| 4680 | 4953 | else => unreachable, // unexpected abi size |
| 4681 | 4954 | }; |
| 4682 | | const rt = registerAlias(reg, abi_size); |
| 4955 | const rt = self.registerAlias(reg, ty); |
| 4683 | 4956 | const offset = switch (abi_size) { |
| 4684 | 4957 | 1 => blk: { |
| 4685 | 4958 | if (math.cast(u12, stack_offset)) |imm| { |
| ... | ... | @@ -4838,7 +5111,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 4838 | 5111 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 4839 | 5112 | const ptr_bytes = @divExact(ptr_bits, 8); |
| 4840 | 5113 | |
| 4841 | | const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); |
| 5114 | const stack_offset = try self.allocMem(ptr_bytes * 2, ptr_bytes * 2, inst); |
| 4842 | 5115 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 4843 | 5116 | try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len }); |
| 4844 | 5117 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -5300,7 +5573,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5300 | 5573 | assert(ret_ty.isError()); |
| 5301 | 5574 | result.return_value = .{ .immediate = 0 }; |
| 5302 | 5575 | } else if (ret_ty_size <= 8) { |
| 5303 | | result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; |
| 5576 | result.return_value = .{ .register = self.registerAlias(c_abi_int_return_regs[0], ret_ty) }; |
| 5304 | 5577 | } else { |
| 5305 | 5578 | return self.fail("TODO support more return types for ARM backend", .{}); |
| 5306 | 5579 | } |
| ... | ... | @@ -5322,7 +5595,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5322 | 5595 | |
| 5323 | 5596 | if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { |
| 5324 | 5597 | if (param_size <= 8) { |
| 5325 | | result.args[i] = .{ .register = registerAlias(c_abi_int_param_regs[ncrn], param_size) }; |
| 5598 | result.args[i] = .{ .register = self.registerAlias(c_abi_int_param_regs[ncrn], ty) }; |
| 5326 | 5599 | ncrn += 1; |
| 5327 | 5600 | } else { |
| 5328 | 5601 | return self.fail("TODO MCValues with multiple registers", .{}); |
| ... | ... | @@ -5358,7 +5631,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5358 | 5631 | assert(ret_ty.isError()); |
| 5359 | 5632 | result.return_value = .{ .immediate = 0 }; |
| 5360 | 5633 | } else if (ret_ty_size <= 8) { |
| 5361 | | result.return_value = .{ .register = registerAlias(.x0, ret_ty_size) }; |
| 5634 | result.return_value = .{ .register = self.registerAlias(.x0, ret_ty) }; |
| 5362 | 5635 | } else { |
| 5363 | 5636 | // The result is returned by reference, not by |
| 5364 | 5637 | // value. This means that x0 (or w0 when pointer |
| ... | ... | @@ -5424,14 +5697,30 @@ fn parseRegName(name: []const u8) ?Register { |
| 5424 | 5697 | return std.meta.stringToEnum(Register, name); |
| 5425 | 5698 | } |
| 5426 | 5699 | |
| 5427 | | fn registerAlias(reg: Register, size_bytes: u64) Register { |
| 5428 | | if (size_bytes == 0) { |
| 5429 | | unreachable; // should be comptime-known |
| 5430 | | } else if (size_bytes <= 4) { |
| 5431 | | return reg.to32(); |
| 5432 | | } else if (size_bytes <= 8) { |
| 5433 | | return reg.to64(); |
| 5434 | | } else { |
| 5435 | | unreachable; // TODO handle floating-point registers |
| 5700 | fn registerAlias(self: *Self, reg: Register, ty: Type) Register { |
| 5701 | const abi_size = ty.abiSize(self.target.*); |
| 5702 | |
| 5703 | switch (reg.class()) { |
| 5704 | .general_purpose => { |
| 5705 | if (abi_size == 0) { |
| 5706 | unreachable; // should be comptime-known |
| 5707 | } else if (abi_size <= 4) { |
| 5708 | return reg.toW(); |
| 5709 | } else if (abi_size <= 8) { |
| 5710 | return reg.toX(); |
| 5711 | } else unreachable; |
| 5712 | }, |
| 5713 | .stack_pointer => unreachable, // we can't store/load the sp |
| 5714 | .floating_point => { |
| 5715 | return switch (ty.floatBits(self.target.*)) { |
| 5716 | 16 => reg.toH(), |
| 5717 | 32 => reg.toS(), |
| 5718 | 64 => reg.toD(), |
| 5719 | 128 => reg.toQ(), |
| 5720 | |
| 5721 | 80 => unreachable, // f80 registers don't exist |
| 5722 | else => unreachable, |
| 5723 | }; |
| 5724 | }, |
| 5436 | 5725 | } |
| 5437 | 5726 | } |