| ... | ... | @@ -105,9 +105,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 105 | 105 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 106 | 106 | |
| 107 | 107 | const MCValue = union(enum) { |
| 108 | | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. |
| 109 | | /// TODO Look into deleting this tag and using `dead` instead, since every use |
| 110 | | /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. |
| 108 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 |
| 109 | /// tag, etc. |
| 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 | 114 | none, |
| 112 | 115 | /// Control flow will not allow this value to be observed. |
| 113 | 116 | unreach, |
| ... | ... | @@ -116,20 +119,41 @@ const MCValue = union(enum) { |
| 116 | 119 | /// The value is undefined. |
| 117 | 120 | undef, |
| 118 | 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 | 125 | immediate: u32, |
| 121 | 126 | /// The value is in a target-specific register. |
| 122 | 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 | 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 | 146 | memory: u64, |
| 126 | 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 | 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 | 154 | ptr_stack_offset: u32, |
| 131 | | /// The value is in the compare flags assuming an unsigned operation, |
| 132 | | /// with this operator applied on top of it. |
| 155 | /// The value is in the compare flags assuming an unsigned |
| 156 | /// operation, with this operator applied on top of it. |
| 133 | 157 | compare_flags_unsigned: math.CompareOperator, |
| 134 | 158 | /// The value is in the compare flags assuming a signed operation, |
| 135 | 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 | 578 | .trunc_float, |
| 555 | 579 | => try self.airUnaryMath(inst), |
| 556 | 580 | |
| 557 | | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 558 | | .sub_with_overflow => try self.airSubWithOverflow(inst), |
| 581 | .add_with_overflow => try self.airOverflow(inst), |
| 582 | .sub_with_overflow => try self.airOverflow(inst), |
| 559 | 583 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 560 | 584 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 561 | 585 | |
| ... | ... | @@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 726 | 750 | .register => |reg| { |
| 727 | 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 | 759 | .compare_flags_signed, .compare_flags_unsigned => { |
| 730 | 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 | 871 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 842 | 872 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 843 | 873 | log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); |
| 874 | |
| 844 | 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 | 884 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 847 | 885 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 848 | 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 | 891 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 854 | 892 | if (self.compare_flags_inst) |inst_to_save| { |
| 855 | 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 | 903 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 859 | 904 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| ... | ... | @@ -1196,7 +1241,7 @@ fn minMax( |
| 1196 | 1241 | // register. |
| 1197 | 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 | 1246 | const cond_choose_lhs: Condition = switch (tag) { |
| 1202 | 1247 | .max => switch (int_info.signedness) { |
| ... | ... | @@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1268 | 1313 | const len = try self.resolveInst(bin_op.rhs); |
| 1269 | 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 | 1317 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 1273 | 1318 | try self.genSetStack(len_ty, stack_offset - 4, len); |
| 1274 | 1319 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -1306,24 +1351,321 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1306 | 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 { |
| 1310 | | _ = inst; |
| 1311 | | return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); |
| 1312 | | } |
| 1354 | fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1355 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1356 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 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 { |
| 1315 | | _ = inst; |
| 1316 | | return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); |
| 1420 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1421 | .add_with_overflow => .adds, |
| 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 | 1454 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1320 | | _ = inst; |
| 1321 | | return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); |
| 1455 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 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 | 1620 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1325 | | _ = inst; |
| 1326 | | return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); |
| 1621 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 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 | 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 | 1766 | const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); |
| 1425 | 1767 | const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); |
| 1426 | 1768 | |
| 1427 | | // TODO optimization for small error unions: put into register |
| 1428 | 1769 | switch (error_union_mcv) { |
| 1429 | 1770 | .register => return self.fail("TODO errUnionPayload for registers", .{}), |
| 1430 | 1771 | .stack_argument_offset => |off| { |
| ... | ... | @@ -1791,8 +2132,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1791 | 2132 | .undef => unreachable, |
| 1792 | 2133 | .unreach => unreachable, |
| 1793 | 2134 | .dead => unreachable, |
| 1794 | | .compare_flags_unsigned => unreachable, |
| 1795 | | .compare_flags_signed => unreachable, |
| 2135 | .compare_flags_unsigned, |
| 2136 | .compare_flags_signed, |
| 2137 | .register_c_flag, |
| 2138 | .register_v_flag, |
| 2139 | => unreachable, // cannot hold an address |
| 1796 | 2140 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 1797 | 2141 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 1798 | 2142 | .register => |reg| { |
| ... | ... | @@ -1887,8 +2231,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 1887 | 2231 | .undef => unreachable, |
| 1888 | 2232 | .unreach => unreachable, |
| 1889 | 2233 | .dead => unreachable, |
| 1890 | | .compare_flags_unsigned => unreachable, |
| 1891 | | .compare_flags_signed => unreachable, |
| 2234 | .compare_flags_unsigned, |
| 2235 | .compare_flags_signed, |
| 2236 | .register_c_flag, |
| 2237 | .register_v_flag, |
| 2238 | => unreachable, // cannot hold an address |
| 1892 | 2239 | .immediate => |imm| { |
| 1893 | 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 | 2390 | .memory => |addr| { |
| 2044 | 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 | 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 | 2458 | /// Asserts that generating an instruction of that form is possible. |
| 2068 | 2459 | fn binOpRegister( |
| 2069 | 2460 | self: *Self, |
| 2070 | | tag: Air.Inst.Tag, |
| 2461 | mir_tag: Mir.Inst.Tag, |
| 2071 | 2462 | maybe_inst: ?Air.Inst.Index, |
| 2072 | 2463 | lhs: MCValue, |
| 2073 | 2464 | rhs: MCValue, |
| ... | ... | @@ -2112,8 +2503,8 @@ fn binOpRegister( |
| 2112 | 2503 | }; |
| 2113 | 2504 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 2114 | 2505 | |
| 2115 | | const dest_reg = switch (tag) { |
| 2116 | | .cmp_eq => .r0, // cmp has no destination regardless |
| 2506 | const dest_reg = switch (mir_tag) { |
| 2507 | .cmp => .r0, // cmp has no destination regardless |
| 2117 | 2508 | else => if (maybe_inst) |inst| blk: { |
| 2118 | 2509 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2119 | 2510 | |
| ... | ... | @@ -2130,47 +2521,31 @@ fn binOpRegister( |
| 2130 | 2521 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2131 | 2522 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2132 | 2523 | |
| 2133 | | const mir_tag: Mir.Inst.Tag = switch (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) { |
| 2524 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2153 | 2525 | .add, |
| 2526 | .adds, |
| 2154 | 2527 | .sub, |
| 2155 | | .cmp_eq, |
| 2156 | | .bit_and, |
| 2157 | | .bool_and, |
| 2158 | | .bit_or, |
| 2159 | | .bool_or, |
| 2160 | | .xor, |
| 2528 | .subs, |
| 2529 | .cmp, |
| 2530 | .@"and", |
| 2531 | .orr, |
| 2532 | .eor, |
| 2161 | 2533 | => .{ .rr_op = .{ |
| 2162 | 2534 | .rd = dest_reg, |
| 2163 | 2535 | .rn = lhs_reg, |
| 2164 | 2536 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), |
| 2165 | 2537 | } }, |
| 2166 | | .shl_exact, |
| 2167 | | .shr_exact, |
| 2538 | .lsl, |
| 2539 | .asr, |
| 2540 | .lsr, |
| 2168 | 2541 | => .{ .rr_shift = .{ |
| 2169 | 2542 | .rd = dest_reg, |
| 2170 | 2543 | .rm = lhs_reg, |
| 2171 | 2544 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), |
| 2172 | 2545 | } }, |
| 2173 | | .mul => .{ .rrr = .{ |
| 2546 | .mul, |
| 2547 | .smulbb, |
| 2548 | => .{ .rrr = .{ |
| 2174 | 2549 | .rd = dest_reg, |
| 2175 | 2550 | .rn = lhs_reg, |
| 2176 | 2551 | .rm = rhs_reg, |
| ... | ... | @@ -2200,7 +2575,7 @@ fn binOpRegister( |
| 2200 | 2575 | /// Asserts that generating an instruction of that form is possible. |
| 2201 | 2576 | fn binOpImmediate( |
| 2202 | 2577 | self: *Self, |
| 2203 | | tag: Air.Inst.Tag, |
| 2578 | mir_tag: Mir.Inst.Tag, |
| 2204 | 2579 | maybe_inst: ?Air.Inst.Index, |
| 2205 | 2580 | lhs: MCValue, |
| 2206 | 2581 | rhs: MCValue, |
| ... | ... | @@ -2230,8 +2605,8 @@ fn binOpImmediate( |
| 2230 | 2605 | }; |
| 2231 | 2606 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 2232 | 2607 | |
| 2233 | | const dest_reg = switch (tag) { |
| 2234 | | .cmp_eq => .r0, // cmp has no destination reg |
| 2608 | const dest_reg = switch (mir_tag) { |
| 2609 | .cmp => .r0, // cmp has no destination reg |
| 2235 | 2610 | else => if (maybe_inst) |inst| blk: { |
| 2236 | 2611 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2237 | 2612 | |
| ... | ... | @@ -2250,40 +2625,23 @@ fn binOpImmediate( |
| 2250 | 2625 | |
| 2251 | 2626 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2252 | 2627 | |
| 2253 | | const mir_tag: Mir.Inst.Tag = switch (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) { |
| 2628 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2272 | 2629 | .add, |
| 2630 | .adds, |
| 2273 | 2631 | .sub, |
| 2274 | | .cmp_eq, |
| 2275 | | .bit_and, |
| 2276 | | .bool_and, |
| 2277 | | .bit_or, |
| 2278 | | .bool_or, |
| 2279 | | .xor, |
| 2632 | .subs, |
| 2633 | .cmp, |
| 2634 | .@"and", |
| 2635 | .orr, |
| 2636 | .eor, |
| 2280 | 2637 | => .{ .rr_op = .{ |
| 2281 | 2638 | .rd = dest_reg, |
| 2282 | 2639 | .rn = lhs_reg, |
| 2283 | 2640 | .op = Instruction.Operand.fromU32(rhs.immediate).?, |
| 2284 | 2641 | } }, |
| 2285 | | .shl_exact, |
| 2286 | | .shr_exact, |
| 2642 | .lsl, |
| 2643 | .asr, |
| 2644 | .lsr, |
| 2287 | 2645 | => .{ .rr_shift = .{ |
| 2288 | 2646 | .rd = dest_reg, |
| 2289 | 2647 | .rm = lhs_reg, |
| ... | ... | @@ -2352,13 +2710,20 @@ fn binOp( |
| 2352 | 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 | 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 | 2722 | } else if (lhs_immediate_ok) { |
| 2358 | 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 | 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 | 2728 | } else { |
| 2364 | 2729 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | ... | @@ -2378,7 +2743,7 @@ fn binOp( |
| 2378 | 2743 | // TODO add optimisations for multiplication |
| 2379 | 2744 | // with immediates, for example a * 2 can be |
| 2380 | 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 | 2747 | } else { |
| 2383 | 2748 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| 2384 | 2749 | } |
| ... | ... | @@ -2432,13 +2797,20 @@ fn binOp( |
| 2432 | 2797 | const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; |
| 2433 | 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 | 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 | 2809 | } else if (lhs_immediate_ok) { |
| 2438 | 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 | 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 | 2815 | } else { |
| 2444 | 2816 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | ... | @@ -2457,10 +2829,19 @@ fn binOp( |
| 2457 | 2829 | if (int_info.bits <= 32) { |
| 2458 | 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 | 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 | 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 | 2846 | } else { |
| 2466 | 2847 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | ... | @@ -2512,13 +2893,19 @@ fn binOp( |
| 2512 | 2893 | const lhs_immediate_ok = lhs == .immediate; |
| 2513 | 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 | 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 | 2904 | } else if (lhs_immediate_ok) { |
| 2518 | 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 | 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 | 2911 | else => unreachable, |
| ... | ... | @@ -2537,7 +2924,7 @@ fn binOp( |
| 2537 | 2924 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 2538 | 2925 | |
| 2539 | 2926 | if (elem_size == 1) { |
| 2540 | | const base_tag: Air.Inst.Tag = switch (tag) { |
| 2927 | const base_tag: Mir.Inst.Tag = switch (tag) { |
| 2541 | 2928 | .ptr_add => .add, |
| 2542 | 2929 | .ptr_sub => .sub, |
| 2543 | 2930 | else => unreachable, |
| ... | ... | @@ -2824,14 +3211,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2824 | 3211 | |
| 2825 | 3212 | switch (mc_arg) { |
| 2826 | 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 | 3214 | .register => |reg| { |
| 2836 | 3215 | try self.register_manager.getReg(reg, null); |
| 2837 | 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 | 3221 | info.stack_byte_count - offset, |
| 2843 | 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 | 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 | 4172 | .memory, |
| 3788 | 4173 | .stack_argument_offset, |
| 3789 | 4174 | .stack_offset, |
| ... | ... | @@ -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 | 4415 | .memory => |addr| { |
| 4029 | 4416 | // The value is in memory at a hard-coded address. |
| 4030 | 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 | 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 | 4554 | .stack_offset, |
| 4163 | 4555 | .memory, |
| 4164 | 4556 | .stack_argument_offset, |