| ... | ... | @@ -513,7 +513,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 513 | 513 | .mul_sat => try self.airMulSat(inst), |
| 514 | 514 | .rem => try self.airRem(inst), |
| 515 | 515 | .mod => try self.airMod(inst), |
| 516 | | .shl, .shl_exact => try self.airShl(inst), |
| 516 | .shl, .shl_exact => try self.airBinOp(inst), |
| 517 | 517 | .shl_sat => try self.airShlSat(inst), |
| 518 | 518 | .min => try self.airMin(inst), |
| 519 | 519 | .max => try self.airMax(inst), |
| ... | ... | @@ -553,7 +553,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 553 | 553 | .bit_and => try self.airBinOp(inst), |
| 554 | 554 | .bit_or => try self.airBinOp(inst), |
| 555 | 555 | .xor => try self.airBinOp(inst), |
| 556 | | .shr, .shr_exact => try self.airShr(inst), |
| 556 | .shr, .shr_exact => try self.airBinOp(inst), |
| 557 | 557 | |
| 558 | 558 | .alloc => try self.airAlloc(inst), |
| 559 | 559 | .ret_ptr => try self.airRetPtr(inst), |
| ... | ... | @@ -1091,24 +1091,12 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { |
| 1091 | 1091 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1092 | 1092 | } |
| 1093 | 1093 | |
| 1094 | | fn airShl(self: *Self, inst: Air.Inst.Index) !void { |
| 1095 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1096 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shl); |
| 1097 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1098 | | } |
| 1099 | | |
| 1100 | 1094 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1101 | 1095 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1102 | 1096 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 1103 | 1097 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1104 | 1098 | } |
| 1105 | 1099 | |
| 1106 | | fn airShr(self: *Self, inst: Air.Inst.Index) !void { |
| 1107 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1108 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shr); |
| 1109 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1110 | | } |
| 1111 | | |
| 1112 | 1100 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1113 | 1101 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1114 | 1102 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); |
| ... | ... | @@ -1820,6 +1808,15 @@ fn binOpRegister( |
| 1820 | 1808 | .bit_or, |
| 1821 | 1809 | .bool_or, |
| 1822 | 1810 | => .orr, |
| 1811 | .shl, |
| 1812 | .shl_exact, |
| 1813 | => .lsl, |
| 1814 | .shr, |
| 1815 | .shr_exact, |
| 1816 | => switch (lhs_ty.intInfo(self.target.*).signedness) { |
| 1817 | .signed => Mir.Inst.Tag.asr, |
| 1818 | .unsigned => Mir.Inst.Tag.lsr, |
| 1819 | }, |
| 1823 | 1820 | .xor => .eor, |
| 1824 | 1821 | else => unreachable, |
| 1825 | 1822 | }; |
| ... | ... | @@ -1838,6 +1835,15 @@ fn binOpRegister( |
| 1838 | 1835 | .rn = lhs_reg, |
| 1839 | 1836 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), |
| 1840 | 1837 | } }, |
| 1838 | .shl, |
| 1839 | .shl_exact, |
| 1840 | .shr, |
| 1841 | .shr_exact, |
| 1842 | => .{ .rr_shift = .{ |
| 1843 | .rd = dest_reg, |
| 1844 | .rm = lhs_reg, |
| 1845 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), |
| 1846 | } }, |
| 1841 | 1847 | .mul => .{ .rrr = .{ |
| 1842 | 1848 | .rd = dest_reg, |
| 1843 | 1849 | .rn = lhs_reg, |
| ... | ... | @@ -1924,6 +1930,15 @@ fn binOpImmediate( |
| 1924 | 1930 | .bit_or, |
| 1925 | 1931 | .bool_or, |
| 1926 | 1932 | => .orr, |
| 1933 | .shl, |
| 1934 | .shl_exact, |
| 1935 | => .lsl, |
| 1936 | .shr, |
| 1937 | .shr_exact, |
| 1938 | => switch (lhs_ty.intInfo(self.target.*).signedness) { |
| 1939 | .signed => Mir.Inst.Tag.asr, |
| 1940 | .unsigned => Mir.Inst.Tag.lsr, |
| 1941 | }, |
| 1927 | 1942 | .xor => .eor, |
| 1928 | 1943 | else => unreachable, |
| 1929 | 1944 | }; |
| ... | ... | @@ -1940,6 +1955,15 @@ fn binOpImmediate( |
| 1940 | 1955 | .rn = lhs_reg, |
| 1941 | 1956 | .op = Instruction.Operand.fromU32(rhs.immediate).?, |
| 1942 | 1957 | } }, |
| 1958 | .shl, |
| 1959 | .shl_exact, |
| 1960 | .shr, |
| 1961 | .shr_exact, |
| 1962 | => .{ .rr_shift = .{ |
| 1963 | .rd = dest_reg, |
| 1964 | .rm = lhs_reg, |
| 1965 | .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs.immediate)), |
| 1966 | } }, |
| 1943 | 1967 | else => unreachable, |
| 1944 | 1968 | }; |
| 1945 | 1969 | |
| ... | ... | @@ -2060,6 +2084,28 @@ fn binOp( |
| 2060 | 2084 | else => unreachable, |
| 2061 | 2085 | } |
| 2062 | 2086 | }, |
| 2087 | .shl, |
| 2088 | .shr, |
| 2089 | => { |
| 2090 | switch (lhs_ty.zigTypeTag()) { |
| 2091 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), |
| 2092 | .Int => { |
| 2093 | const int_info = lhs_ty.intInfo(self.target.*); |
| 2094 | if (int_info.bits <= 32) { |
| 2095 | const rhs_immediate_ok = rhs == .immediate; |
| 2096 | |
| 2097 | if (rhs_immediate_ok) { |
| 2098 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| 2099 | } else { |
| 2100 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 2101 | } |
| 2102 | } else { |
| 2103 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| 2104 | } |
| 2105 | }, |
| 2106 | else => unreachable, |
| 2107 | } |
| 2108 | }, |
| 2063 | 2109 | .bool_and, |
| 2064 | 2110 | .bool_or, |
| 2065 | 2111 | => { |
| ... | ... | @@ -2129,212 +2175,6 @@ fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool { |
| 2129 | 2175 | }; |
| 2130 | 2176 | } |
| 2131 | 2177 | |
| 2132 | | fn genBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { |
| 2133 | | // In the case of bitshifts, the type of rhs is different |
| 2134 | | // from the resulting type |
| 2135 | | const ty = self.air.typeOf(op_lhs); |
| 2136 | | |
| 2137 | | switch (ty.zigTypeTag()) { |
| 2138 | | .Int => { |
| 2139 | | const int_info = ty.intInfo(self.target.*); |
| 2140 | | return self.genBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness); |
| 2141 | | }, |
| 2142 | | else => unreachable, |
| 2143 | | } |
| 2144 | | } |
| 2145 | | |
| 2146 | | fn genBinIntOp( |
| 2147 | | self: *Self, |
| 2148 | | inst: Air.Inst.Index, |
| 2149 | | op_lhs: Air.Inst.Ref, |
| 2150 | | op_rhs: Air.Inst.Ref, |
| 2151 | | op: Air.Inst.Tag, |
| 2152 | | bits: u16, |
| 2153 | | signedness: std.builtin.Signedness, |
| 2154 | | ) !MCValue { |
| 2155 | | if (bits > 32) { |
| 2156 | | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| 2157 | | } |
| 2158 | | |
| 2159 | | const lhs = try self.resolveInst(op_lhs); |
| 2160 | | const rhs = try self.resolveInst(op_rhs); |
| 2161 | | |
| 2162 | | const lhs_is_register = lhs == .register; |
| 2163 | | const rhs_is_register = rhs == .register; |
| 2164 | | const lhs_should_be_register = switch (op) { |
| 2165 | | .shr, .shl => true, |
| 2166 | | else => try self.armOperandShouldBeRegister(lhs), |
| 2167 | | }; |
| 2168 | | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); |
| 2169 | | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); |
| 2170 | | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); |
| 2171 | | const can_swap_lhs_and_rhs = switch (op) { |
| 2172 | | .shr, .shl => false, |
| 2173 | | else => true, |
| 2174 | | }; |
| 2175 | | |
| 2176 | | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 2177 | | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); |
| 2178 | | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 2179 | | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); |
| 2180 | | |
| 2181 | | // Destination must be a register |
| 2182 | | var dst_mcv: MCValue = undefined; |
| 2183 | | var lhs_mcv = lhs; |
| 2184 | | var rhs_mcv = rhs; |
| 2185 | | var swap_lhs_and_rhs = false; |
| 2186 | | |
| 2187 | | // Allocate registers for operands and/or destination |
| 2188 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 2189 | | if (reuse_lhs) { |
| 2190 | | // Allocate 0 or 1 registers |
| 2191 | | if (!rhs_is_register and rhs_should_be_register) { |
| 2192 | | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?) }; |
| 2193 | | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); |
| 2194 | | } |
| 2195 | | dst_mcv = lhs; |
| 2196 | | } else if (reuse_rhs and can_swap_lhs_and_rhs) { |
| 2197 | | // Allocate 0 or 1 registers |
| 2198 | | if (!lhs_is_register and lhs_should_be_register) { |
| 2199 | | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?) }; |
| 2200 | | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv); |
| 2201 | | } |
| 2202 | | dst_mcv = rhs; |
| 2203 | | |
| 2204 | | swap_lhs_and_rhs = true; |
| 2205 | | } else { |
| 2206 | | // Allocate 1 or 2 registers |
| 2207 | | if (lhs_should_be_register and rhs_should_be_register) { |
| 2208 | | if (lhs_is_register and rhs_is_register) { |
| 2209 | | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; |
| 2210 | | } else if (lhs_is_register) { |
| 2211 | | // Move RHS to register |
| 2212 | | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; |
| 2213 | | rhs_mcv = dst_mcv; |
| 2214 | | } else if (rhs_is_register) { |
| 2215 | | // Move LHS to register |
| 2216 | | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; |
| 2217 | | lhs_mcv = dst_mcv; |
| 2218 | | } else { |
| 2219 | | // Move LHS and RHS to register |
| 2220 | | const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }); |
| 2221 | | lhs_mcv = MCValue{ .register = regs[0] }; |
| 2222 | | rhs_mcv = MCValue{ .register = regs[1] }; |
| 2223 | | dst_mcv = lhs_mcv; |
| 2224 | | |
| 2225 | | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); |
| 2226 | | } |
| 2227 | | } else if (lhs_should_be_register) { |
| 2228 | | // RHS is immediate |
| 2229 | | if (lhs_is_register) { |
| 2230 | | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; |
| 2231 | | } else { |
| 2232 | | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; |
| 2233 | | lhs_mcv = dst_mcv; |
| 2234 | | } |
| 2235 | | } else if (rhs_should_be_register and can_swap_lhs_and_rhs) { |
| 2236 | | // LHS is immediate |
| 2237 | | if (rhs_is_register) { |
| 2238 | | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; |
| 2239 | | } else { |
| 2240 | | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; |
| 2241 | | rhs_mcv = dst_mcv; |
| 2242 | | } |
| 2243 | | |
| 2244 | | swap_lhs_and_rhs = true; |
| 2245 | | } else unreachable; // binary operation on two immediates |
| 2246 | | } |
| 2247 | | |
| 2248 | | // Move the operands to the newly allocated registers |
| 2249 | | if (lhs_mcv == .register and !lhs_is_register) { |
| 2250 | | try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs); |
| 2251 | | } |
| 2252 | | if (rhs_mcv == .register and !rhs_is_register) { |
| 2253 | | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); |
| 2254 | | } |
| 2255 | | |
| 2256 | | try self.genBinOpCode( |
| 2257 | | dst_mcv.register, |
| 2258 | | lhs_mcv, |
| 2259 | | rhs_mcv, |
| 2260 | | swap_lhs_and_rhs, |
| 2261 | | op, |
| 2262 | | signedness, |
| 2263 | | ); |
| 2264 | | return dst_mcv; |
| 2265 | | } |
| 2266 | | |
| 2267 | | fn genBinOpCode( |
| 2268 | | self: *Self, |
| 2269 | | dst_reg: Register, |
| 2270 | | lhs_mcv: MCValue, |
| 2271 | | rhs_mcv: MCValue, |
| 2272 | | swap_lhs_and_rhs: bool, |
| 2273 | | op: Air.Inst.Tag, |
| 2274 | | signedness: std.builtin.Signedness, |
| 2275 | | ) !void { |
| 2276 | | assert(lhs_mcv == .register or rhs_mcv == .register); |
| 2277 | | |
| 2278 | | const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register; |
| 2279 | | const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv; |
| 2280 | | |
| 2281 | | const operand = switch (op2) { |
| 2282 | | .none => unreachable, |
| 2283 | | .undef => unreachable, |
| 2284 | | .dead, .unreach => unreachable, |
| 2285 | | .compare_flags_unsigned => unreachable, |
| 2286 | | .compare_flags_signed => unreachable, |
| 2287 | | .ptr_stack_offset => unreachable, |
| 2288 | | .ptr_embedded_in_code => unreachable, |
| 2289 | | .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?, |
| 2290 | | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), |
| 2291 | | .stack_offset, |
| 2292 | | .stack_argument_offset, |
| 2293 | | .embedded_in_code, |
| 2294 | | .memory, |
| 2295 | | => unreachable, |
| 2296 | | }; |
| 2297 | | |
| 2298 | | switch (op) { |
| 2299 | | .cmp_eq => { |
| 2300 | | _ = try self.addInst(.{ |
| 2301 | | .tag = .cmp, |
| 2302 | | .data = .{ .rr_op = .{ |
| 2303 | | .rd = .r0, |
| 2304 | | .rn = op1, |
| 2305 | | .op = operand, |
| 2306 | | } }, |
| 2307 | | }); |
| 2308 | | }, |
| 2309 | | .shl, .shr => { |
| 2310 | | assert(!swap_lhs_and_rhs); |
| 2311 | | const shift_amount = switch (operand) { |
| 2312 | | .register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), |
| 2313 | | .immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), |
| 2314 | | }; |
| 2315 | | |
| 2316 | | const tag: Mir.Inst.Tag = switch (op) { |
| 2317 | | .shl => .lsl, |
| 2318 | | .shr => switch (signedness) { |
| 2319 | | .signed => Mir.Inst.Tag.asr, |
| 2320 | | .unsigned => Mir.Inst.Tag.lsr, |
| 2321 | | }, |
| 2322 | | else => unreachable, |
| 2323 | | }; |
| 2324 | | |
| 2325 | | _ = try self.addInst(.{ |
| 2326 | | .tag = tag, |
| 2327 | | .data = .{ .rr_shift = .{ |
| 2328 | | .rd = dst_reg, |
| 2329 | | .rm = op1, |
| 2330 | | .shift_amount = shift_amount, |
| 2331 | | } }, |
| 2332 | | }); |
| 2333 | | }, |
| 2334 | | else => unreachable, // not a binary instruction |
| 2335 | | } |
| 2336 | | } |
| 2337 | | |
| 2338 | 2178 | fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void { |
| 2339 | 2179 | switch (abi_size) { |
| 2340 | 2180 | 1, 3, 4 => { |
| ... | ... | @@ -2769,9 +2609,18 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2769 | 2609 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs); |
| 2770 | 2610 | } |
| 2771 | 2611 | |
| 2772 | | // The destination register is not present in the cmp instruction |
| 2773 | | // The signedness of the integer does not matter for the cmp instruction |
| 2774 | | try self.genBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); |
| 2612 | _ = try self.addInst(.{ |
| 2613 | .tag = .cmp, |
| 2614 | .data = .{ .rr_op = .{ |
| 2615 | .rd = undefined, |
| 2616 | .rn = lhs_mcv.register, |
| 2617 | .op = switch (rhs_mcv) { |
| 2618 | .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?, |
| 2619 | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), |
| 2620 | else => unreachable, |
| 2621 | }, |
| 2622 | } }, |
| 2623 | }); |
| 2775 | 2624 | |
| 2776 | 2625 | break :result switch (signedness) { |
| 2777 | 2626 | .signed => MCValue{ .compare_flags_signed = op }, |
| ... | ... | @@ -3003,7 +2852,14 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3003 | 2852 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, |
| 3004 | 2853 | }; |
| 3005 | 2854 | |
| 3006 | | try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); |
| 2855 | _ = try self.addInst(.{ |
| 2856 | .tag = .cmp, |
| 2857 | .data = .{ .rr_op = .{ |
| 2858 | .rd = undefined, |
| 2859 | .rn = reg_mcv.register, |
| 2860 | .op = Instruction.Operand.fromU32(0).?, |
| 2861 | } }, |
| 2862 | }); |
| 3007 | 2863 | |
| 3008 | 2864 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 3009 | 2865 | } else { |
| ... | ... | @@ -3033,7 +2889,14 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3033 | 2889 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, |
| 3034 | 2890 | }; |
| 3035 | 2891 | |
| 3036 | | try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); |
| 2892 | _ = try self.addInst(.{ |
| 2893 | .tag = .cmp, |
| 2894 | .data = .{ .rr_op = .{ |
| 2895 | .rd = undefined, |
| 2896 | .rn = reg_mcv.register, |
| 2897 | .op = Instruction.Operand.fromU32(0).?, |
| 2898 | } }, |
| 2899 | }); |
| 3037 | 2900 | |
| 3038 | 2901 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 3039 | 2902 | } else { |