| ... | @@ -105,9 +105,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | ... | @@ -105,9 +105,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 105 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; | 105 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 106 | | 106 | |
| 107 | const MCValue = union(enum) { | 107 | const MCValue = union(enum) { |
| 108 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. | 108 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 |
| 109 | /// TODO Look into deleting this tag and using `dead` instead, since every use | 109 | /// tag, etc. |
| 110 | /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. | 110 | /// |
| | 111 | /// TODO Look into deleting this tag and using `dead` instead, |
| | 112 | /// since every use of MCValue.none should be instead looking at |
| | 113 | /// the type and noticing it is 0 bits. |
| 111 | none, | 114 | none, |
| 112 | /// Control flow will not allow this value to be observed. | 115 | /// Control flow will not allow this value to be observed. |
| 113 | unreach, | 116 | unreach, |
| ... | @@ -116,20 +119,41 @@ const MCValue = union(enum) { | ... | @@ -116,20 +119,41 @@ const MCValue = union(enum) { |
| 116 | /// The value is undefined. | 119 | /// The value is undefined. |
| 117 | undef, | 120 | undef, |
| 118 | /// A pointer-sized integer that fits in a register. | 121 | /// A pointer-sized integer that fits in a register. |
| 119 | /// If the type is a pointer, this is the pointer address in virtual address space. | 122 | /// |
| | 123 | /// If the type is a pointer, this is the pointer address in |
| | 124 | /// virtual address space. |
| 120 | immediate: u32, | 125 | immediate: u32, |
| 121 | /// The value is in a target-specific register. | 126 | /// The value is in a target-specific register. |
| 122 | register: Register, | 127 | register: Register, |
| | 128 | /// The value is a tuple { wrapped: u32, overflow: u1 } where |
| | 129 | /// wrapped is stored in the register and the overflow bit is |
| | 130 | /// stored in the C flag of the CPSR. |
| | 131 | /// |
| | 132 | /// This MCValue is only generated by a add_with_overflow or |
| | 133 | /// sub_with_overflow instruction operating on u32. |
| | 134 | register_c_flag: Register, |
| | 135 | /// The value is a tuple { wrapped: i32, overflow: u1 } where |
| | 136 | /// wrapped is stored in the register and the overflow bit is |
| | 137 | /// stored in the V flag of the CPSR. |
| | 138 | /// |
| | 139 | /// This MCValue is only generated by a add_with_overflow or |
| | 140 | /// sub_with_overflow instruction operating on i32. |
| | 141 | register_v_flag: Register, |
| 123 | /// The value is in memory at a hard-coded address. | 142 | /// The value is in memory at a hard-coded address. |
| 124 | /// If the type is a pointer, it means the pointer address is at this memory location. | 143 | /// |
| | 144 | /// If the type is a pointer, it means the pointer address is at |
| | 145 | /// this memory location. |
| 125 | memory: u64, | 146 | memory: u64, |
| 126 | /// The value is one of the stack variables. | 147 | /// The value is one of the stack variables. |
| 127 | /// If the type is a pointer, it means the pointer address is in the stack at this offset. | 148 | /// |
| | 149 | /// If the type is a pointer, it means the pointer address is in |
| | 150 | /// the stack at this offset. |
| 128 | stack_offset: u32, | 151 | stack_offset: u32, |
| 129 | /// The value is a pointer to one of the stack variables (payload is stack offset). | 152 | /// The value is a pointer to one of the stack variables (payload |
| | 153 | /// is stack offset). |
| 130 | ptr_stack_offset: u32, | 154 | ptr_stack_offset: u32, |
| 131 | /// The value is in the compare flags assuming an unsigned operation, | 155 | /// The value is in the compare flags assuming an unsigned |
| 132 | /// with this operator applied on top of it. | 156 | /// operation, with this operator applied on top of it. |
| 133 | compare_flags_unsigned: math.CompareOperator, | 157 | compare_flags_unsigned: math.CompareOperator, |
| 134 | /// The value is in the compare flags assuming a signed operation, | 158 | /// The value is in the compare flags assuming a signed operation, |
| 135 | /// with this operator applied on top of it. | 159 | /// with this operator applied on top of it. |
| ... | @@ -554,8 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -554,8 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 554 | .trunc_float, | 578 | .trunc_float, |
| 555 | => try self.airUnaryMath(inst), | 579 | => try self.airUnaryMath(inst), |
| 556 | | 580 | |
| 557 | .add_with_overflow => try self.airAddWithOverflow(inst), | 581 | .add_with_overflow => try self.airOverflow(inst), |
| 558 | .sub_with_overflow => try self.airSubWithOverflow(inst), | 582 | .sub_with_overflow => try self.airOverflow(inst), |
| 559 | .mul_with_overflow => try self.airMulWithOverflow(inst), | 583 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 560 | .shl_with_overflow => try self.airShlWithOverflow(inst), | 584 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 561 | | 585 | |
| ... | @@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { | ... | @@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 726 | .register => |reg| { | 750 | .register => |reg| { |
| 727 | self.register_manager.freeReg(reg); | 751 | self.register_manager.freeReg(reg); |
| 728 | }, | 752 | }, |
| | 753 | .register_c_flag, |
| | 754 | .register_v_flag, |
| | 755 | => |reg| { |
| | 756 | self.register_manager.freeReg(reg); |
| | 757 | self.compare_flags_inst = null; |
| | 758 | }, |
| 729 | .compare_flags_signed, .compare_flags_unsigned => { | 759 | .compare_flags_signed, .compare_flags_unsigned => { |
| 730 | self.compare_flags_inst = null; | 760 | self.compare_flags_inst = null; |
| 731 | }, | 761 | }, |
| ... | @@ -841,8 +871,16 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { | ... | @@ -841,8 +871,16 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 841 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 871 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 842 | const stack_mcv = try self.allocRegOrMem(inst, false); | 872 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 843 | log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); | 873 | log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); |
| | 874 | |
| 844 | const reg_mcv = self.getResolvedInstValue(inst); | 875 | const reg_mcv = self.getResolvedInstValue(inst); |
| 845 | assert(reg == reg_mcv.register); | 876 | switch (reg_mcv) { |
| | 877 | .register, |
| | 878 | .register_c_flag, |
| | 879 | .register_v_flag, |
| | 880 | => |r| assert(r == reg), |
| | 881 | else => unreachable, // not a register |
| | 882 | } |
| | 883 | |
| 846 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 884 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 847 | try branch.inst_table.put(self.gpa, inst, stack_mcv); | 885 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 848 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); | 886 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| ... | @@ -853,7 +891,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void | ... | @@ -853,7 +891,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 853 | fn spillCompareFlagsIfOccupied(self: *Self) !void { | 891 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 854 | if (self.compare_flags_inst) |inst_to_save| { | 892 | if (self.compare_flags_inst) |inst_to_save| { |
| 855 | const mcv = self.getResolvedInstValue(inst_to_save); | 893 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 856 | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); | 894 | switch (mcv) { |
| | 895 | .compare_flags_signed, |
| | 896 | .compare_flags_unsigned, |
| | 897 | .register_c_flag, |
| | 898 | .register_v_flag, |
| | 899 | => {}, |
| | 900 | else => unreachable, // mcv doesn't occupy the compare flags |
| | 901 | } |
| 857 | | 902 | |
| 858 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); | 903 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 859 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); | 904 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| ... | @@ -1196,7 +1241,7 @@ fn minMax( | ... | @@ -1196,7 +1241,7 @@ fn minMax( |
| 1196 | // register. | 1241 | // register. |
| 1197 | assert(lhs_reg != rhs_reg); // see note above | 1242 | assert(lhs_reg != rhs_reg); // see note above |
| 1198 | | 1243 | |
| 1199 | _ = try self.binOpRegister(.cmp_eq, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); | 1244 | _ = try self.binOpRegister(.cmp, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); |
| 1200 | | 1245 | |
| 1201 | const cond_choose_lhs: Condition = switch (tag) { | 1246 | const cond_choose_lhs: Condition = switch (tag) { |
| 1202 | .max => switch (int_info.signedness) { | 1247 | .max => switch (int_info.signedness) { |
| ... | @@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1268 | const len = try self.resolveInst(bin_op.rhs); | 1313 | const len = try self.resolveInst(bin_op.rhs); |
| 1269 | const len_ty = self.air.typeOf(bin_op.rhs); | 1314 | const len_ty = self.air.typeOf(bin_op.rhs); |
| 1270 | | 1315 | |
| 1271 | const stack_offset = try self.allocMem(inst, 8, 8); | 1316 | const stack_offset = try self.allocMem(inst, 8, 4); |
| 1272 | try self.genSetStack(ptr_ty, stack_offset, ptr); | 1317 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 1273 | try self.genSetStack(len_ty, stack_offset - 4, len); | 1318 | try self.genSetStack(len_ty, stack_offset - 4, len); |
| 1274 | break :result MCValue{ .stack_offset = stack_offset }; | 1319 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | @@ -1306,24 +1351,321 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1306,24 +1351,321 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1306 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1351 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1307 | } | 1352 | } |
| 1308 | | 1353 | |
| 1309 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1354 | fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1310 | _ = inst; | 1355 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1311 | return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); | 1356 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1312 | } | 1357 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 1358 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 1359 | const lhs = try self.resolveInst(extra.lhs); |
| | 1360 | const rhs = try self.resolveInst(extra.rhs); |
| | 1361 | const lhs_ty = self.air.typeOf(extra.lhs); |
| | 1362 | const rhs_ty = self.air.typeOf(extra.rhs); |
| | 1363 | |
| | 1364 | const tuple_ty = self.air.typeOfIndex(inst); |
| | 1365 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| | 1366 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| | 1367 | const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); |
| | 1368 | |
| | 1369 | switch (lhs_ty.zigTypeTag()) { |
| | 1370 | .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), |
| | 1371 | .Int => { |
| | 1372 | assert(lhs_ty.eql(rhs_ty, self.target.*)); |
| | 1373 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 1374 | if (int_info.bits < 32) { |
| | 1375 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| | 1376 | |
| | 1377 | try self.spillCompareFlagsIfOccupied(); |
| | 1378 | self.compare_flags_inst = null; |
| | 1379 | |
| | 1380 | const base_tag: Air.Inst.Tag = switch (tag) { |
| | 1381 | .add_with_overflow => .add, |
| | 1382 | .sub_with_overflow => .sub, |
| | 1383 | else => unreachable, |
| | 1384 | }; |
| | 1385 | const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); |
| | 1386 | const dest_reg = dest.register; |
| | 1387 | self.register_manager.freezeRegs(&.{dest_reg}); |
| | 1388 | defer self.register_manager.unfreezeRegs(&.{dest_reg}); |
| | 1389 | |
| | 1390 | const truncated_reg = try self.register_manager.allocReg(null); |
| | 1391 | self.register_manager.freezeRegs(&.{truncated_reg}); |
| | 1392 | defer self.register_manager.unfreezeRegs(&.{truncated_reg}); |
| | 1393 | |
| | 1394 | // sbfx/ubfx truncated, dest, #0, #bits |
| | 1395 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| | 1396 | |
| | 1397 | // cmp dest, truncated |
| | 1398 | _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); |
| | 1399 | |
| | 1400 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| | 1401 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); |
| | 1402 | |
| | 1403 | break :result MCValue{ .stack_offset = stack_offset }; |
| | 1404 | } else if (int_info.bits == 32) { |
| | 1405 | // Only say yes if the operation is |
| | 1406 | // commutative, i.e. we can swap both of the |
| | 1407 | // operands |
| | 1408 | const lhs_immediate_ok = switch (tag) { |
| | 1409 | .add_with_overflow => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, |
| | 1410 | .sub_with_overflow => false, |
| | 1411 | else => unreachable, |
| | 1412 | }; |
| | 1413 | const rhs_immediate_ok = switch (tag) { |
| | 1414 | .add_with_overflow, |
| | 1415 | .sub_with_overflow, |
| | 1416 | => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, |
| | 1417 | else => unreachable, |
| | 1418 | }; |
| 1313 | | 1419 | |
| 1314 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1420 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1315 | _ = inst; | 1421 | .add_with_overflow => .adds, |
| 1316 | return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); | 1422 | .sub_with_overflow => .subs, |
| | 1423 | else => unreachable, |
| | 1424 | }; |
| | 1425 | |
| | 1426 | try self.spillCompareFlagsIfOccupied(); |
| | 1427 | self.compare_flags_inst = inst; |
| | 1428 | |
| | 1429 | const dest = blk: { |
| | 1430 | if (rhs_immediate_ok) { |
| | 1431 | break :blk try self.binOpImmediate(mir_tag, null, lhs, rhs, lhs_ty, false); |
| | 1432 | } else if (lhs_immediate_ok) { |
| | 1433 | // swap lhs and rhs |
| | 1434 | break :blk try self.binOpImmediate(mir_tag, null, rhs, lhs, rhs_ty, true); |
| | 1435 | } else { |
| | 1436 | break :blk try self.binOpRegister(mir_tag, null, lhs, rhs, lhs_ty, rhs_ty); |
| | 1437 | } |
| | 1438 | }; |
| | 1439 | |
| | 1440 | switch (int_info.signedness) { |
| | 1441 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, |
| | 1442 | .signed => break :result MCValue{ .register_v_flag = dest.register }, |
| | 1443 | } |
| | 1444 | } else { |
| | 1445 | return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); |
| | 1446 | } |
| | 1447 | }, |
| | 1448 | else => unreachable, |
| | 1449 | } |
| | 1450 | }; |
| | 1451 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1317 | } | 1452 | } |
| 1318 | | 1453 | |
| 1319 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1454 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1320 | _ = inst; | 1455 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1321 | return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); | 1456 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 1457 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| | 1458 | const result: MCValue = result: { |
| | 1459 | const lhs = try self.resolveInst(extra.lhs); |
| | 1460 | const rhs = try self.resolveInst(extra.rhs); |
| | 1461 | const lhs_ty = self.air.typeOf(extra.lhs); |
| | 1462 | const rhs_ty = self.air.typeOf(extra.rhs); |
| | 1463 | |
| | 1464 | const tuple_ty = self.air.typeOfIndex(inst); |
| | 1465 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| | 1466 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| | 1467 | const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); |
| | 1468 | |
| | 1469 | switch (lhs_ty.zigTypeTag()) { |
| | 1470 | .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), |
| | 1471 | .Int => { |
| | 1472 | assert(lhs_ty.eql(rhs_ty, self.target.*)); |
| | 1473 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 1474 | if (int_info.bits <= 16) { |
| | 1475 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| | 1476 | |
| | 1477 | try self.spillCompareFlagsIfOccupied(); |
| | 1478 | self.compare_flags_inst = null; |
| | 1479 | |
| | 1480 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| | 1481 | .signed => .smulbb, |
| | 1482 | .unsigned => .mul, |
| | 1483 | }; |
| | 1484 | |
| | 1485 | const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); |
| | 1486 | const dest_reg = dest.register; |
| | 1487 | self.register_manager.freezeRegs(&.{dest_reg}); |
| | 1488 | defer self.register_manager.unfreezeRegs(&.{dest_reg}); |
| | 1489 | |
| | 1490 | const truncated_reg = try self.register_manager.allocReg(null); |
| | 1491 | self.register_manager.freezeRegs(&.{truncated_reg}); |
| | 1492 | defer self.register_manager.unfreezeRegs(&.{truncated_reg}); |
| | 1493 | |
| | 1494 | // sbfx/ubfx truncated, dest, #0, #bits |
| | 1495 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| | 1496 | |
| | 1497 | // cmp dest, truncated |
| | 1498 | _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); |
| | 1499 | |
| | 1500 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| | 1501 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); |
| | 1502 | |
| | 1503 | break :result MCValue{ .stack_offset = stack_offset }; |
| | 1504 | } else if (int_info.bits <= 32) { |
| | 1505 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| | 1506 | |
| | 1507 | try self.spillCompareFlagsIfOccupied(); |
| | 1508 | self.compare_flags_inst = null; |
| | 1509 | |
| | 1510 | const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { |
| | 1511 | .signed => .smull, |
| | 1512 | .unsigned => .umull, |
| | 1513 | }; |
| | 1514 | |
| | 1515 | // TODO extract umull etc. to binOpTwoRegister |
| | 1516 | // once MCValue.rr is implemented |
| | 1517 | const lhs_is_register = lhs == .register; |
| | 1518 | const rhs_is_register = rhs == .register; |
| | 1519 | |
| | 1520 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| | 1521 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| | 1522 | |
| | 1523 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| | 1524 | const reg = try self.register_manager.allocReg(null); |
| | 1525 | self.register_manager.freezeRegs(&.{reg}); |
| | 1526 | |
| | 1527 | break :blk reg; |
| | 1528 | }; |
| | 1529 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| | 1530 | |
| | 1531 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| | 1532 | const reg = try self.register_manager.allocReg(null); |
| | 1533 | self.register_manager.freezeRegs(&.{reg}); |
| | 1534 | |
| | 1535 | break :blk reg; |
| | 1536 | }; |
| | 1537 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| | 1538 | |
| | 1539 | const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }); |
| | 1540 | self.register_manager.freezeRegs(&dest_regs); |
| | 1541 | defer self.register_manager.unfreezeRegs(&dest_regs); |
| | 1542 | const rdlo = dest_regs[0]; |
| | 1543 | const rdhi = dest_regs[1]; |
| | 1544 | |
| | 1545 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| | 1546 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| | 1547 | |
| | 1548 | const truncated_reg = try self.register_manager.allocReg(null); |
| | 1549 | self.register_manager.freezeRegs(&.{truncated_reg}); |
| | 1550 | defer self.register_manager.unfreezeRegs(&.{truncated_reg}); |
| | 1551 | |
| | 1552 | _ = try self.addInst(.{ |
| | 1553 | .tag = base_tag, |
| | 1554 | .data = .{ .rrrr = .{ |
| | 1555 | .rdlo = rdlo, |
| | 1556 | .rdhi = rdhi, |
| | 1557 | .rn = lhs_reg, |
| | 1558 | .rm = rhs_reg, |
| | 1559 | } }, |
| | 1560 | }); |
| | 1561 | |
| | 1562 | // sbfx/ubfx truncated, rdlo, #0, #bits |
| | 1563 | try self.truncRegister(rdlo, truncated_reg, int_info.signedness, int_info.bits); |
| | 1564 | |
| | 1565 | // str truncated, [...] |
| | 1566 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| | 1567 | |
| | 1568 | // cmp truncated, rdlo |
| | 1569 | _ = try self.binOp(.cmp_eq, null, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize); |
| | 1570 | |
| | 1571 | // mov rdlo, #0 |
| | 1572 | _ = try self.addInst(.{ |
| | 1573 | .tag = .mov, |
| | 1574 | .data = .{ .rr_op = .{ |
| | 1575 | .rd = rdlo, |
| | 1576 | .rn = .r0, |
| | 1577 | .op = Instruction.Operand.fromU32(0).?, |
| | 1578 | } }, |
| | 1579 | }); |
| | 1580 | |
| | 1581 | // movne rdlo, #1 |
| | 1582 | _ = try self.addInst(.{ |
| | 1583 | .tag = .mov, |
| | 1584 | .cond = .ne, |
| | 1585 | .data = .{ .rr_op = .{ |
| | 1586 | .rd = rdlo, |
| | 1587 | .rn = .r0, |
| | 1588 | .op = Instruction.Operand.fromU32(1).?, |
| | 1589 | } }, |
| | 1590 | }); |
| | 1591 | |
| | 1592 | // cmp rdhi, #0 |
| | 1593 | _ = try self.binOp(.cmp_eq, null, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize); |
| | 1594 | |
| | 1595 | // movne rdlo, #1 |
| | 1596 | _ = try self.addInst(.{ |
| | 1597 | .tag = .mov, |
| | 1598 | .cond = .ne, |
| | 1599 | .data = .{ .rr_op = .{ |
| | 1600 | .rd = rdlo, |
| | 1601 | .rn = .r0, |
| | 1602 | .op = Instruction.Operand.fromU32(1).?, |
| | 1603 | } }, |
| | 1604 | }); |
| | 1605 | |
| | 1606 | // strb rdlo, [...] |
| | 1607 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .register = rdlo }); |
| | 1608 | |
| | 1609 | break :result MCValue{ .stack_offset = stack_offset }; |
| | 1610 | } else { |
| | 1611 | return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); |
| | 1612 | } |
| | 1613 | }, |
| | 1614 | else => unreachable, |
| | 1615 | } |
| | 1616 | }; |
| | 1617 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1322 | } | 1618 | } |
| 1323 | | 1619 | |
| 1324 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1620 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1325 | _ = inst; | 1621 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1326 | return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); | 1622 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| | 1623 | if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| | 1624 | const result: MCValue = result: { |
| | 1625 | const lhs = try self.resolveInst(extra.lhs); |
| | 1626 | const rhs = try self.resolveInst(extra.rhs); |
| | 1627 | const lhs_ty = self.air.typeOf(extra.lhs); |
| | 1628 | const rhs_ty = self.air.typeOf(extra.rhs); |
| | 1629 | |
| | 1630 | const tuple_ty = self.air.typeOfIndex(inst); |
| | 1631 | const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); |
| | 1632 | const tuple_align = tuple_ty.abiAlignment(self.target.*); |
| | 1633 | const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); |
| | 1634 | |
| | 1635 | switch (lhs_ty.zigTypeTag()) { |
| | 1636 | .Vector => return self.fail("TODO implement shl_with_overflow for vectors", .{}), |
| | 1637 | .Int => { |
| | 1638 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 1639 | if (int_info.bits <= 32) { |
| | 1640 | const stack_offset = try self.allocMem(inst, tuple_size, tuple_align); |
| | 1641 | |
| | 1642 | if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register}); |
| | 1643 | defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register}); |
| | 1644 | |
| | 1645 | try self.spillCompareFlagsIfOccupied(); |
| | 1646 | self.compare_flags_inst = null; |
| | 1647 | |
| | 1648 | // lsl dest, lhs, rhs |
| | 1649 | const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); |
| | 1650 | |
| | 1651 | // asr/lsr reconstructed, dest, rhs |
| | 1652 | const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); |
| | 1653 | |
| | 1654 | // cmp lhs, reconstructed |
| | 1655 | _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); |
| | 1656 | |
| | 1657 | try self.genSetStack(lhs_ty, stack_offset, dest); |
| | 1658 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq }); |
| | 1659 | |
| | 1660 | break :result MCValue{ .stack_offset = stack_offset }; |
| | 1661 | } else { |
| | 1662 | return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); |
| | 1663 | } |
| | 1664 | }, |
| | 1665 | else => unreachable, |
| | 1666 | } |
| | 1667 | }; |
| | 1668 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1327 | } | 1669 | } |
| 1328 | | 1670 | |
| 1329 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { | 1671 | fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -1424,7 +1766,6 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) | ... | @@ -1424,7 +1766,6 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) |
| 1424 | const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); | 1766 | const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); |
| 1425 | const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); | 1767 | const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); |
| 1426 | | 1768 | |
| 1427 | // TODO optimization for small error unions: put into register | | |
| 1428 | switch (error_union_mcv) { | 1769 | switch (error_union_mcv) { |
| 1429 | .register => return self.fail("TODO errUnionPayload for registers", .{}), | 1770 | .register => return self.fail("TODO errUnionPayload for registers", .{}), |
| 1430 | .stack_argument_offset => |off| { | 1771 | .stack_argument_offset => |off| { |
| ... | @@ -1791,8 +2132,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -1791,8 +2132,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1791 | .undef => unreachable, | 2132 | .undef => unreachable, |
| 1792 | .unreach => unreachable, | 2133 | .unreach => unreachable, |
| 1793 | .dead => unreachable, | 2134 | .dead => unreachable, |
| 1794 | .compare_flags_unsigned => unreachable, | 2135 | .compare_flags_unsigned, |
| 1795 | .compare_flags_signed => unreachable, | 2136 | .compare_flags_signed, |
| | 2137 | .register_c_flag, |
| | 2138 | .register_v_flag, |
| | 2139 | => unreachable, // cannot hold an address |
| 1796 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), | 2140 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 1797 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), | 2141 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 1798 | .register => |reg| { | 2142 | .register => |reg| { |
| ... | @@ -1887,8 +2231,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -1887,8 +2231,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 1887 | .undef => unreachable, | 2231 | .undef => unreachable, |
| 1888 | .unreach => unreachable, | 2232 | .unreach => unreachable, |
| 1889 | .dead => unreachable, | 2233 | .dead => unreachable, |
| 1890 | .compare_flags_unsigned => unreachable, | 2234 | .compare_flags_unsigned, |
| 1891 | .compare_flags_signed => unreachable, | 2235 | .compare_flags_signed, |
| | 2236 | .register_c_flag, |
| | 2237 | .register_v_flag, |
| | 2238 | => unreachable, // cannot hold an address |
| 1892 | .immediate => |imm| { | 2239 | .immediate => |imm| { |
| 1893 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); | 2240 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |
| 1894 | }, | 2241 | }, |
| ... | @@ -2043,6 +2390,50 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2043,6 +2390,50 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2043 | .memory => |addr| { | 2390 | .memory => |addr| { |
| 2044 | break :result MCValue{ .memory = addr + struct_field_offset }; | 2391 | break :result MCValue{ .memory = addr + struct_field_offset }; |
| 2045 | }, | 2392 | }, |
| | 2393 | .register_c_flag, |
| | 2394 | .register_v_flag, |
| | 2395 | => |reg| { |
| | 2396 | switch (index) { |
| | 2397 | 0 => { |
| | 2398 | // get wrapped value: return register |
| | 2399 | break :result MCValue{ .register = reg }; |
| | 2400 | }, |
| | 2401 | 1 => { |
| | 2402 | // get overflow bit: set register to C flag |
| | 2403 | // resp. V flag |
| | 2404 | const dest_reg = try self.register_manager.allocReg(null); |
| | 2405 | |
| | 2406 | // mov reg, #0 |
| | 2407 | _ = try self.addInst(.{ |
| | 2408 | .tag = .mov, |
| | 2409 | .data = .{ .rr_op = .{ |
| | 2410 | .rd = dest_reg, |
| | 2411 | .rn = .r0, |
| | 2412 | .op = Instruction.Operand.fromU32(0).?, |
| | 2413 | } }, |
| | 2414 | }); |
| | 2415 | |
| | 2416 | // C flag: movcs reg, #1 |
| | 2417 | // V flag: movvs reg, #1 |
| | 2418 | _ = try self.addInst(.{ |
| | 2419 | .tag = .mov, |
| | 2420 | .cond = switch (mcv) { |
| | 2421 | .register_c_flag => .cs, |
| | 2422 | .register_v_flag => .vs, |
| | 2423 | else => unreachable, |
| | 2424 | }, |
| | 2425 | .data = .{ .rr_op = .{ |
| | 2426 | .rd = dest_reg, |
| | 2427 | .rn = .r0, |
| | 2428 | .op = Instruction.Operand.fromU32(1).?, |
| | 2429 | } }, |
| | 2430 | }); |
| | 2431 | |
| | 2432 | break :result MCValue{ .register = dest_reg }; |
| | 2433 | }, |
| | 2434 | else => unreachable, |
| | 2435 | } |
| | 2436 | }, |
| 2046 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), | 2437 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| 2047 | } | 2438 | } |
| 2048 | }; | 2439 | }; |
| ... | @@ -2067,7 +2458,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2067,7 +2458,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2067 | /// Asserts that generating an instruction of that form is possible. | 2458 | /// Asserts that generating an instruction of that form is possible. |
| 2068 | fn binOpRegister( | 2459 | fn binOpRegister( |
| 2069 | self: *Self, | 2460 | self: *Self, |
| 2070 | tag: Air.Inst.Tag, | 2461 | mir_tag: Mir.Inst.Tag, |
| 2071 | maybe_inst: ?Air.Inst.Index, | 2462 | maybe_inst: ?Air.Inst.Index, |
| 2072 | lhs: MCValue, | 2463 | lhs: MCValue, |
| 2073 | rhs: MCValue, | 2464 | rhs: MCValue, |
| ... | @@ -2112,8 +2503,8 @@ fn binOpRegister( | ... | @@ -2112,8 +2503,8 @@ fn binOpRegister( |
| 2112 | }; | 2503 | }; |
| 2113 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); | 2504 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 2114 | | 2505 | |
| 2115 | const dest_reg = switch (tag) { | 2506 | const dest_reg = switch (mir_tag) { |
| 2116 | .cmp_eq => .r0, // cmp has no destination regardless | 2507 | .cmp => .r0, // cmp has no destination regardless |
| 2117 | else => if (maybe_inst) |inst| blk: { | 2508 | else => if (maybe_inst) |inst| blk: { |
| 2118 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2509 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2119 | | 2510 | |
| ... | @@ -2130,47 +2521,31 @@ fn binOpRegister( | ... | @@ -2130,47 +2521,31 @@ fn binOpRegister( |
| 2130 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 2521 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2131 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | 2522 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2132 | | 2523 | |
| 2133 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 2524 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2134 | .add => .add, | | |
| 2135 | .sub => .sub, | | |
| 2136 | .cmp_eq => .cmp, | | |
| 2137 | .mul => .mul, | | |
| 2138 | .bit_and, | | |
| 2139 | .bool_and, | | |
| 2140 | => .@"and", | | |
| 2141 | .bit_or, | | |
| 2142 | .bool_or, | | |
| 2143 | => .orr, | | |
| 2144 | .shl_exact => .lsl, | | |
| 2145 | .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { | | |
| 2146 | .signed => Mir.Inst.Tag.asr, | | |
| 2147 | .unsigned => Mir.Inst.Tag.lsr, | | |
| 2148 | }, | | |
| 2149 | .xor => .eor, | | |
| 2150 | else => unreachable, | | |
| 2151 | }; | | |
| 2152 | const mir_data: Mir.Inst.Data = switch (tag) { | | |
| 2153 | .add, | 2525 | .add, |
| | 2526 | .adds, |
| 2154 | .sub, | 2527 | .sub, |
| 2155 | .cmp_eq, | 2528 | .subs, |
| 2156 | .bit_and, | 2529 | .cmp, |
| 2157 | .bool_and, | 2530 | .@"and", |
| 2158 | .bit_or, | 2531 | .orr, |
| 2159 | .bool_or, | 2532 | .eor, |
| 2160 | .xor, | | |
| 2161 | => .{ .rr_op = .{ | 2533 | => .{ .rr_op = .{ |
| 2162 | .rd = dest_reg, | 2534 | .rd = dest_reg, |
| 2163 | .rn = lhs_reg, | 2535 | .rn = lhs_reg, |
| 2164 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), | 2536 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), |
| 2165 | } }, | 2537 | } }, |
| 2166 | .shl_exact, | 2538 | .lsl, |
| 2167 | .shr_exact, | 2539 | .asr, |
| | 2540 | .lsr, |
| 2168 | => .{ .rr_shift = .{ | 2541 | => .{ .rr_shift = .{ |
| 2169 | .rd = dest_reg, | 2542 | .rd = dest_reg, |
| 2170 | .rm = lhs_reg, | 2543 | .rm = lhs_reg, |
| 2171 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), | 2544 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), |
| 2172 | } }, | 2545 | } }, |
| 2173 | .mul => .{ .rrr = .{ | 2546 | .mul, |
| | 2547 | .smulbb, |
| | 2548 | => .{ .rrr = .{ |
| 2174 | .rd = dest_reg, | 2549 | .rd = dest_reg, |
| 2175 | .rn = lhs_reg, | 2550 | .rn = lhs_reg, |
| 2176 | .rm = rhs_reg, | 2551 | .rm = rhs_reg, |
| ... | @@ -2200,7 +2575,7 @@ fn binOpRegister( | ... | @@ -2200,7 +2575,7 @@ fn binOpRegister( |
| 2200 | /// Asserts that generating an instruction of that form is possible. | 2575 | /// Asserts that generating an instruction of that form is possible. |
| 2201 | fn binOpImmediate( | 2576 | fn binOpImmediate( |
| 2202 | self: *Self, | 2577 | self: *Self, |
| 2203 | tag: Air.Inst.Tag, | 2578 | mir_tag: Mir.Inst.Tag, |
| 2204 | maybe_inst: ?Air.Inst.Index, | 2579 | maybe_inst: ?Air.Inst.Index, |
| 2205 | lhs: MCValue, | 2580 | lhs: MCValue, |
| 2206 | rhs: MCValue, | 2581 | rhs: MCValue, |
| ... | @@ -2230,8 +2605,8 @@ fn binOpImmediate( | ... | @@ -2230,8 +2605,8 @@ fn binOpImmediate( |
| 2230 | }; | 2605 | }; |
| 2231 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); | 2606 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 2232 | | 2607 | |
| 2233 | const dest_reg = switch (tag) { | 2608 | const dest_reg = switch (mir_tag) { |
| 2234 | .cmp_eq => .r0, // cmp has no destination reg | 2609 | .cmp => .r0, // cmp has no destination reg |
| 2235 | else => if (maybe_inst) |inst| blk: { | 2610 | else => if (maybe_inst) |inst| blk: { |
| 2236 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2611 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2237 | | 2612 | |
| ... | @@ -2250,40 +2625,23 @@ fn binOpImmediate( | ... | @@ -2250,40 +2625,23 @@ fn binOpImmediate( |
| 2250 | | 2625 | |
| 2251 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 2626 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2252 | | 2627 | |
| 2253 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 2628 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2254 | .add => .add, | | |
| 2255 | .sub => .sub, | | |
| 2256 | .cmp_eq => .cmp, | | |
| 2257 | .bit_and, | | |
| 2258 | .bool_and, | | |
| 2259 | => .@"and", | | |
| 2260 | .bit_or, | | |
| 2261 | .bool_or, | | |
| 2262 | => .orr, | | |
| 2263 | .shl_exact => .lsl, | | |
| 2264 | .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { | | |
| 2265 | .signed => Mir.Inst.Tag.asr, | | |
| 2266 | .unsigned => Mir.Inst.Tag.lsr, | | |
| 2267 | }, | | |
| 2268 | .xor => .eor, | | |
| 2269 | else => unreachable, | | |
| 2270 | }; | | |
| 2271 | const mir_data: Mir.Inst.Data = switch (tag) { | | |
| 2272 | .add, | 2629 | .add, |
| | 2630 | .adds, |
| 2273 | .sub, | 2631 | .sub, |
| 2274 | .cmp_eq, | 2632 | .subs, |
| 2275 | .bit_and, | 2633 | .cmp, |
| 2276 | .bool_and, | 2634 | .@"and", |
| 2277 | .bit_or, | 2635 | .orr, |
| 2278 | .bool_or, | 2636 | .eor, |
| 2279 | .xor, | | |
| 2280 | => .{ .rr_op = .{ | 2637 | => .{ .rr_op = .{ |
| 2281 | .rd = dest_reg, | 2638 | .rd = dest_reg, |
| 2282 | .rn = lhs_reg, | 2639 | .rn = lhs_reg, |
| 2283 | .op = Instruction.Operand.fromU32(rhs.immediate).?, | 2640 | .op = Instruction.Operand.fromU32(rhs.immediate).?, |
| 2284 | } }, | 2641 | } }, |
| 2285 | .shl_exact, | 2642 | .lsl, |
| 2286 | .shr_exact, | 2643 | .asr, |
| | 2644 | .lsr, |
| 2287 | => .{ .rr_shift = .{ | 2645 | => .{ .rr_shift = .{ |
| 2288 | .rd = dest_reg, | 2646 | .rd = dest_reg, |
| 2289 | .rm = lhs_reg, | 2647 | .rm = lhs_reg, |
| ... | @@ -2352,13 +2710,20 @@ fn binOp( | ... | @@ -2352,13 +2710,20 @@ fn binOp( |
| 2352 | else => unreachable, | 2710 | else => unreachable, |
| 2353 | }; | 2711 | }; |
| 2354 | | 2712 | |
| | 2713 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 2714 | .add => .add, |
| | 2715 | .sub => .sub, |
| | 2716 | .cmp_eq => .cmp, |
| | 2717 | else => unreachable, |
| | 2718 | }; |
| | 2719 | |
| 2355 | if (rhs_immediate_ok) { | 2720 | if (rhs_immediate_ok) { |
| 2356 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); | 2721 | return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| 2357 | } else if (lhs_immediate_ok) { | 2722 | } else if (lhs_immediate_ok) { |
| 2358 | // swap lhs and rhs | 2723 | // swap lhs and rhs |
| 2359 | return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); | 2724 | return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); |
| 2360 | } else { | 2725 | } else { |
| 2361 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | 2726 | return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 2362 | } | 2727 | } |
| 2363 | } else { | 2728 | } else { |
| 2364 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | 2729 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | @@ -2378,7 +2743,7 @@ fn binOp( | ... | @@ -2378,7 +2743,7 @@ fn binOp( |
| 2378 | // TODO add optimisations for multiplication | 2743 | // TODO add optimisations for multiplication |
| 2379 | // with immediates, for example a * 2 can be | 2744 | // with immediates, for example a * 2 can be |
| 2380 | // lowered to a << 1 | 2745 | // lowered to a << 1 |
| 2381 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | 2746 | return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 2382 | } else { | 2747 | } else { |
| 2383 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | 2748 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| 2384 | } | 2749 | } |
| ... | @@ -2432,13 +2797,20 @@ fn binOp( | ... | @@ -2432,13 +2797,20 @@ fn binOp( |
| 2432 | const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; | 2797 | const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; |
| 2433 | const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null; | 2798 | const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null; |
| 2434 | | 2799 | |
| | 2800 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 2801 | .bit_and => .@"and", |
| | 2802 | .bit_or => .orr, |
| | 2803 | .xor => .eor, |
| | 2804 | else => unreachable, |
| | 2805 | }; |
| | 2806 | |
| 2435 | if (rhs_immediate_ok) { | 2807 | if (rhs_immediate_ok) { |
| 2436 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); | 2808 | return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| 2437 | } else if (lhs_immediate_ok) { | 2809 | } else if (lhs_immediate_ok) { |
| 2438 | // swap lhs and rhs | 2810 | // swap lhs and rhs |
| 2439 | return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); | 2811 | return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); |
| 2440 | } else { | 2812 | } else { |
| 2441 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | 2813 | return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 2442 | } | 2814 | } |
| 2443 | } else { | 2815 | } else { |
| 2444 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | 2816 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | @@ -2457,10 +2829,19 @@ fn binOp( | ... | @@ -2457,10 +2829,19 @@ fn binOp( |
| 2457 | if (int_info.bits <= 32) { | 2829 | if (int_info.bits <= 32) { |
| 2458 | const rhs_immediate_ok = rhs == .immediate; | 2830 | const rhs_immediate_ok = rhs == .immediate; |
| 2459 | | 2831 | |
| | 2832 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 2833 | .shl_exact => .lsl, |
| | 2834 | .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) { |
| | 2835 | .signed => Mir.Inst.Tag.asr, |
| | 2836 | .unsigned => Mir.Inst.Tag.lsr, |
| | 2837 | }, |
| | 2838 | else => unreachable, |
| | 2839 | }; |
| | 2840 | |
| 2460 | if (rhs_immediate_ok) { | 2841 | if (rhs_immediate_ok) { |
| 2461 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); | 2842 | return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| 2462 | } else { | 2843 | } else { |
| 2463 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | 2844 | return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 2464 | } | 2845 | } |
| 2465 | } else { | 2846 | } else { |
| 2466 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | 2847 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | @@ -2512,13 +2893,19 @@ fn binOp( | ... | @@ -2512,13 +2893,19 @@ fn binOp( |
| 2512 | const lhs_immediate_ok = lhs == .immediate; | 2893 | const lhs_immediate_ok = lhs == .immediate; |
| 2513 | const rhs_immediate_ok = rhs == .immediate; | 2894 | const rhs_immediate_ok = rhs == .immediate; |
| 2514 | | 2895 | |
| | 2896 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 2897 | .bool_and => .@"and", |
| | 2898 | .bool_or => .orr, |
| | 2899 | else => unreachable, |
| | 2900 | }; |
| | 2901 | |
| 2515 | if (rhs_immediate_ok) { | 2902 | if (rhs_immediate_ok) { |
| 2516 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); | 2903 | return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| 2517 | } else if (lhs_immediate_ok) { | 2904 | } else if (lhs_immediate_ok) { |
| 2518 | // swap lhs and rhs | 2905 | // swap lhs and rhs |
| 2519 | return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); | 2906 | return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); |
| 2520 | } else { | 2907 | } else { |
| 2521 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | 2908 | return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 2522 | } | 2909 | } |
| 2523 | }, | 2910 | }, |
| 2524 | else => unreachable, | 2911 | else => unreachable, |
| ... | @@ -2537,7 +2924,7 @@ fn binOp( | ... | @@ -2537,7 +2924,7 @@ fn binOp( |
| 2537 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); | 2924 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 2538 | | 2925 | |
| 2539 | if (elem_size == 1) { | 2926 | if (elem_size == 1) { |
| 2540 | const base_tag: Air.Inst.Tag = switch (tag) { | 2927 | const base_tag: Mir.Inst.Tag = switch (tag) { |
| 2541 | .ptr_add => .add, | 2928 | .ptr_add => .add, |
| 2542 | .ptr_sub => .sub, | 2929 | .ptr_sub => .sub, |
| 2543 | else => unreachable, | 2930 | else => unreachable, |
| ... | @@ -2824,14 +3211,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2824,14 +3211,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2824 | | 3211 | |
| 2825 | switch (mc_arg) { | 3212 | switch (mc_arg) { |
| 2826 | .none => continue, | 3213 | .none => continue, |
| 2827 | .undef => unreachable, | | |
| 2828 | .immediate => unreachable, | | |
| 2829 | .unreach => unreachable, | | |
| 2830 | .dead => unreachable, | | |
| 2831 | .memory => unreachable, | | |
| 2832 | .compare_flags_signed => unreachable, | | |
| 2833 | .compare_flags_unsigned => unreachable, | | |
| 2834 | .ptr_stack_offset => unreachable, | | |
| 2835 | .register => |reg| { | 3214 | .register => |reg| { |
| 2836 | try self.register_manager.getReg(reg, null); | 3215 | try self.register_manager.getReg(reg, null); |
| 2837 | try self.genSetReg(arg_ty, reg, arg_mcv); | 3216 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | @@ -2842,6 +3221,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -2842,6 +3221,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2842 | info.stack_byte_count - offset, | 3221 | info.stack_byte_count - offset, |
| 2843 | arg_mcv, | 3222 | arg_mcv, |
| 2844 | ), | 3223 | ), |
| | 3224 | else => unreachable, |
| 2845 | } | 3225 | } |
| 2846 | } | 3226 | } |
| 2847 | | 3227 | |
| ... | @@ -3784,6 +4164,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -3784,6 +4164,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3784 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), | 4164 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 3785 | } | 4165 | } |
| 3786 | }, | 4166 | }, |
| | 4167 | .register_c_flag, |
| | 4168 | .register_v_flag, |
| | 4169 | => { |
| | 4170 | return self.fail("TODO implement genSetStack {}", .{mcv}); |
| | 4171 | }, |
| 3787 | .memory, | 4172 | .memory, |
| 3788 | .stack_argument_offset, | 4173 | .stack_argument_offset, |
| 3789 | .stack_offset, | 4174 | .stack_offset, |
| ... | @@ -4025,6 +4410,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -4025,6 +4410,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4025 | } }, | 4410 | } }, |
| 4026 | }); | 4411 | }); |
| 4027 | }, | 4412 | }, |
| | 4413 | .register_c_flag => unreachable, // doesn't fit into a register |
| | 4414 | .register_v_flag => unreachable, // doesn't fit into a register |
| 4028 | .memory => |addr| { | 4415 | .memory => |addr| { |
| 4029 | // The value is in memory at a hard-coded address. | 4416 | // The value is in memory at a hard-coded address. |
| 4030 | // If the type is a pointer, it means the pointer address is at this memory location. | 4417 | // If the type is a pointer, it means the pointer address is at this memory location. |
| ... | @@ -4159,6 +4546,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I | ... | @@ -4159,6 +4546,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4159 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), | 4546 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 4160 | } | 4547 | } |
| 4161 | }, | 4548 | }, |
| | 4549 | .register_c_flag, |
| | 4550 | .register_v_flag, |
| | 4551 | => { |
| | 4552 | return self.fail("TODO implement genSetStack {}", .{mcv}); |
| | 4553 | }, |
| 4162 | .stack_offset, | 4554 | .stack_offset, |
| 4163 | .memory, | 4555 | .memory, |
| 4164 | .stack_argument_offset, | 4556 | .stack_argument_offset, |