| ... | ... | @@ -987,6 +987,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 987 | 987 | .x86_64 => { |
| 988 | 988 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00); |
| 989 | 989 | }, |
| 990 | .arm, .armeb => return try self.genArmBinArith(inst, .add), |
| 990 | 991 | else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}), |
| 991 | 992 | } |
| 992 | 993 | } |
| ... | ... | @@ -1144,10 +1145,99 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1144 | 1145 | .x86_64 => { |
| 1145 | 1146 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28); |
| 1146 | 1147 | }, |
| 1148 | .arm, .armeb => return try self.genArmBinArith(inst, .sub), |
| 1147 | 1149 | else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}), |
| 1148 | 1150 | } |
| 1149 | 1151 | } |
| 1150 | 1152 | |
| 1153 | fn genArmBinArith(self: *Self, inst: *ir.Inst.BinOp, op: ir.Inst.Tag) !MCValue { |
| 1154 | const lhs = try self.resolveInst(inst.lhs); |
| 1155 | const rhs = try self.resolveInst(inst.rhs); |
| 1156 | |
| 1157 | // Destination must be a register |
| 1158 | // Source may be register, memory or an immediate |
| 1159 | // |
| 1160 | // So there are two options: (lhs is src and rhs is dest) |
| 1161 | // or (rhs is src and lhs is dest) |
| 1162 | const lhs_is_dest = blk: { |
| 1163 | if (self.reuseOperand(&inst.base, 0, lhs)) { |
| 1164 | break :blk true; |
| 1165 | } else if (self.reuseOperand(&inst.base, 1, rhs)) { |
| 1166 | break :blk false; |
| 1167 | } else { |
| 1168 | break :blk lhs == .register; |
| 1169 | } |
| 1170 | }; |
| 1171 | |
| 1172 | var dst_mcv: MCValue = undefined; |
| 1173 | var src_mcv: MCValue = undefined; |
| 1174 | var src_inst: *ir.Inst = undefined; |
| 1175 | if (lhs_is_dest) { |
| 1176 | // LHS is the destination |
| 1177 | // RHS is the source |
| 1178 | src_inst = inst.rhs; |
| 1179 | src_mcv = rhs; |
| 1180 | dst_mcv = if (lhs != .register) try self.copyToNewRegister(&inst.base, lhs) else lhs; |
| 1181 | } else { |
| 1182 | // RHS is the destination |
| 1183 | // LHS is the source |
| 1184 | src_inst = inst.lhs; |
| 1185 | src_mcv = lhs; |
| 1186 | dst_mcv = if (rhs != .register) try self.copyToNewRegister(&inst.base, rhs) else rhs; |
| 1187 | } |
| 1188 | |
| 1189 | try self.genArmBinArithCode(inst.base.src, dst_mcv.register, src_mcv, lhs_is_dest, op); |
| 1190 | return dst_mcv; |
| 1191 | } |
| 1192 | |
| 1193 | fn genArmBinArithCode( |
| 1194 | self: *Self, |
| 1195 | src: usize, |
| 1196 | dst_reg: Register, |
| 1197 | src_mcv: MCValue, |
| 1198 | lhs_is_dest: bool, |
| 1199 | op: ir.Inst.Tag, |
| 1200 | ) !void { |
| 1201 | const operand = switch (src_mcv) { |
| 1202 | .none => unreachable, |
| 1203 | .undef => unreachable, |
| 1204 | .dead, .unreach => unreachable, |
| 1205 | .compare_flags_unsigned => unreachable, |
| 1206 | .compare_flags_signed => unreachable, |
| 1207 | .ptr_stack_offset => unreachable, |
| 1208 | .ptr_embedded_in_code => unreachable, |
| 1209 | .immediate => |imm| blk: { |
| 1210 | if (imm > std.math.maxInt(u32)) return self.fail(src, "TODO ARM binary arithmetic immediate larger than u32", .{}); |
| 1211 | |
| 1212 | // Load immediate into register if it doesn't fit |
| 1213 | // as an operand |
| 1214 | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse |
| 1215 | Instruction.Operand.reg(try self.copyToTmpRegister(src, src_mcv), Instruction.Operand.Shift.none); |
| 1216 | }, |
| 1217 | .register => |src_reg| Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none), |
| 1218 | .stack_offset, |
| 1219 | .embedded_in_code, |
| 1220 | .memory, |
| 1221 | => Instruction.Operand.reg(try self.copyToTmpRegister(src, src_mcv), Instruction.Operand.Shift.none), |
| 1222 | }; |
| 1223 | |
| 1224 | switch (op) { |
| 1225 | .add => { |
| 1226 | // TODO runtime safety checks (overflow) |
| 1227 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, dst_reg, operand).toU32()); |
| 1228 | }, |
| 1229 | .sub => { |
| 1230 | // TODO runtime safety checks (underflow) |
| 1231 | if (lhs_is_dest) { |
| 1232 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, dst_reg, operand).toU32()); |
| 1233 | } else { |
| 1234 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, dst_reg, operand).toU32()); |
| 1235 | } |
| 1236 | }, |
| 1237 | else => unreachable, // not a binary arithmetic instruction |
| 1238 | } |
| 1239 | } |
| 1240 | |
| 1151 | 1241 | /// ADD, SUB, XOR, OR, AND |
| 1152 | 1242 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { |
| 1153 | 1243 | try self.code.ensureCapacity(self.code.items.len + 8); |
| ... | ... | @@ -2106,7 +2196,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2106 | 2196 | // lhs OR rhs |
| 2107 | 2197 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 1, 0x08); |
| 2108 | 2198 | }, |
| 2109 | | else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}), |
| 2199 | else => return self.fail(inst.base.src, "TODO implement boolean operations for {}", .{self.target.cpu.arch}), |
| 2110 | 2200 | } |
| 2111 | 2201 | } |
| 2112 | 2202 | |