| ... | @@ -157,40 +157,6 @@ const MCValue = union(enum) { | ... | @@ -157,40 +157,6 @@ const MCValue = union(enum) { |
| 157 | condition_flags: Condition, | 157 | condition_flags: Condition, |
| 158 | /// The value is a function argument passed via the stack. | 158 | /// The value is a function argument passed via the stack. |
| 159 | stack_argument_offset: u32, | 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 | const Branch = struct { | 162 | const Branch = struct { |
| ... | @@ -414,11 +380,9 @@ fn gen(self: *Self) !void { | ... | @@ -414,11 +380,9 @@ fn gen(self: *Self) !void { |
| 414 | // to the stack. | 380 | // to the stack. |
| 415 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 381 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 416 | const ptr_bytes = @divExact(ptr_bits, 8); | 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; | 385 | const stack_offset = try self.allocMem(ptr_bytes, ptr_bytes, null); |
| 420 | self.next_stack_offset = stack_offset; | | |
| 421 | self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); | | |
| 422 | | 386 | |
| 423 | try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg }); | 387 | try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg }); |
| 424 | self.ret_mcv = MCValue{ .stack_offset = stack_offset }; | 388 | self.ret_mcv = MCValue{ .stack_offset = stack_offset }; |
| ... | @@ -879,17 +843,30 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { | ... | @@ -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 | if (abi_align > self.stack_align) | 855 | if (abi_align > self.stack_align) |
| 884 | self.stack_align = abi_align; | 856 | self.stack_align = abi_align; |
| | 857 | |
| 885 | // TODO find a free slot instead of always appending | 858 | // TODO find a free slot instead of always appending |
| 886 | const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; | 859 | const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; |
| 887 | self.next_stack_offset = offset; | 860 | self.next_stack_offset = offset; |
| 888 | self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); | 861 | self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); |
| 889 | try self.stack.putNoClobber(self.gpa, offset, .{ | 862 | |
| 890 | .inst = inst, | 863 | if (maybe_inst) |inst| { |
| 891 | .size = abi_size, | 864 | try self.stack.putNoClobber(self.gpa, offset, .{ |
| 892 | }); | 865 | .inst = inst, |
| | 866 | .size = abi_size, |
| | 867 | }); |
| | 868 | } |
| | 869 | |
| 893 | return offset; | 870 | return offset; |
| 894 | } | 871 | } |
| 895 | | 872 | |
| ... | @@ -910,40 +887,41 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { | ... | @@ -910,40 +887,41 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 910 | }; | 887 | }; |
| 911 | // TODO swap this for inst.ty.ptrAlign | 888 | // TODO swap this for inst.ty.ptrAlign |
| 912 | const abi_align = elem_ty.abiAlignment(self.target.*); | 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 { | 894 | fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { |
| 917 | const elem_ty = self.air.typeOfIndex(inst); | | |
| 918 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { | 895 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { |
| 919 | const mod = self.bin_file.options.module.?; | 896 | const mod = self.bin_file.options.module.?; |
| 920 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); | 897 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); |
| 921 | }; | 898 | }; |
| 922 | const abi_align = elem_ty.abiAlignment(self.target.*); | 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 | if (reg_ok) { | 901 | if (reg_ok) { |
| 927 | // Make sure the type can fit in a register before we try to allocate one. | 902 | // Make sure the type can fit in a register before we try to allocate one. |
| 928 | if (abi_size <= 8) { | 903 | if (abi_size <= 8) { |
| 929 | if (self.register_manager.tryAllocReg(inst, gp)) |reg| { | 904 | if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| { |
| 930 | return MCValue{ .register = registerAlias(reg, abi_size) }; | 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 | return MCValue{ .stack_offset = stack_offset }; | 911 | return MCValue{ .stack_offset = stack_offset }; |
| 936 | } | 912 | } |
| 937 | | 913 | |
| 938 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 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 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); | 916 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| | 917 | |
| 941 | const reg_mcv = self.getResolvedInstValue(inst); | 918 | const reg_mcv = self.getResolvedInstValue(inst); |
| 942 | switch (reg_mcv) { | 919 | switch (reg_mcv) { |
| 943 | .register => |r| assert(reg.id() == r.id()), | 920 | .register => |r| assert(reg.id() == r.id()), |
| 944 | .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()), | 921 | .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()), |
| 945 | else => unreachable, // not a register | 922 | else => unreachable, // not a register |
| 946 | } | 923 | } |
| | 924 | |
| 947 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 925 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 948 | try branch.inst_table.put(self.gpa, inst, stack_mcv); | 926 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 949 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); | 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,10 +931,11 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 953 | /// occupied | 931 | /// occupied |
| 954 | fn spillCompareFlagsIfOccupied(self: *Self) !void { | 932 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 955 | if (self.condition_flags_inst) |inst_to_save| { | 933 | if (self.condition_flags_inst) |inst_to_save| { |
| | 934 | const ty = self.air.typeOfIndex(inst_to_save); |
| 956 | const mcv = self.getResolvedInstValue(inst_to_save); | 935 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 957 | const new_mcv = switch (mcv) { | 936 | const new_mcv = switch (mcv) { |
| 958 | .condition_flags => try self.allocRegOrMem(inst_to_save, true), | 937 | .condition_flags => try self.allocRegOrMem(ty, true, inst_to_save), |
| 959 | .register_with_overflow => try self.allocRegOrMem(inst_to_save, false), | 938 | .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save), |
| 960 | else => unreachable, // mcv doesn't occupy the compare flags | 939 | else => unreachable, // mcv doesn't occupy the compare flags |
| 961 | }; | 940 | }; |
| 962 | | 941 | |
| ... | @@ -982,7 +961,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { | ... | @@ -982,7 +961,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 982 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 961 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 983 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { | 962 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 984 | const raw_reg = try self.register_manager.allocReg(null, gp); | 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 | try self.genSetReg(ty, reg, mcv); | 965 | try self.genSetReg(ty, reg, mcv); |
| 987 | return reg; | 966 | return reg; |
| 988 | } | 967 | } |
| ... | @@ -993,7 +972,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { | ... | @@ -993,7 +972,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 993 | fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { | 972 | fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { |
| 994 | const raw_reg = try self.register_manager.allocReg(reg_owner, gp); | 973 | const raw_reg = try self.register_manager.allocReg(reg_owner, gp); |
| 995 | const ty = self.air.typeOfIndex(reg_owner); | 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 | try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); | 976 | try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); |
| 998 | return MCValue{ .register = reg }; | 977 | return MCValue{ .register = reg }; |
| 999 | } | 978 | } |
| ... | @@ -1031,7 +1010,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1031,7 +1010,6 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1031 | const operand_info = operand_ty.intInfo(self.target.*); | 1010 | const operand_info = operand_ty.intInfo(self.target.*); |
| 1032 | | 1011 | |
| 1033 | const dest_ty = self.air.typeOfIndex(inst); | 1012 | const dest_ty = self.air.typeOfIndex(inst); |
| 1034 | const dest_abi_size = dest_ty.abiSize(self.target.*); | | |
| 1035 | const dest_info = dest_ty.intInfo(self.target.*); | 1013 | const dest_info = dest_ty.intInfo(self.target.*); |
| 1036 | | 1014 | |
| 1037 | const result: MCValue = result: { | 1015 | const result: MCValue = result: { |
| ... | @@ -1042,19 +1020,19 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1042,19 +1020,19 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1042 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); | 1020 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 1043 | | 1021 | |
| 1044 | const truncated: MCValue = switch (operand_mcv) { | 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 | else => operand_mcv, | 1024 | else => operand_mcv, |
| 1047 | }; | 1025 | }; |
| 1048 | | 1026 | |
| 1049 | if (dest_info.bits > operand_info.bits) { | 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 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); | 1029 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); |
| 1052 | break :result dest_mcv; | 1030 | break :result dest_mcv; |
| 1053 | } else { | 1031 | } else { |
| 1054 | if (self.reuseOperand(inst, operand, 0, truncated)) { | 1032 | if (self.reuseOperand(inst, operand, 0, truncated)) { |
| 1055 | break :result truncated; | 1033 | break :result truncated; |
| 1056 | } else { | 1034 | } else { |
| 1057 | const dest_mcv = try self.allocRegOrMem(inst, true); | 1035 | const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst); |
| 1058 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); | 1036 | try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); |
| 1059 | break :result dest_mcv; | 1037 | break :result dest_mcv; |
| 1060 | } | 1038 | } |
| ... | @@ -1117,7 +1095,7 @@ fn trunc( | ... | @@ -1117,7 +1095,7 @@ fn trunc( |
| 1117 | else => operand_reg: { | 1095 | else => operand_reg: { |
| 1118 | if (info_a.bits <= 64) { | 1096 | if (info_a.bits <= 64) { |
| 1119 | const raw_reg = try self.copyToTmpRegister(operand_ty, operand); | 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 | } else { | 1099 | } else { |
| 1122 | return self.fail("TODO load least significant word into register", .{}); | 1100 | return self.fail("TODO load least significant word into register", .{}); |
| 1123 | } | 1101 | } |
| ... | @@ -1130,14 +1108,14 @@ fn trunc( | ... | @@ -1130,14 +1108,14 @@ fn trunc( |
| 1130 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1108 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1131 | | 1109 | |
| 1132 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { | 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 | } else { | 1112 | } else { |
| 1135 | const raw_reg = try self.register_manager.allocReg(inst, gp); | 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 | } else blk: { | 1116 | } else blk: { |
| 1139 | const raw_reg = try self.register_manager.allocReg(null, gp); | 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 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); | 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,7 +1172,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1194 | } | 1172 | } |
| 1195 | | 1173 | |
| 1196 | const raw_reg = try self.register_manager.allocReg(null, gp); | 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 | _ = try self.addInst(.{ | 1178 | _ = try self.addInst(.{ |
| ... | @@ -1227,7 +1205,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1227,7 +1205,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1227 | } | 1205 | } |
| 1228 | | 1206 | |
| 1229 | const raw_reg = try self.register_manager.allocReg(null, gp); | 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 | _ = try self.addInst(.{ | 1211 | _ = try self.addInst(.{ |
| ... | @@ -1279,7 +1257,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1279,7 +1257,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1279 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 1257 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 1280 | const ptr_bytes = @divExact(ptr_bits, 8); | 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 | try self.genSetStack(ptr_ty, stack_offset, ptr); | 1261 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 1284 | try self.genSetStack(len_ty, stack_offset - ptr_bytes, len); | 1262 | try self.genSetStack(len_ty, stack_offset - ptr_bytes, len); |
| 1285 | break :result MCValue{ .stack_offset = stack_offset }; | 1263 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | @@ -1287,101 +1265,266 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1287,101 +1265,266 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1287 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | 1342 | /// If the Mir instruction these registers are allocated for |
| 1293 | /// instruction of the form | 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. | 1353 | /// Furthermore, allocReg guarantees that all arguments which are |
| 1298 | fn binOpRegister( | 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 | self: *Self, | 1358 | self: *Self, |
| 1300 | mir_tag: Mir.Inst.Tag, | 1359 | read_args: []const ReadArg, |
| 1301 | lhs: MCValue, | 1360 | write_args: []const WriteArg, |
| 1302 | rhs: MCValue, | 1361 | reuse_metadata: ?ReuseMetadata, |
| 1303 | lhs_ty: Type, | 1362 | ) InnerError!void { |
| 1304 | rhs_ty: Type, | 1363 | // Air instructions have exactly one output |
| 1305 | metadata: ?BinOpMetadata, | 1364 | assert(!(reuse_metadata != null and write_args.len != 1)); // see note above |
| 1306 | ) !MCValue { | 1365 | |
| 1307 | const lhs_is_register = lhs == .register; | 1366 | // The operand mapping is a 1:1 mapping of read args to their |
| 1308 | const rhs_is_register = rhs == .register; | 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.*))); | 1380 | // When we reuse a read_arg as a destination, the corresponding |
| 1311 | if (rhs_is_register) assert(rhs.register == registerAlias(rhs.register, rhs_ty.abiSize(self.target.*))); | 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) | 1385 | // Lock all args which are already allocated to registers |
| 1314 | self.register_manager.lockReg(lhs.register) | 1386 | for (read_args) |arg, i| { |
| 1315 | else | 1387 | const mcv = try arg.bind.resolveToMcv(self); |
| 1316 | null; | 1388 | if (mcv == .register) { |
| 1317 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | 1389 | read_locks[i] = self.register_manager.lockReg(mcv.register); |
| | 1390 | } |
| | 1391 | } |
| 1318 | | 1392 | |
| 1319 | const rhs_lock: ?RegisterLock = if (rhs_is_register) | 1393 | for (write_args) |arg, i| { |
| 1320 | self.register_manager.lockReg(rhs.register) | 1394 | if (arg.bind == .reg) { |
| 1321 | else | 1395 | write_locks[i] = self.register_manager.lockReg(arg.bind.reg); |
| 1322 | null; | 1396 | } |
| 1323 | defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg); | 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: { | 1417 | if (reuse_metadata != null) { |
| 1328 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | 1418 | const inst = reuse_metadata.?.corresponding_inst; |
| 1329 | break :inst Air.refToIndex(md.lhs).?; | 1419 | const operand_mapping = reuse_metadata.?.operand_mapping; |
| 1330 | } else null; | 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); | 1459 | // For all read_args which need to be moved from non-register to |
| 1333 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | 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; | 1473 | // Overwrite the MCValue associated with this inst |
| 1338 | }; | 1474 | branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* }); |
| 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; | | |
| 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 | 1491 | try self.genSetReg(arg.ty, arg.reg.*, mcv); |
| 1356 | // the case of shifts. See comment above. | 1492 | } |
| 1357 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | 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); | 1517 | const write_args = [_]WriteArg{ |
| 1364 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); | 1518 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_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 | }, | | |
| 1381 | }; | 1519 | }; |
| 1382 | | 1520 | try self.allocRegs( |
| 1383 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 1521 | &read_args, |
| 1384 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | 1522 | &write_args, |
| | 1523 | if (maybe_inst) |inst| .{ |
| | 1524 | .corresponding_inst = inst, |
| | 1525 | .operand_mapping = &.{ 0, 1 }, |
| | 1526 | } else null, |
| | 1527 | ); |
| 1385 | | 1528 | |
| 1386 | const mir_data: Mir.Inst.Data = switch (mir_tag) { | 1529 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1387 | .add_shifted_register, | 1530 | .add_shifted_register, |
| ... | @@ -1395,12 +1538,6 @@ fn binOpRegister( | ... | @@ -1395,12 +1538,6 @@ fn binOpRegister( |
| 1395 | .imm6 = 0, | 1538 | .imm6 = 0, |
| 1396 | .shift = .lsl, | 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 | .mul, | 1541 | .mul, |
| 1405 | .lsl_register, | 1542 | .lsl_register, |
| 1406 | .asr_register, | 1543 | .asr_register, |
| ... | @@ -1415,7 +1552,7 @@ fn binOpRegister( | ... | @@ -1415,7 +1552,7 @@ fn binOpRegister( |
| 1415 | .smull, | 1552 | .smull, |
| 1416 | .umull, | 1553 | .umull, |
| 1417 | => .{ .rrr = .{ | 1554 | => .{ .rrr = .{ |
| 1418 | .rd = dest_reg.to64(), | 1555 | .rd = dest_reg.toX(), |
| 1419 | .rn = lhs_reg, | 1556 | .rn = lhs_reg, |
| 1420 | .rm = rhs_reg, | 1557 | .rm = rhs_reg, |
| 1421 | } }, | 1558 | } }, |
| ... | @@ -1440,77 +1577,38 @@ fn binOpRegister( | ... | @@ -1440,77 +1577,38 @@ fn binOpRegister( |
| 1440 | return MCValue{ .register = dest_reg }; | 1577 | return MCValue{ .register = dest_reg }; |
| 1441 | } | 1578 | } |
| 1442 | | 1579 | |
| 1443 | /// Don't call this function directly. Use binOp instead. | 1580 | /// Wrapper around allocRegs and addInst tailored for specific Mir |
| 1444 | /// | 1581 | /// instructions which are binary operations acting on a register and |
| 1445 | /// Calling this function signals an intention to generate a Mir | 1582 | /// an immediate |
| 1446 | /// instruction of the form | | |
| 1447 | /// | 1583 | /// |
| 1448 | /// op dest, lhs, #rhs_imm | 1584 | /// Returns the destination register |
| 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. | | |
| 1455 | fn binOpImmediate( | 1585 | fn binOpImmediate( |
| 1456 | self: *Self, | 1586 | self: *Self, |
| 1457 | mir_tag: Mir.Inst.Tag, | 1587 | mir_tag: Mir.Inst.Tag, |
| 1458 | lhs: MCValue, | 1588 | lhs_bind: ReadArg.Bind, |
| 1459 | rhs: MCValue, | 1589 | rhs_immediate: u64, |
| 1460 | lhs_ty: Type, | 1590 | lhs_ty: Type, |
| 1461 | lhs_and_rhs_swapped: bool, | 1591 | lhs_and_rhs_swapped: bool, |
| 1462 | metadata: ?BinOpMetadata, | 1592 | maybe_inst: ?Air.Inst.Index, |
| 1463 | ) !MCValue { | 1593 | ) !MCValue { |
| 1464 | const lhs_is_register = lhs == .register; | 1594 | var lhs_reg: Register = undefined; |
| 1465 | | 1595 | var dest_reg: Register = undefined; |
| 1466 | if (lhs_is_register) assert(lhs.register == registerAlias(lhs.register, lhs_ty.abiSize(self.target.*))); | | |
| 1467 | | 1596 | |
| 1468 | const lhs_lock: ?RegisterLock = if (lhs_is_register) | 1597 | const read_args = [_]ReadArg{ |
| 1469 | self.register_manager.lockReg(lhs.register) | 1598 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 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; | | |
| 1489 | }; | 1599 | }; |
| 1490 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 1600 | const write_args = [_]WriteArg{ |
| 1491 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | 1601 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_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 | }, | | |
| 1511 | }; | 1602 | }; |
| 1512 | | 1603 | const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0}; |
| 1513 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 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 | const mir_data: Mir.Inst.Data = switch (mir_tag) { | 1613 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1516 | .add_immediate, | 1614 | .add_immediate, |
| ... | @@ -1520,7 +1618,7 @@ fn binOpImmediate( | ... | @@ -1520,7 +1618,7 @@ fn binOpImmediate( |
| 1520 | => .{ .rr_imm12_sh = .{ | 1618 | => .{ .rr_imm12_sh = .{ |
| 1521 | .rd = dest_reg, | 1619 | .rd = dest_reg, |
| 1522 | .rn = lhs_reg, | 1620 | .rn = lhs_reg, |
| 1523 | .imm12 = @intCast(u12, rhs.immediate), | 1621 | .imm12 = @intCast(u12, rhs_immediate), |
| 1524 | } }, | 1622 | } }, |
| 1525 | .lsl_immediate, | 1623 | .lsl_immediate, |
| 1526 | .asr_immediate, | 1624 | .asr_immediate, |
| ... | @@ -1528,11 +1626,7 @@ fn binOpImmediate( | ... | @@ -1528,11 +1626,7 @@ fn binOpImmediate( |
| 1528 | => .{ .rr_shift = .{ | 1626 | => .{ .rr_shift = .{ |
| 1529 | .rd = dest_reg, | 1627 | .rd = dest_reg, |
| 1530 | .rn = lhs_reg, | 1628 | .rn = lhs_reg, |
| 1531 | .shift = @intCast(u6, rhs.immediate), | 1629 | .shift = @intCast(u6, rhs_immediate), |
| 1532 | } }, | | |
| 1533 | .cmp_immediate => .{ .r_imm12_sh = .{ | | |
| 1534 | .rn = lhs_reg, | | |
| 1535 | .imm12 = @intCast(u12, rhs.immediate), | | |
| 1536 | } }, | 1630 | } }, |
| 1537 | else => unreachable, | 1631 | else => unreachable, |
| 1538 | }; | 1632 | }; |
| ... | @@ -1545,428 +1639,527 @@ fn binOpImmediate( | ... | @@ -1545,428 +1639,527 @@ fn binOpImmediate( |
| 1545 | return MCValue{ .register = dest_reg }; | 1639 | return MCValue{ .register = dest_reg }; |
| 1546 | } | 1640 | } |
| 1547 | | 1641 | |
| 1548 | const BinOpMetadata = struct { | 1642 | fn addSub( |
| 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( | | |
| 1567 | self: *Self, | 1643 | self: *Self, |
| 1568 | tag: Air.Inst.Tag, | 1644 | tag: Air.Inst.Tag, |
| 1569 | lhs: MCValue, | 1645 | lhs_bind: ReadArg.Bind, |
| 1570 | rhs: MCValue, | 1646 | rhs_bind: ReadArg.Bind, |
| 1571 | lhs_ty: Type, | 1647 | lhs_ty: Type, |
| 1572 | rhs_ty: Type, | 1648 | rhs_ty: Type, |
| 1573 | metadata: ?BinOpMetadata, | 1649 | maybe_inst: ?Air.Inst.Index, |
| 1574 | ) InnerError!MCValue { | 1650 | ) InnerError!MCValue { |
| 1575 | const mod = self.bin_file.options.module.?; | 1651 | const mod = self.bin_file.options.module.?; |
| 1576 | switch (tag) { | 1652 | switch (lhs_ty.zigTypeTag()) { |
| 1577 | .add, | 1653 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1578 | .sub, | 1654 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1579 | .cmp_eq, | 1655 | .Int => { |
| 1580 | => { | 1656 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1581 | switch (lhs_ty.zigTypeTag()) { | 1657 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1582 | .Float => return self.fail("TODO binary operations on floats", .{}), | 1658 | if (int_info.bits <= 64) { |
| 1583 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 1659 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); |
| 1584 | .Int => { | 1660 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| 1585 | assert(lhs_ty.eql(rhs_ty, mod)); | 1661 | |
| 1586 | const int_info = lhs_ty.intInfo(self.target.*); | 1662 | // Only say yes if the operation is |
| 1587 | if (int_info.bits <= 64) { | 1663 | // commutative, i.e. we can swap both of the |
| 1588 | // Only say yes if the operation is | 1664 | // operands |
| 1589 | // commutative, i.e. we can swap both of the | 1665 | const lhs_immediate_ok = switch (tag) { |
| 1590 | // operands | 1666 | .add => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, |
| 1591 | const lhs_immediate_ok = switch (tag) { | 1667 | .sub => false, |
| 1592 | .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), | 1668 | else => unreachable, |
| 1593 | .sub, .cmp_eq => false, | 1669 | }; |
| 1594 | else => unreachable, | 1670 | const rhs_immediate_ok = switch (tag) { |
| 1595 | }; | 1671 | .add, |
| 1596 | const rhs_immediate_ok = switch (tag) { | 1672 | .sub, |
| 1597 | .add, | 1673 | => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, |
| 1598 | .sub, | 1674 | else => unreachable, |
| 1599 | .cmp_eq, | 1675 | }; |
| 1600 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), | | |
| 1601 | else => unreachable, | | |
| 1602 | }; | | |
| 1603 | | 1676 | |
| 1604 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { | 1677 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1605 | .add => .add_shifted_register, | 1678 | .add => .add_shifted_register, |
| 1606 | .sub => .sub_shifted_register, | 1679 | .sub => .sub_shifted_register, |
| 1607 | .cmp_eq => .cmp_shifted_register, | 1680 | else => unreachable, |
| 1608 | else => unreachable, | 1681 | }; |
| 1609 | }; | 1682 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { |
| 1610 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { | 1683 | .add => .add_immediate, |
| 1611 | .add => .add_immediate, | 1684 | .sub => .sub_immediate, |
| 1612 | .sub => .sub_immediate, | 1685 | else => unreachable, |
| 1613 | .cmp_eq => .cmp_immediate, | 1686 | }; |
| 1614 | else => unreachable, | | |
| 1615 | }; | | |
| 1616 | | 1687 | |
| 1617 | if (rhs_immediate_ok) { | 1688 | if (rhs_immediate_ok) { |
| 1618 | return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); | 1689 | return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); |
| 1619 | } else if (lhs_immediate_ok) { | 1690 | } else if (lhs_immediate_ok) { |
| 1620 | // swap lhs and rhs | 1691 | // swap lhs and rhs |
| 1621 | return try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, metadata); | 1692 | return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); |
| 1622 | } else { | 1693 | } else { |
| 1623 | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); | 1694 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1624 | } | 1695 | } |
| 1625 | } else { | 1696 | } else { |
| 1626 | return self.fail("TODO binary operations on int with bits > 64", .{}); | 1697 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1627 | } | | |
| 1628 | }, | | |
| 1629 | else => unreachable, | | |
| 1630 | } | 1698 | } |
| 1631 | }, | 1699 | }, |
| 1632 | .mul => { | 1700 | else => unreachable, |
| 1633 | switch (lhs_ty.zigTypeTag()) { | 1701 | } |
| 1634 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 1702 | } |
| 1635 | .Int => { | 1703 | |
| 1636 | assert(lhs_ty.eql(rhs_ty, mod)); | 1704 | fn mul( |
| 1637 | const int_info = lhs_ty.intInfo(self.target.*); | 1705 | self: *Self, |
| 1638 | if (int_info.bits <= 64) { | 1706 | lhs_bind: ReadArg.Bind, |
| 1639 | // TODO add optimisations for multiplication | 1707 | rhs_bind: ReadArg.Bind, |
| 1640 | // with immediates, for example a * 2 can be | 1708 | lhs_ty: Type, |
| 1641 | // lowered to a << 1 | 1709 | rhs_ty: Type, |
| 1642 | return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata); | 1710 | maybe_inst: ?Air.Inst.Index, |
| 1643 | } else { | 1711 | ) InnerError!MCValue { |
| 1644 | return self.fail("TODO binary operations on int with bits > 64", .{}); | 1712 | const mod = self.bin_file.options.module.?; |
| 1645 | } | 1713 | switch (lhs_ty.zigTypeTag()) { |
| 1646 | }, | 1714 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1647 | else => unreachable, | 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 => { | 1727 | else => unreachable, |
| 1651 | switch (lhs_ty.zigTypeTag()) { | 1728 | } |
| 1652 | .Float => return self.fail("TODO div_float", .{}), | 1729 | } |
| 1653 | .Vector => return self.fail("TODO div_float on vectors", .{}), | 1730 | |
| 1654 | else => unreachable, | 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 => { | 1781 | else => unreachable, |
| 1658 | switch (lhs_ty.zigTypeTag()) { | 1782 | } |
| 1659 | .Float => return self.fail("TODO div on floats", .{}), | 1783 | } |
| 1660 | .Vector => return self.fail("TODO div on vectors", .{}), | 1784 | |
| 1661 | .Int => { | 1785 | fn divFloor( |
| 1662 | assert(lhs_ty.eql(rhs_ty, mod)); | 1786 | self: *Self, |
| 1663 | const int_info = lhs_ty.intInfo(self.target.*); | 1787 | lhs_bind: ReadArg.Bind, |
| 1664 | if (int_info.bits <= 64) { | 1788 | rhs_bind: ReadArg.Bind, |
| 1665 | switch (int_info.signedness) { | 1789 | lhs_ty: Type, |
| 1666 | .signed => { | 1790 | rhs_ty: Type, |
| 1667 | switch (tag) { | 1791 | maybe_inst: ?Air.Inst.Index, |
| 1668 | .div_trunc, .div_exact => { | 1792 | ) InnerError!MCValue { |
| 1669 | // TODO optimize integer division by constants | 1793 | const mod = self.bin_file.options.module.?; |
| 1670 | return try self.binOpRegister(.sdiv, lhs, rhs, lhs_ty, rhs_ty, metadata); | 1794 | switch (lhs_ty.zigTypeTag()) { |
| 1671 | }, | 1795 | .Float => return self.fail("TODO div on floats", .{}), |
| 1672 | .div_floor => return self.fail("TODO div_floor on signed integers", .{}), | 1796 | .Vector => return self.fail("TODO div on vectors", .{}), |
| 1673 | else => unreachable, | 1797 | .Int => { |
| 1674 | } | 1798 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1675 | }, | 1799 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1676 | .unsigned => { | 1800 | if (int_info.bits <= 64) { |
| 1677 | // TODO optimize integer division by constants | 1801 | switch (int_info.signedness) { |
| 1678 | return try self.binOpRegister(.udiv, lhs, rhs, lhs_ty, rhs_ty, metadata); | 1802 | .signed => { |
| 1679 | }, | 1803 | return self.fail("TODO div_floor on signed integers", .{}); |
| 1680 | } | 1804 | }, |
| 1681 | } else { | 1805 | .unsigned => { |
| 1682 | return self.fail("TODO integer division for ints with bits > 64", .{}); | 1806 | // TODO optimize integer division by constants |
| 1683 | } | 1807 | return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1684 | }, | 1808 | }, |
| 1685 | else => unreachable, | 1809 | } |
| | 1810 | } else { |
| | 1811 | return self.fail("TODO integer division for ints with bits > 64", .{}); |
| 1686 | } | 1812 | } |
| 1687 | }, | 1813 | }, |
| 1688 | .rem, .mod => { | 1814 | else => unreachable, |
| 1689 | switch (lhs_ty.zigTypeTag()) { | 1815 | } |
| 1690 | .Float => return self.fail("TODO rem/mod on floats", .{}), | 1816 | } |
| 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 }); | | |
| 1725 | | 1817 | |
| 1726 | break :blk reg; | 1818 | fn divExact( |
| 1727 | }; | 1819 | self: *Self, |
| 1728 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 1820 | lhs_bind: ReadArg.Bind, |
| 1729 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | 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: { | 1852 | fn rem( |
| 1732 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | 1853 | self: *Self, |
| 1733 | break :inst Air.refToIndex(md.rhs).?; | 1854 | lhs_bind: ReadArg.Bind, |
| 1734 | } else null; | 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); | 1862 | const mod = self.bin_file.options.module.?; |
| 1737 | const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); | 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; | 1901 | _ = try self.addInst(.{ |
| 1742 | }; | 1902 | .tag = .msub, |
| 1743 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); | 1903 | .data = .{ .rrrr = .{ |
| 1744 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); | 1904 | .rd = remainder_reg, |
| 1745 | | 1905 | .rn = quotient_reg, |
| 1746 | const dest_regs: [2]Register = blk: { | 1906 | .rm = rhs_reg, |
| 1747 | const raw_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp); | 1907 | .ra = lhs_reg, |
| 1748 | const abi_size = lhs_ty.abiSize(self.target.*); | 1908 | } }, |
| 1749 | break :blk .{ | 1909 | }); |
| 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]; | | |
| 1760 | | 1910 | |
| 1761 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 1911 | return MCValue{ .register = remainder_reg }; |
| 1762 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | 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(.{ | 1920 | fn modulo( |
| 1765 | .tag = switch (int_info.signedness) { | 1921 | self: *Self, |
| 1766 | .signed => .sdiv, | 1922 | lhs_bind: ReadArg.Bind, |
| 1767 | .unsigned => .udiv, | 1923 | rhs_bind: ReadArg.Bind, |
| 1768 | }, | 1924 | lhs_ty: Type, |
| 1769 | .data = .{ .rrr = .{ | 1925 | rhs_ty: Type, |
| 1770 | .rd = quotient_reg, | 1926 | maybe_inst: ?Air.Inst.Index, |
| 1771 | .rn = lhs_reg, | 1927 | ) InnerError!MCValue { |
| 1772 | .rm = rhs_reg, | 1928 | _ = lhs_bind; |
| 1773 | } }, | 1929 | _ = rhs_bind; |
| 1774 | }); | 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(.{ | 1941 | fn wrappingArithmetic( |
| 1777 | .tag = .msub, | 1942 | self: *Self, |
| 1778 | .data = .{ .rrrr = .{ | 1943 | tag: Air.Inst.Tag, |
| 1779 | .rd = remainder_reg, | 1944 | lhs_bind: ReadArg.Bind, |
| 1780 | .rn = quotient_reg, | 1945 | rhs_bind: ReadArg.Bind, |
| 1781 | .rm = rhs_reg, | 1946 | lhs_ty: Type, |
| 1782 | .ra = lhs_reg, | 1947 | rhs_ty: Type, |
| 1783 | } }, | 1948 | maybe_inst: ?Air.Inst.Index, |
| 1784 | }); | 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 }; | 1963 | // Truncate if necessary |
| 1787 | } | 1964 | const result_reg = result.register; |
| 1788 | } else { | 1965 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 1789 | return self.fail("TODO rem/mod for integers with bits > 64", .{}); | 1966 | return result; |
| 1790 | } | 1967 | } else { |
| 1791 | }, | 1968 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 1792 | else => unreachable, | | |
| 1793 | } | 1969 | } |
| 1794 | }, | 1970 | }, |
| 1795 | .addwrap, | 1971 | else => unreachable, |
| 1796 | .subwrap, | 1972 | } |
| 1797 | .mulwrap, | 1973 | } |
| 1798 | => { | | |
| 1799 | const base_tag: Air.Inst.Tag = switch (tag) { | | |
| 1800 | .addwrap => .add, | | |
| 1801 | .subwrap => .sub, | | |
| 1802 | .mulwrap => .mul, | | |
| 1803 | else => unreachable, | | |
| 1804 | }; | | |
| 1805 | | 1974 | |
| 1806 | // Generate an add/sub/mul | 1975 | fn bitwise( |
| 1807 | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | 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 | 1999 | return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); |
| 1810 | switch (lhs_ty.zigTypeTag()) { | 2000 | } else { |
| 1811 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 2001 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 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, | | |
| 1823 | } | 2002 | } |
| 1824 | }, | 2003 | }, |
| 1825 | .bit_and, | 2004 | else => unreachable, |
| 1826 | .bit_or, | 2005 | } |
| 1827 | .xor, | 2006 | } |
| 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 | }; | | |
| 1842 | | 2007 | |
| 1843 | return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | 2008 | fn shiftExact( |
| 1844 | } else { | 2009 | self: *Self, |
| 1845 | return self.fail("TODO binary operations on int with bits > 64", .{}); | 2010 | tag: Air.Inst.Tag, |
| 1846 | } | 2011 | lhs_bind: ReadArg.Bind, |
| 1847 | }, | 2012 | rhs_bind: ReadArg.Bind, |
| 1848 | else => unreachable, | 2013 | lhs_ty: Type, |
| 1849 | } | 2014 | rhs_ty: Type, |
| 1850 | }, | 2015 | maybe_inst: ?Air.Inst.Index, |
| 1851 | .shl_exact, | 2016 | ) InnerError!MCValue { |
| 1852 | .shr_exact, | 2017 | _ = rhs_ty; |
| 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; | | |
| 1860 | | 2018 | |
| 1861 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { | 2019 | switch (lhs_ty.zigTypeTag()) { |
| 1862 | .shl_exact => .lsl_register, | 2020 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1863 | .shr_exact => switch (int_info.signedness) { | 2021 | .Int => { |
| 1864 | .signed => Mir.Inst.Tag.asr_register, | 2022 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1865 | .unsigned => Mir.Inst.Tag.lsr_register, | 2023 | if (int_info.bits <= 64) { |
| 1866 | }, | 2024 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| 1867 | else => unreachable, | 2025 | |
| 1868 | }; | 2026 | const mir_tag_register: Mir.Inst.Tag = switch (tag) { |
| 1869 | const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { | 2027 | .shl_exact => .lsl_register, |
| 1870 | .shl_exact => .lsl_immediate, | 2028 | .shr_exact => switch (int_info.signedness) { |
| 1871 | .shr_exact => switch (int_info.signedness) { | 2029 | .signed => Mir.Inst.Tag.asr_register, |
| 1872 | .signed => Mir.Inst.Tag.asr_immediate, | 2030 | .unsigned => Mir.Inst.Tag.lsr_register, |
| 1873 | .unsigned => Mir.Inst.Tag.lsr_immediate, | 2031 | }, |
| 1874 | }, | 2032 | else => unreachable, |
| 1875 | else => unreachable, | 2033 | }; |
| 1876 | }; | 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) { | 2043 | if (rhs_immediate) |imm| { |
| 1879 | return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); | 2044 | return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst); |
| 1880 | } else { | 2045 | } else { |
| 1881 | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); | 2046 | // We intentionally pass lhs_ty here in order to |
| 1882 | } | 2047 | // prevent using the 32-bit register alias when |
| 1883 | } else { | 2048 | // lhs_ty is > 32 bits. |
| 1884 | return self.fail("TODO binary operations on int with bits > 64", .{}); | 2049 | return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst); |
| 1885 | } | 2050 | } |
| 1886 | }, | 2051 | } else { |
| 1887 | else => unreachable, | 2052 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1888 | } | 2053 | } |
| 1889 | }, | 2054 | }, |
| 1890 | .shl, | 2055 | else => unreachable, |
| 1891 | .shr, | 2056 | } |
| 1892 | => { | 2057 | } |
| 1893 | const base_tag: Air.Inst.Tag = switch (tag) { | | |
| 1894 | .shl => .shl_exact, | | |
| 1895 | .shr => .shr_exact, | | |
| 1896 | else => unreachable, | | |
| 1897 | }; | | |
| 1898 | | 2058 | |
| 1899 | // Generate a shl_exact/shr_exact | 2059 | fn shiftNormal( |
| 1900 | const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); | 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 | 2080 | // Truncate if necessary |
| 1903 | switch (tag) { | 2081 | switch (tag) { |
| 1904 | .shr => return result, | 2082 | .shr => return result, |
| 1905 | .shl => switch (lhs_ty.zigTypeTag()) { | 2083 | .shl => { |
| 1906 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | 2084 | const result_reg = result.register; |
| 1907 | .Int => { | 2085 | try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); |
| 1908 | const int_info = lhs_ty.intInfo(self.target.*); | 2086 | return result; |
| 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 | } | | |
| 1916 | }, | 2087 | }, |
| 1917 | else => unreachable, | 2088 | else => unreachable, |
| 1918 | }, | 2089 | } |
| 1919 | else => unreachable, | 2090 | } else { |
| | 2091 | return self.fail("TODO binary operations on integers > u64/i64", .{}); |
| 1920 | } | 2092 | } |
| 1921 | }, | 2093 | }, |
| 1922 | .bool_and, | 2094 | else => unreachable, |
| 1923 | .bool_or, | 2095 | } |
| 1924 | => { | 2096 | } |
| 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 | }; | | |
| 1935 | | 2097 | |
| 1936 | return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); | 2098 | fn booleanOp( |
| 1937 | }, | 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 | else => unreachable, | 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, | 2120 | else => unreachable, |
| 1942 | .ptr_sub, | 2121 | } |
| 1943 | => { | 2122 | } |
| 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.*); | | |
| 1952 | | 2123 | |
| 1953 | if (elem_size == 1) { | 2124 | fn ptrArithmetic( |
| 1954 | const base_tag: Mir.Inst.Tag = switch (tag) { | 2125 | self: *Self, |
| 1955 | .ptr_add => .add_shifted_register, | 2126 | tag: Air.Inst.Tag, |
| 1956 | .ptr_sub => .sub_shifted_register, | 2127 | lhs_bind: ReadArg.Bind, |
| 1957 | else => unreachable, | 2128 | rhs_bind: ReadArg.Bind, |
| 1958 | }; | 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); | 2138 | const ptr_ty = lhs_ty; |
| 1961 | } else { | 2139 | const elem_ty = switch (ptr_ty.ptrSize()) { |
| 1962 | // convert the offset into a byte offset by | 2140 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| 1963 | // multiplying it with elem_size | 2141 | else => ptr_ty.childType(), |
| 1964 | const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); | 2142 | }; |
| 1965 | const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); | 2143 | const elem_size = elem_ty.abiSize(self.target.*); |
| 1966 | return addr; | 2144 | |
| 1967 | } | 2145 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1968 | }, | 2146 | .ptr_add => .add, |
| | 2147 | .ptr_sub => .sub, |
| 1969 | else => unreachable, | 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 | else => unreachable, | 2165 | else => unreachable, |
| ... | @@ -1975,38 +2168,66 @@ fn binOp( | ... | @@ -1975,38 +2168,66 @@ fn binOp( |
| 1975 | | 2168 | |
| 1976 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | 2169 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 1977 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 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 | const lhs_ty = self.air.typeOf(bin_op.lhs); | 2171 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1981 | const rhs_ty = self.air.typeOf(bin_op.rhs); | 2172 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1982 | | 2173 | |
| 1983 | const result: MCValue = if (self.liveness.isUnused(inst)) | 2174 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1984 | .dead | 2175 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; |
| 1985 | else | 2176 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 1986 | try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ | 2177 | |
| 1987 | .inst = inst, | 2178 | break :result switch (tag) { |
| 1988 | .lhs = bin_op.lhs, | 2179 | .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 1989 | .rhs = bin_op.rhs, | 2180 | .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), |
| 1990 | }); | 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 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 2216 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1992 | } | 2217 | } |
| 1993 | | 2218 | |
| 1994 | fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | 2219 | fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 1995 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 2220 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1996 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | 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 | const lhs_ty = self.air.typeOf(bin_op.lhs); | 2222 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 2000 | const rhs_ty = self.air.typeOf(bin_op.rhs); | 2223 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 2001 | | 2224 | |
| 2002 | const result: MCValue = if (self.liveness.isUnused(inst)) | 2225 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2003 | .dead | 2226 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; |
| 2004 | else | 2227 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 2005 | try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ | 2228 | |
| 2006 | .inst = inst, | 2229 | break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); |
| 2007 | .lhs = bin_op.lhs, | 2230 | }; |
| 2008 | .rhs = bin_op.rhs, | | |
| 2009 | }); | | |
| 2010 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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,8 +2254,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2033 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 2254 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2034 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2255 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2035 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2256 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2036 | const lhs = try self.resolveInst(extra.lhs); | 2257 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2037 | const rhs = try self.resolveInst(extra.rhs); | 2258 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2038 | const lhs_ty = self.air.typeOf(extra.lhs); | 2259 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2039 | const rhs_ty = self.air.typeOf(extra.rhs); | 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,7 +2272,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2051 | const int_info = lhs_ty.intInfo(self.target.*); | 2272 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2052 | switch (int_info.bits) { | 2273 | switch (int_info.bits) { |
| 2053 | 1...31, 33...63 => { | 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 | try self.spillCompareFlagsIfOccupied(); | 2277 | try self.spillCompareFlagsIfOccupied(); |
| 2057 | self.condition_flags_inst = null; | 2278 | self.condition_flags_inst = null; |
| ... | @@ -2061,13 +2282,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2061,13 +2282,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2061 | .sub_with_overflow => .sub, | 2282 | .sub_with_overflow => .sub, |
| 2062 | else => unreachable, | 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 | const dest_reg = dest.register; | 2286 | const dest_reg = dest.register; |
| 2066 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); | 2287 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2067 | defer self.register_manager.unlockReg(dest_reg_lock); | 2288 | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2068 | | 2289 | |
| 2069 | const raw_truncated_reg = try self.register_manager.allocReg(null, gp); | 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 | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); | 2292 | const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); |
| 2072 | defer self.register_manager.unlockReg(truncated_reg_lock); | 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,7 +2296,15 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2075 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); | 2296 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2076 | | 2297 | |
| 2077 | // cmp dest, truncated | 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 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); | 2309 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2081 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); | 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,18 +2312,21 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2083 | break :result MCValue{ .stack_offset = stack_offset }; | 2312 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2084 | }, | 2313 | }, |
| 2085 | 32, 64 => { | 2314 | 32, 64 => { |
| | 2315 | const lhs_immediate = try lhs_bind.resolveToImmediate(self); |
| | 2316 | const rhs_immediate = try rhs_bind.resolveToImmediate(self); |
| | 2317 | |
| 2086 | // Only say yes if the operation is | 2318 | // Only say yes if the operation is |
| 2087 | // commutative, i.e. we can swap both of the | 2319 | // commutative, i.e. we can swap both of the |
| 2088 | // operands | 2320 | // operands |
| 2089 | const lhs_immediate_ok = switch (tag) { | 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 | .sub_with_overflow => false, | 2323 | .sub_with_overflow => false, |
| 2092 | else => unreachable, | 2324 | else => unreachable, |
| 2093 | }; | 2325 | }; |
| 2094 | const rhs_immediate_ok = switch (tag) { | 2326 | const rhs_immediate_ok = switch (tag) { |
| 2095 | .add_with_overflow, | 2327 | .add_with_overflow, |
| 2096 | .sub_with_overflow, | 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 | else => unreachable, | 2330 | else => unreachable, |
| 2099 | }; | 2331 | }; |
| 2100 | | 2332 | |
| ... | @@ -2114,12 +2346,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2114,12 +2346,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2114 | | 2346 | |
| 2115 | const dest = blk: { | 2347 | const dest = blk: { |
| 2116 | if (rhs_immediate_ok) { | 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 | } else if (lhs_immediate_ok) { | 2350 | } else if (lhs_immediate_ok) { |
| 2119 | // swap lhs and rhs | 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 | } else { | 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,8 +2382,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2150 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2382 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2151 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); | 2383 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2152 | const result: MCValue = result: { | 2384 | const result: MCValue = result: { |
| 2153 | const lhs = try self.resolveInst(extra.lhs); | 2385 | const mod = self.bin_file.options.module.?; |
| 2154 | const rhs = try self.resolveInst(extra.rhs); | 2386 | |
| | 2387 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| | 2388 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2155 | const lhs_ty = self.air.typeOf(extra.lhs); | 2389 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2156 | const rhs_ty = self.air.typeOf(extra.rhs); | 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,20 +2397,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2163 | switch (lhs_ty.zigTypeTag()) { | 2397 | switch (lhs_ty.zigTypeTag()) { |
| 2164 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), | 2398 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| 2165 | .Int => { | 2399 | .Int => { |
| | 2400 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 2166 | const int_info = lhs_ty.intInfo(self.target.*); | 2401 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2167 | | | |
| 2168 | if (int_info.bits <= 32) { | 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 | try self.spillCompareFlagsIfOccupied(); | 2405 | try self.spillCompareFlagsIfOccupied(); |
| 2172 | self.condition_flags_inst = null; | | |
| 2173 | | 2406 | |
| 2174 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { | 2407 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| 2175 | .signed => .smull, | 2408 | .signed => .smull, |
| 2176 | .unsigned => .umull, | 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 | const dest_reg = dest.register; | 2413 | const dest_reg = dest.register; |
| 2181 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); | 2414 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2182 | defer self.register_manager.unlockReg(dest_reg_lock); | 2415 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | @@ -2186,8 +2419,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2186,8 +2419,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2186 | defer self.register_manager.unlockReg(truncated_reg_lock); | 2419 | defer self.register_manager.unlockReg(truncated_reg_lock); |
| 2187 | | 2420 | |
| 2188 | try self.truncRegister( | 2421 | try self.truncRegister( |
| 2189 | dest_reg.to32(), | 2422 | dest_reg.toW(), |
| 2190 | truncated_reg.to32(), | 2423 | truncated_reg.toW(), |
| 2191 | int_info.signedness, | 2424 | int_info.signedness, |
| 2192 | int_info.bits, | 2425 | int_info.bits, |
| 2193 | ); | 2426 | ); |
| ... | @@ -2197,8 +2430,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2197,8 +2430,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2197 | _ = try self.addInst(.{ | 2430 | _ = try self.addInst(.{ |
| 2198 | .tag = .cmp_extended_register, | 2431 | .tag = .cmp_extended_register, |
| 2199 | .data = .{ .rr_extend_shift = .{ | 2432 | .data = .{ .rr_extend_shift = .{ |
| 2200 | .rn = dest_reg.to64(), | 2433 | .rn = dest_reg.toX(), |
| 2201 | .rm = truncated_reg.to32(), | 2434 | .rm = truncated_reg.toW(), |
| 2202 | .ext_type = .sxtw, | 2435 | .ext_type = .sxtw, |
| 2203 | .imm3 = 0, | 2436 | .imm3 = 0, |
| 2204 | } }, | 2437 | } }, |
| ... | @@ -2208,8 +2441,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2208,8 +2441,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2208 | _ = try self.addInst(.{ | 2441 | _ = try self.addInst(.{ |
| 2209 | .tag = .cmp_extended_register, | 2442 | .tag = .cmp_extended_register, |
| 2210 | .data = .{ .rr_extend_shift = .{ | 2443 | .data = .{ .rr_extend_shift = .{ |
| 2211 | .rn = dest_reg.to64(), | 2444 | .rn = dest_reg.toX(), |
| 2212 | .rm = truncated_reg.to32(), | 2445 | .rm = truncated_reg.toW(), |
| 2213 | .ext_type = .uxtw, | 2446 | .ext_type = .uxtw, |
| 2214 | .imm3 = 0, | 2447 | .imm3 = 0, |
| 2215 | } }, | 2448 | } }, |
| ... | @@ -2222,53 +2455,30 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2222,53 +2455,30 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2222 | | 2455 | |
| 2223 | break :result MCValue{ .stack_offset = stack_offset }; | 2456 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2224 | } else if (int_info.bits <= 64) { | 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 | try self.spillCompareFlagsIfOccupied(); | 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); | 2462 | var lhs_reg: Register = undefined; |
| 2263 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | 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: { | 2468 | const read_args = [_]ReadArg{ |
| 2266 | const raw_reg = try self.register_manager.allocReg(null, gp); | 2469 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 2267 | const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); | 2470 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 2268 | break :blk reg; | | |
| 2269 | }; | 2471 | }; |
| 2270 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); | 2472 | const write_args = [_]WriteArg{ |
| 2271 | defer self.register_manager.unlockReg(dest_reg_lock); | 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 | switch (int_info.signedness) { | 2483 | switch (int_info.signedness) { |
| 2274 | .signed => { | 2484 | .signed => { |
| ... | @@ -2282,10 +2492,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -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 | // smulh dest_high, lhs, rhs | 2495 | // smulh dest_high, lhs, rhs |
| 2290 | _ = try self.addInst(.{ | 2496 | _ = try self.addInst(.{ |
| 2291 | .tag = .smulh, | 2497 | .tag = .smulh, |
| ... | @@ -2332,10 +2538,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2332,10 +2538,6 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2332 | } | 2538 | } |
| 2333 | }, | 2539 | }, |
| 2334 | .unsigned => { | 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 | // umulh dest_high, lhs, rhs | 2541 | // umulh dest_high, lhs, rhs |
| 2340 | _ = try self.addInst(.{ | 2542 | _ = try self.addInst(.{ |
| 2341 | .tag = .umulh, | 2543 | .tag = .umulh, |
| ... | @@ -2356,14 +2558,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2356,14 +2558,13 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2356 | } }, | 2558 | } }, |
| 2357 | }); | 2559 | }); |
| 2358 | | 2560 | |
| 2359 | _ = try self.binOp( | 2561 | _ = try self.addInst(.{ |
| 2360 | .cmp_eq, | 2562 | .tag = .cmp_immediate, |
| 2361 | .{ .register = dest_high_reg }, | 2563 | .data = .{ .r_imm12_sh = .{ |
| 2362 | .{ .immediate = 0 }, | 2564 | .rn = dest_high_reg, |
| 2363 | Type.usize, | 2565 | .imm12 = 0, |
| 2364 | Type.usize, | 2566 | } }, |
| 2365 | null, | 2567 | }); |
| 2366 | ); | | |
| 2367 | | 2568 | |
| 2368 | if (int_info.bits < 64) { | 2569 | if (int_info.bits < 64) { |
| 2369 | // lsr dest_high, dest, #shift | 2570 | // lsr dest_high, dest, #shift |
| ... | @@ -2376,22 +2577,17 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2376,22 +2577,17 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2376 | } }, | 2577 | } }, |
| 2377 | }); | 2578 | }); |
| 2378 | | 2579 | |
| 2379 | _ = try self.binOp( | 2580 | _ = try self.addInst(.{ |
| 2380 | .cmp_eq, | 2581 | .tag = .cmp_immediate, |
| 2381 | .{ .register = dest_high_reg }, | 2582 | .data = .{ .r_imm12_sh = .{ |
| 2382 | .{ .immediate = 0 }, | 2583 | .rn = dest_high_reg, |
| 2383 | Type.usize, | 2584 | .imm12 = 0, |
| 2384 | Type.usize, | 2585 | } }, |
| 2385 | null, | 2586 | }); |
| 2386 | ); | | |
| 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 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); | 2591 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2396 | | 2592 | |
| 2397 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); | 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,8 +2607,8 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2411 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2607 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2412 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); | 2608 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2413 | const result: MCValue = result: { | 2609 | const result: MCValue = result: { |
| 2414 | const lhs = try self.resolveInst(extra.lhs); | 2610 | const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2415 | const rhs = try self.resolveInst(extra.rhs); | 2611 | const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2416 | const lhs_ty = self.air.typeOf(extra.lhs); | 2612 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 2417 | const rhs_ty = self.air.typeOf(extra.rhs); | 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,35 +2622,112 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2426 | .Int => { | 2622 | .Int => { |
| 2427 | const int_info = lhs_ty.intInfo(self.target.*); | 2623 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2428 | if (int_info.bits <= 64) { | 2624 | if (int_info.bits <= 64) { |
| 2429 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); | 2625 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 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); | | |
| 2436 | | 2626 | |
| 2437 | try self.spillCompareFlagsIfOccupied(); | 2627 | try self.spillCompareFlagsIfOccupied(); |
| 2438 | self.condition_flags_inst = null; | | |
| 2439 | | 2628 | |
| 2440 | // lsl dest, lhs, rhs | 2629 | var lhs_reg: Register = undefined; |
| 2441 | const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); | 2630 | var rhs_reg: Register = undefined; |
| 2442 | const dest_reg = dest.register; | 2631 | var dest_reg: Register = undefined; |
| 2443 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); | 2632 | var reconstructed_reg: Register = undefined; |
| 2444 | defer self.register_manager.unlockReg(dest_reg_lock); | 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 | 2698 | try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); |
| 2447 | const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null); | 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 | // cmp lhs, reconstructed | 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 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); | 2726 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2454 | | 2727 | |
| 2455 | break :result MCValue{ .stack_offset = stack_offset }; | 2728 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2456 | } else { | 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 | else => unreachable, | 2733 | else => unreachable, |
| ... | @@ -2712,63 +2985,59 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2712,63 +2985,59 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2712 | } | 2985 | } |
| 2713 | | 2986 | |
| 2714 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | 2987 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2715 | const is_volatile = false; // TODO | | |
| 2716 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2988 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2717 | | 2989 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2718 | if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 2990 | const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { |
| 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 | | | |
| 2730 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 2991 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2731 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); | 2992 | const ptr_ty = 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); | | |
| 2738 | | 2993 | |
| | 2994 | const slice_mcv = try self.resolveInst(bin_op.lhs); |
| 2739 | const base_mcv = slicePtr(slice_mcv); | 2995 | const base_mcv = slicePtr(slice_mcv); |
| 2740 | | 2996 | |
| 2741 | switch (elem_size) { | 2997 | const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; |
| 2742 | else => { | 2998 | const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 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); | | |
| 2753 | | 2999 | |
| 2754 | break :result dest; | 3000 | break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); |
| 2755 | }, | | |
| 2756 | } | | |
| 2757 | }; | 3001 | }; |
| 2758 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { | 3027 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2762 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 3028 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2763 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 3029 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2764 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3030 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2765 | const slice_mcv = try self.resolveInst(extra.lhs); | 3031 | const slice_mcv = try self.resolveInst(extra.lhs); |
| 2766 | const index_mcv = try self.resolveInst(extra.rhs); | | |
| 2767 | const base_mcv = slicePtr(slice_mcv); | 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 | const slice_ty = self.air.typeOf(extra.lhs); | 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 | break :result addr; | 3041 | break :result addr; |
| 2773 | }; | 3042 | }; |
| 2774 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 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,12 +3060,13 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2791 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 3060 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2792 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 3061 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2793 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3062 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2794 | const ptr_mcv = try self.resolveInst(extra.lhs); | 3063 | const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs }; |
| 2795 | const index_mcv = try self.resolveInst(extra.rhs); | 3064 | const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; |
| 2796 | | 3065 | |
| 2797 | const ptr_ty = self.air.typeOf(extra.lhs); | 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 | break :result addr; | 3070 | break :result addr; |
| 2801 | }; | 3071 | }; |
| 2802 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 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,7 +3123,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 2853 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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 | if (!self.liveness.operandDies(inst, op_index)) | 3133 | if (!self.liveness.operandDies(inst, op_index)) |
| 2858 | return false; | 3134 | return false; |
| 2859 | | 3135 | |
| ... | @@ -2912,7 +3188,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -2912,7 +3188,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2912 | .stack_offset => |off| { | 3188 | .stack_offset => |off| { |
| 2913 | if (elem_size <= 8) { | 3189 | if (elem_size <= 8) { |
| 2914 | const raw_tmp_reg = try self.register_manager.allocReg(null, gp); | 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 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); | 3192 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 2917 | defer self.register_manager.unlockReg(tmp_reg_lock); | 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,11 +3326,11 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3050 | if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { | 3326 | if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { |
| 3051 | // The MCValue that holds the pointer can be re-used as the value. | 3327 | // The MCValue that holds the pointer can be re-used as the value. |
| 3052 | break :blk switch (ptr) { | 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 | else => ptr, | 3330 | else => ptr, |
| 3055 | }; | 3331 | }; |
| 3056 | } else { | 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 | try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); | 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,7 +3412,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3136 | else => { | 3412 | else => { |
| 3137 | if (abi_size <= 8) { | 3413 | if (abi_size <= 8) { |
| 3138 | const raw_tmp_reg = try self.register_manager.allocReg(null, gp); | 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 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); | 3416 | const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 3141 | defer self.register_manager.unlockReg(tmp_reg_lock); | 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,26 +3505,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3229 | break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; | 3505 | break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; |
| 3230 | }, | 3506 | }, |
| 3231 | else => { | 3507 | else => { |
| 3232 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ | 3508 | const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; |
| 3233 | .immediate = struct_field_offset, | 3509 | const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .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 | ); | | |
| 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,7 +3555,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3295 | } else { | 3555 | } else { |
| 3296 | // Copy to new register | 3556 | // Copy to new register |
| 3297 | const raw_dest_reg = try self.register_manager.allocReg(null, gp); | 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 | try self.genSetReg(struct_field_ty, dest_reg, field); | 3559 | try self.genSetReg(struct_field_ty, dest_reg, field); |
| 3300 | | 3560 | |
| 3301 | break :result MCValue{ .register = dest_reg }; | 3561 | break :result MCValue{ .register = dest_reg }; |
| ... | @@ -3330,7 +3590,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3330,7 +3590,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 3330 | return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); | 3590 | return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); |
| 3331 | }; | 3591 | }; |
| 3332 | const abi_align = ty.abiAlignment(self.target.*); | 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 | try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); | 3594 | try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 3335 | | 3595 | |
| 3336 | break :blk MCValue{ .stack_offset = stack_offset }; | 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,11 +3668,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3408 | const ret_ty = fn_ty.fnReturnType(); | 3668 | const ret_ty = fn_ty.fnReturnType(); |
| 3409 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 3669 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 3410 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); | 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(); | 3673 | const ret_ptr_reg = self.registerAlias(.x0, Type.usize); |
| 3414 | const ptr_bytes = @divExact(ptr_bits, 8); | | |
| 3415 | const ret_ptr_reg = registerAlias(.x0, ptr_bytes); | | |
| 3416 | | 3674 | |
| 3417 | var ptr_ty_payload: Type.Payload.ElemType = .{ | 3675 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 3418 | .base = .{ .tag = .single_mut_pointer }, | 3676 | .base = .{ .tag = .single_mut_pointer }, |
| ... | @@ -3636,14 +3894,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3636,14 +3894,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3636 | const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 3894 | const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 3637 | const abi_align = ret_ty.abiAlignment(self.target.*); | 3895 | const abi_align = ret_ty.abiAlignment(self.target.*); |
| 3638 | | 3896 | |
| 3639 | // This is essentially allocMem without the | 3897 | const offset = try self.allocMem(abi_size, abi_align, null); |
| 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); | | |
| 3647 | | 3898 | |
| 3648 | const tmp_mcv = MCValue{ .stack_offset = offset }; | 3899 | const tmp_mcv = MCValue{ .stack_offset = offset }; |
| 3649 | try self.load(tmp_mcv, ptr, ptr_ty); | 3900 | try self.load(tmp_mcv, ptr, ptr_ty); |
| ... | @@ -3660,54 +3911,100 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3660,54 +3911,100 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3660 | | 3911 | |
| 3661 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | 3912 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3662 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 3913 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3663 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3914 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 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 | }; | | |
| 3690 | | 3915 | |
| 3691 | const int_info = int_ty.intInfo(self.target.*); | 3916 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 3692 | if (int_info.bits <= 64) { | 3917 | break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op); |
| 3693 | _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ | 3918 | }; |
| 3694 | .inst = inst, | 3919 | |
| 3695 | .lhs = bin_op.lhs, | 3920 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3696 | .rhs = bin_op.rhs, | 3921 | } |
| 3697 | }); | 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(); | 3952 | const int_info = int_ty.intInfo(self.target.*); |
| 3700 | self.condition_flags_inst = inst; | 3953 | if (int_info.bits <= 64) { |
| | 3954 | try self.spillCompareFlagsIfOccupied(); |
| 3701 | | 3955 | |
| 3702 | break :result switch (int_info.signedness) { | 3956 | var lhs_reg: Register = undefined; |
| 3703 | .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) }, | 3957 | var rhs_reg: Register = undefined; |
| 3704 | .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) }, | 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 | } else { | 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 | }; | 4000 | |
| 3710 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { | 4010 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -3952,15 +4249,13 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { | ... | @@ -3952,15 +4249,13 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 3952 | | 4249 | |
| 3953 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 4250 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3954 | const error_type = ty.errorUnionSet(); | 4251 | const error_type = ty.errorUnionSet(); |
| 3955 | const error_int_type = Type.initTag(.u16); | | |
| 3956 | | 4252 | |
| 3957 | if (error_type.errorSetIsEmpty()) { | 4253 | if (error_type.errorSetIsEmpty()) { |
| 3958 | return MCValue{ .immediate = 0 }; // always false | 4254 | return MCValue{ .immediate = 0 }; // always false |
| 3959 | } | 4255 | } |
| 3960 | | 4256 | |
| 3961 | const error_mcv = try self.errUnionErr(operand, ty); | 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); | 4258 | return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); |
| 3963 | return MCValue{ .condition_flags = .hi }; | | |
| 3964 | } | 4259 | } |
| 3965 | | 4260 | |
| 3966 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 4261 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| ... | @@ -3991,15 +4286,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3991,15 +4286,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3991 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4286 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 3992 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4287 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3993 | const operand_ptr = try self.resolveInst(un_op); | 4288 | const operand_ptr = try self.resolveInst(un_op); |
| 3994 | const operand: MCValue = blk: { | 4289 | const ptr_ty = self.air.typeOf(un_op); |
| 3995 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4290 | const elem_ty = ptr_ty.elemType(); |
| 3996 | // The MCValue that holds the pointer can be re-used as the value. | 4291 | |
| 3997 | break :blk operand_ptr; | 4292 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 3998 | } else { | 4293 | try self.load(operand, operand_ptr, ptr_ty); |
| 3999 | break :blk try self.allocRegOrMem(inst, true); | 4294 | |
| 4000 | } | | |
| 4001 | }; | | |
| 4002 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | | |
| 4003 | break :result try self.isNull(operand); | 4295 | break :result try self.isNull(operand); |
| 4004 | }; | 4296 | }; |
| 4005 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4297 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| ... | @@ -4018,15 +4310,12 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4018,15 +4310,12 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4018 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4310 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4019 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4311 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4020 | const operand_ptr = try self.resolveInst(un_op); | 4312 | const operand_ptr = try self.resolveInst(un_op); |
| 4021 | const operand: MCValue = blk: { | 4313 | const ptr_ty = self.air.typeOf(un_op); |
| 4022 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4314 | const elem_ty = ptr_ty.elemType(); |
| 4023 | // The MCValue that holds the pointer can be re-used as the value. | 4315 | |
| 4024 | break :blk operand_ptr; | 4316 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4025 | } else { | 4317 | try self.load(operand, operand_ptr, ptr_ty); |
| 4026 | break :blk try self.allocRegOrMem(inst, true); | 4318 | |
| 4027 | } | | |
| 4028 | }; | | |
| 4029 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | | |
| 4030 | break :result try self.isNonNull(operand); | 4319 | break :result try self.isNonNull(operand); |
| 4031 | }; | 4320 | }; |
| 4032 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4321 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| ... | @@ -4047,16 +4336,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4047,16 +4336,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4047 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4336 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4048 | const operand_ptr = try self.resolveInst(un_op); | 4337 | const operand_ptr = try self.resolveInst(un_op); |
| 4049 | const ptr_ty = self.air.typeOf(un_op); | 4338 | const ptr_ty = self.air.typeOf(un_op); |
| 4050 | const operand: MCValue = blk: { | 4339 | const elem_ty = ptr_ty.elemType(); |
| 4051 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4340 | |
| 4052 | // The MCValue that holds the pointer can be re-used as the value. | 4341 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4053 | break :blk operand_ptr; | 4342 | try self.load(operand, operand_ptr, ptr_ty); |
| 4054 | } else { | 4343 | |
| 4055 | break :blk try self.allocRegOrMem(inst, true); | 4344 | break :result try self.isErr(elem_ty, operand); |
| 4056 | } | | |
| 4057 | }; | | |
| 4058 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | | |
| 4059 | break :result try self.isErr(ptr_ty.elemType(), operand); | | |
| 4060 | }; | 4345 | }; |
| 4061 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,16 +4361,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4076 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4361 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4077 | const operand_ptr = try self.resolveInst(un_op); | 4362 | const operand_ptr = try self.resolveInst(un_op); |
| 4078 | const ptr_ty = self.air.typeOf(un_op); | 4363 | const ptr_ty = self.air.typeOf(un_op); |
| 4079 | const operand: MCValue = blk: { | 4364 | const elem_ty = ptr_ty.elemType(); |
| 4080 | if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { | 4365 | |
| 4081 | // The MCValue that holds the pointer can be re-used as the value. | 4366 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4082 | break :blk operand_ptr; | 4367 | try self.load(operand, operand_ptr, ptr_ty); |
| 4083 | } else { | 4368 | |
| 4084 | break :blk try self.allocRegOrMem(inst, true); | 4369 | break :result try self.isNonErr(elem_ty, operand); |
| 4085 | } | | |
| 4086 | }; | | |
| 4087 | try self.load(operand, operand_ptr, self.air.typeOf(un_op)); | | |
| 4088 | break :result try self.isNonErr(ptr_ty.elemType(), operand); | | |
| 4089 | }; | 4370 | }; |
| 4090 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +4459,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 4178 | .none, .dead, .unreach => unreachable, | 4459 | .none, .dead, .unreach => unreachable, |
| 4179 | .register, .stack_offset, .memory => operand_mcv, | 4460 | .register, .stack_offset, .memory => operand_mcv, |
| 4180 | .immediate, .stack_argument_offset, .condition_flags => blk: { | 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 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); | 4463 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 4183 | break :blk new_mcv; | 4464 | break :blk new_mcv; |
| 4184 | }, | 4465 | }, |
| ... | @@ -4376,7 +4657,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -4376,7 +4657,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4376 | 4, 8 => .str_stack, | 4657 | 4, 8 => .str_stack, |
| 4377 | else => unreachable, // unexpected abi size | 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 | _ = try self.addInst(.{ | 4662 | _ = try self.addInst(.{ |
| 4382 | .tag = tag, | 4663 | .tag = tag, |
| ... | @@ -4399,10 +4680,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -4399,10 +4680,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4399 | const overflow_bit_ty = ty.structFieldType(1); | 4680 | const overflow_bit_ty = ty.structFieldType(1); |
| 4400 | const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); | 4681 | const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); |
| 4401 | const raw_cond_reg = try self.register_manager.allocReg(null, gp); | 4682 | const raw_cond_reg = try self.register_manager.allocReg(null, gp); |
| 4402 | const cond_reg = registerAlias( | 4683 | const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty); |
| 4403 | raw_cond_reg, | | |
| 4404 | @intCast(u32, overflow_bit_ty.abiSize(self.target.*)), | | |
| 4405 | ); | | |
| 4406 | | 4684 | |
| 4407 | _ = try self.addInst(.{ | 4685 | _ = try self.addInst(.{ |
| 4408 | .tag = .cset, | 4686 | .tag = .cset, |
| ... | @@ -4515,16 +4793,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -4515,16 +4793,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4515 | } | 4793 | } |
| 4516 | }, | 4794 | }, |
| 4517 | .ptr_stack_offset => |off| { | 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 | _ = try self.addInst(.{ | 4796 | _ = try self.addInst(.{ |
| 4523 | .tag = .sub_immediate, | 4797 | .tag = .ldr_ptr_stack, |
| 4524 | .data = .{ .rr_imm12_sh = .{ | 4798 | .data = .{ .load_store_stack = .{ |
| 4525 | .rd = reg, | 4799 | .rt = reg, |
| 4526 | .rn = .x29, | 4800 | .offset = @intCast(u32, off), |
| 4527 | .imm12 = imm12, | | |
| 4528 | } }, | 4801 | } }, |
| 4529 | }); | 4802 | }); |
| 4530 | }, | 4803 | }, |
| ... | @@ -4599,8 +4872,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -4599,8 +4872,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4599 | .memory => |addr| { | 4872 | .memory => |addr| { |
| 4600 | // The value is in memory at a hard-coded address. | 4873 | // The value is in memory at a hard-coded address. |
| 4601 | // If the type is a pointer, it means the pointer address is at this memory location. | 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 }); | 4875 | try self.genSetReg(ty, reg.toX(), .{ .immediate = addr }); |
| 4603 | try self.genLdrRegister(reg, reg.to64(), ty); | 4876 | try self.genLdrRegister(reg, reg.toX(), ty); |
| 4604 | }, | 4877 | }, |
| 4605 | .stack_offset => |off| { | 4878 | .stack_offset => |off| { |
| 4606 | const abi_size = ty.abiSize(self.target.*); | 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,7 +4952,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4679 | 4, 8 => .str_immediate, | 4952 | 4, 8 => .str_immediate, |
| 4680 | else => unreachable, // unexpected abi size | 4953 | else => unreachable, // unexpected abi size |
| 4681 | }; | 4954 | }; |
| 4682 | const rt = registerAlias(reg, abi_size); | 4955 | const rt = self.registerAlias(reg, ty); |
| 4683 | const offset = switch (abi_size) { | 4956 | const offset = switch (abi_size) { |
| 4684 | 1 => blk: { | 4957 | 1 => blk: { |
| 4685 | if (math.cast(u12, stack_offset)) |imm| { | 4958 | if (math.cast(u12, stack_offset)) |imm| { |
| ... | @@ -4838,7 +5111,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4838,7 +5111,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 4838 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 5111 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 4839 | const ptr_bytes = @divExact(ptr_bits, 8); | 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 | try self.genSetStack(ptr_ty, stack_offset, ptr); | 5115 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 4843 | try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len }); | 5116 | try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len }); |
| 4844 | break :result MCValue{ .stack_offset = stack_offset }; | 5117 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | @@ -5300,7 +5573,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -5300,7 +5573,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5300 | assert(ret_ty.isError()); | 5573 | assert(ret_ty.isError()); |
| 5301 | result.return_value = .{ .immediate = 0 }; | 5574 | result.return_value = .{ .immediate = 0 }; |
| 5302 | } else if (ret_ty_size <= 8) { | 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 | } else { | 5577 | } else { |
| 5305 | return self.fail("TODO support more return types for ARM backend", .{}); | 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,7 +5595,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5322 | | 5595 | |
| 5323 | if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { | 5596 | if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { |
| 5324 | if (param_size <= 8) { | 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 | ncrn += 1; | 5599 | ncrn += 1; |
| 5327 | } else { | 5600 | } else { |
| 5328 | return self.fail("TODO MCValues with multiple registers", .{}); | 5601 | return self.fail("TODO MCValues with multiple registers", .{}); |
| ... | @@ -5358,7 +5631,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -5358,7 +5631,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5358 | assert(ret_ty.isError()); | 5631 | assert(ret_ty.isError()); |
| 5359 | result.return_value = .{ .immediate = 0 }; | 5632 | result.return_value = .{ .immediate = 0 }; |
| 5360 | } else if (ret_ty_size <= 8) { | 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 | } else { | 5635 | } else { |
| 5363 | // The result is returned by reference, not by | 5636 | // The result is returned by reference, not by |
| 5364 | // value. This means that x0 (or w0 when pointer | 5637 | // value. This means that x0 (or w0 when pointer |
| ... | @@ -5424,14 +5697,30 @@ fn parseRegName(name: []const u8) ?Register { | ... | @@ -5424,14 +5697,30 @@ fn parseRegName(name: []const u8) ?Register { |
| 5424 | return std.meta.stringToEnum(Register, name); | 5697 | return std.meta.stringToEnum(Register, name); |
| 5425 | } | 5698 | } |
| 5426 | | 5699 | |
| 5427 | fn registerAlias(reg: Register, size_bytes: u64) Register { | 5700 | fn registerAlias(self: *Self, reg: Register, ty: Type) Register { |
| 5428 | if (size_bytes == 0) { | 5701 | const abi_size = ty.abiSize(self.target.*); |
| 5429 | unreachable; // should be comptime-known | 5702 | |
| 5430 | } else if (size_bytes <= 4) { | 5703 | switch (reg.class()) { |
| 5431 | return reg.to32(); | 5704 | .general_purpose => { |
| 5432 | } else if (size_bytes <= 8) { | 5705 | if (abi_size == 0) { |
| 5433 | return reg.to64(); | 5706 | unreachable; // should be comptime-known |
| 5434 | } else { | 5707 | } else if (abi_size <= 4) { |
| 5435 | unreachable; // TODO handle floating-point registers | 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 | } |