| ... | @@ -975,6 +975,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -975,6 +975,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 975 | }; | 975 | }; |
| 976 | return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base, 6, 0x30); | 976 | return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base, 6, 0x30); |
| 977 | }, | 977 | }, |
| | 978 | .arm, .armeb => { |
| | 979 | var imm = ir.Inst.Constant{ |
| | 980 | .base = .{ |
| | 981 | .tag = .constant, |
| | 982 | .deaths = 0, |
| | 983 | .ty = inst.operand.ty, |
| | 984 | .src = inst.operand.src, |
| | 985 | }, |
| | 986 | .val = Value.initTag(.bool_true), |
| | 987 | }; |
| | 988 | return try self.genArmBinOp(&inst.base, inst.operand, &imm.base, .not); |
| | 989 | }, |
| 978 | else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}), | 990 | else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}), |
| 979 | } | 991 | } |
| 980 | } | 992 | } |
| ... | @@ -987,6 +999,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -987,6 +999,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 987 | .x86_64 => { | 999 | .x86_64 => { |
| 988 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00); | 1000 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00); |
| 989 | }, | 1001 | }, |
| | 1002 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .add), |
| 990 | else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}), | 1003 | else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}), |
| 991 | } | 1004 | } |
| 992 | } | 1005 | } |
| ... | @@ -1144,10 +1157,108 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1144,10 +1157,108 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1144 | .x86_64 => { | 1157 | .x86_64 => { |
| 1145 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28); | 1158 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28); |
| 1146 | }, | 1159 | }, |
| | 1160 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .sub), |
| 1147 | else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}), | 1161 | else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}), |
| 1148 | } | 1162 | } |
| 1149 | } | 1163 | } |
| 1150 | | 1164 | |
| | 1165 | fn genArmBinOp(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, op: ir.Inst.Tag) !MCValue { |
| | 1166 | const lhs = try self.resolveInst(op_lhs); |
| | 1167 | const rhs = try self.resolveInst(op_rhs); |
| | 1168 | |
| | 1169 | // Destination must be a register |
| | 1170 | // Source may be register, memory or an immediate |
| | 1171 | // |
| | 1172 | // So there are two options: (lhs is src and rhs is dest) |
| | 1173 | // or (rhs is src and lhs is dest) |
| | 1174 | const lhs_is_dest = blk: { |
| | 1175 | if (self.reuseOperand(inst, 0, lhs)) { |
| | 1176 | break :blk true; |
| | 1177 | } else if (self.reuseOperand(inst, 1, rhs)) { |
| | 1178 | break :blk false; |
| | 1179 | } else { |
| | 1180 | break :blk lhs == .register; |
| | 1181 | } |
| | 1182 | }; |
| | 1183 | |
| | 1184 | var dst_mcv: MCValue = undefined; |
| | 1185 | var src_mcv: MCValue = undefined; |
| | 1186 | var src_inst: *ir.Inst = undefined; |
| | 1187 | if (lhs_is_dest) { |
| | 1188 | // LHS is the destination |
| | 1189 | // RHS is the source |
| | 1190 | src_inst = op_rhs; |
| | 1191 | src_mcv = rhs; |
| | 1192 | dst_mcv = if (lhs != .register) try self.copyToNewRegister(inst, lhs) else lhs; |
| | 1193 | } else { |
| | 1194 | // RHS is the destination |
| | 1195 | // LHS is the source |
| | 1196 | src_inst = op_lhs; |
| | 1197 | src_mcv = lhs; |
| | 1198 | dst_mcv = if (rhs != .register) try self.copyToNewRegister(inst, rhs) else rhs; |
| | 1199 | } |
| | 1200 | |
| | 1201 | try self.genArmBinOpCode(inst.src, dst_mcv.register, src_mcv, lhs_is_dest, op); |
| | 1202 | return dst_mcv; |
| | 1203 | } |
| | 1204 | |
| | 1205 | fn genArmBinOpCode( |
| | 1206 | self: *Self, |
| | 1207 | src: usize, |
| | 1208 | dst_reg: Register, |
| | 1209 | src_mcv: MCValue, |
| | 1210 | lhs_is_dest: bool, |
| | 1211 | op: ir.Inst.Tag, |
| | 1212 | ) !void { |
| | 1213 | const operand = switch (src_mcv) { |
| | 1214 | .none => unreachable, |
| | 1215 | .undef => unreachable, |
| | 1216 | .dead, .unreach => unreachable, |
| | 1217 | .compare_flags_unsigned => unreachable, |
| | 1218 | .compare_flags_signed => unreachable, |
| | 1219 | .ptr_stack_offset => unreachable, |
| | 1220 | .ptr_embedded_in_code => unreachable, |
| | 1221 | .immediate => |imm| blk: { |
| | 1222 | if (imm > std.math.maxInt(u32)) return self.fail(src, "TODO ARM binary arithmetic immediate larger than u32", .{}); |
| | 1223 | |
| | 1224 | // Load immediate into register if it doesn't fit |
| | 1225 | // as an operand |
| | 1226 | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse |
| | 1227 | Instruction.Operand.reg(try self.copyToTmpRegister(src, src_mcv), Instruction.Operand.Shift.none); |
| | 1228 | }, |
| | 1229 | .register => |src_reg| Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none), |
| | 1230 | .stack_offset, |
| | 1231 | .embedded_in_code, |
| | 1232 | .memory, |
| | 1233 | => Instruction.Operand.reg(try self.copyToTmpRegister(src, src_mcv), Instruction.Operand.Shift.none), |
| | 1234 | }; |
| | 1235 | |
| | 1236 | switch (op) { |
| | 1237 | .add => { |
| | 1238 | // TODO runtime safety checks (overflow) |
| | 1239 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, dst_reg, operand).toU32()); |
| | 1240 | }, |
| | 1241 | .sub => { |
| | 1242 | // TODO runtime safety checks (underflow) |
| | 1243 | if (lhs_is_dest) { |
| | 1244 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, dst_reg, operand).toU32()); |
| | 1245 | } else { |
| | 1246 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, dst_reg, operand).toU32()); |
| | 1247 | } |
| | 1248 | }, |
| | 1249 | .booland => { |
| | 1250 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, dst_reg, operand).toU32()); |
| | 1251 | }, |
| | 1252 | .boolor => { |
| | 1253 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, dst_reg, operand).toU32()); |
| | 1254 | }, |
| | 1255 | .not => { |
| | 1256 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, dst_reg, operand).toU32()); |
| | 1257 | }, |
| | 1258 | else => unreachable, // not a binary instruction |
| | 1259 | } |
| | 1260 | } |
| | 1261 | |
| 1151 | /// ADD, SUB, XOR, OR, AND | 1262 | /// ADD, SUB, XOR, OR, AND |
| 1152 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { | 1263 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { |
| 1153 | try self.code.ensureCapacity(self.code.items.len + 8); | 1264 | try self.code.ensureCapacity(self.code.items.len + 8); |
| ... | @@ -2099,14 +2210,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2099,14 +2210,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2099 | if (inst.base.isUnused()) | 2210 | if (inst.base.isUnused()) |
| 2100 | return MCValue.dead; | 2211 | return MCValue.dead; |
| 2101 | switch (arch) { | 2212 | switch (arch) { |
| 2102 | .x86_64 => if (inst.base.tag == .booland) { | 2213 | .x86_64 => switch (inst.base.tag) { |
| 2103 | // lhs AND rhs | 2214 | // lhs AND rhs |
| 2104 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 4, 0x20); | 2215 | .booland => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 4, 0x20), |
| 2105 | } else { | | |
| 2106 | // lhs OR rhs | 2216 | // lhs OR rhs |
| 2107 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 1, 0x08); | 2217 | .boolor => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 1, 0x08), |
| | 2218 | else => unreachable, // Not a boolean operation |
| 2108 | }, | 2219 | }, |
| 2109 | else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}), | 2220 | .arm, .armeb => switch (inst.base.tag) { |
| | 2221 | .booland => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .booland), |
| | 2222 | .boolor => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .boolor), |
| | 2223 | else => unreachable, // Not a boolean operation |
| | 2224 | }, |
| | 2225 | else => return self.fail(inst.base.src, "TODO implement boolean operations for {}", .{self.target.cpu.arch}), |
| 2110 | } | 2226 | } |
| 2111 | } | 2227 | } |
| 2112 | | 2228 | |
| ... | @@ -2359,14 +2475,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2359,14 +2475,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2359 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); | 2475 | return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); |
| 2360 | }, | 2476 | }, |
| 2361 | .register => |reg| { | 2477 | .register => |reg| { |
| 2362 | // TODO: strb, strh | 2478 | // TODO: strh |
| 2363 | if (stack_offset <= math.maxInt(u12)) { | 2479 | const offset = if (stack_offset <= math.maxInt(u12)) blk: { |
| 2364 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.str(.al, reg, .fp, .{ | 2480 | break :blk Instruction.Offset.imm(@intCast(u12, stack_offset)); |
| 2365 | .offset = Instruction.Offset.imm(@intCast(u12, stack_offset)), | 2481 | } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = stack_offset }), 0); |
| | 2482 | |
| | 2483 | const abi_size = ty.abiSize(self.target.*); |
| | 2484 | switch (abi_size) { |
| | 2485 | 1 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.strb(.al, reg, .fp, .{ |
| | 2486 | .offset = offset, |
| 2366 | .positive = false, | 2487 | .positive = false, |
| 2367 | }).toU32()); | 2488 | }).toU32()), |
| 2368 | } else { | 2489 | 2 => return self.fail(src, "TODO implement strh", .{}), |
| 2369 | return self.fail(src, "TODO genSetStack with larger offsets", .{}); | 2490 | 4 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.str(.al, reg, .fp, .{ |
| | 2491 | .offset = offset, |
| | 2492 | .positive = false, |
| | 2493 | }).toU32()), |
| | 2494 | else => return self.fail(src, "TODO a type of size {} is not allowed in a register", .{abi_size}), |
| 2370 | } | 2495 | } |
| 2371 | }, | 2496 | }, |
| 2372 | .memory => |vaddr| { | 2497 | .memory => |vaddr| { |
| ... | @@ -2537,15 +2662,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2537,15 +2662,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2537 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); | 2662 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); |
| 2538 | }, | 2663 | }, |
| 2539 | .stack_offset => |unadjusted_off| { | 2664 | .stack_offset => |unadjusted_off| { |
| 2540 | // TODO: ldrb, ldrh | 2665 | // TODO: ldrh |
| 2541 | // TODO: maybe addressing from sp instead of fp | 2666 | // TODO: maybe addressing from sp instead of fp |
| 2542 | if (unadjusted_off <= math.maxInt(u12)) { | 2667 | const offset = if (unadjusted_off <= math.maxInt(u12)) blk: { |
| 2543 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, .fp, .{ | 2668 | break :blk Instruction.Offset.imm(@intCast(u12, unadjusted_off)); |
| 2544 | .offset = Instruction.Offset.imm(@intCast(u12, unadjusted_off)), | 2669 | } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = unadjusted_off }), 0); |
| | 2670 | |
| | 2671 | // TODO: supply type information to genSetReg as we do to genSetStack |
| | 2672 | // const abi_size = ty.abiSize(self.target.*); |
| | 2673 | const abi_size = 4; |
| | 2674 | switch (abi_size) { |
| | 2675 | 1 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrb(.al, reg, .fp, .{ |
| | 2676 | .offset = offset, |
| 2545 | .positive = false, | 2677 | .positive = false, |
| 2546 | }).toU32()); | 2678 | }).toU32()), |
| 2547 | } else { | 2679 | 2 => return self.fail(src, "TODO implement strh", .{}), |
| 2548 | return self.fail(src, "TODO genSetReg with larger stack offset", .{}); | 2680 | 4 => writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, .fp, .{ |
| | 2681 | .offset = offset, |
| | 2682 | .positive = false, |
| | 2683 | }).toU32()), |
| | 2684 | else => return self.fail(src, "TODO a type of size {} is not allowed in a register", .{abi_size}), |
| 2549 | } | 2685 | } |
| 2550 | }, | 2686 | }, |
| 2551 | else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}), | 2687 | else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}), |