| ... | ... | @@ -521,12 +521,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 521 | 521 | |
| 522 | 522 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 523 | 523 | |
| 524 | | .cmp_lt => @panic("TODO try self.airCmp(inst, .lt)"), |
| 525 | | .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"), |
| 526 | | .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"), |
| 527 | | .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"), |
| 528 | | .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"), |
| 529 | | .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"), |
| 524 | .cmp_lt => try self.airCmp(inst, .lt), |
| 525 | .cmp_lte => try self.airCmp(inst, .lte), |
| 526 | .cmp_eq => try self.airCmp(inst, .eq), |
| 527 | .cmp_gte => try self.airCmp(inst, .gte), |
| 528 | .cmp_gt => try self.airCmp(inst, .gt), |
| 529 | .cmp_neq => try self.airCmp(inst, .neq), |
| 530 | 530 | .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), |
| 531 | 531 | .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"), |
| 532 | 532 | |
| ... | ... | @@ -996,6 +996,54 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 996 | 996 | @panic("TODO handle return value with BigTomb"); |
| 997 | 997 | } |
| 998 | 998 | |
| 999 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1000 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1001 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1002 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1003 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1004 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1005 | |
| 1006 | var int_buffer: Type.Payload.Bits = undefined; |
| 1007 | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| 1008 | .Vector => unreachable, // Should be handled by cmp_vector? |
| 1009 | .Enum => lhs_ty.intTagType(&int_buffer), |
| 1010 | .Int => lhs_ty, |
| 1011 | .Bool => Type.initTag(.u1), |
| 1012 | .Pointer => Type.usize, |
| 1013 | .ErrorSet => Type.initTag(.u16), |
| 1014 | .Optional => blk: { |
| 1015 | var opt_buffer: Type.Payload.ElemType = undefined; |
| 1016 | const payload_ty = lhs_ty.optionalChild(&opt_buffer); |
| 1017 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 1018 | break :blk Type.initTag(.u1); |
| 1019 | } else if (lhs_ty.isPtrLikeOptional()) { |
| 1020 | break :blk Type.usize; |
| 1021 | } else { |
| 1022 | return self.fail("TODO SPARCv9 cmp non-pointer optionals", .{}); |
| 1023 | } |
| 1024 | }, |
| 1025 | .Float => return self.fail("TODO SPARCv9 cmp floats", .{}), |
| 1026 | else => unreachable, |
| 1027 | }; |
| 1028 | |
| 1029 | const int_info = int_ty.intInfo(self.target.*); |
| 1030 | if (int_info.bits <= 64) { |
| 1031 | _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); |
| 1032 | |
| 1033 | try self.spillCompareFlagsIfOccupied(); |
| 1034 | self.compare_flags_inst = inst; |
| 1035 | |
| 1036 | break :result switch (int_info.signedness) { |
| 1037 | .signed => MCValue{ .compare_flags_signed = op }, |
| 1038 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 1039 | }; |
| 1040 | } else { |
| 1041 | return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{}); |
| 1042 | } |
| 1043 | }; |
| 1044 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1045 | } |
| 1046 | |
| 999 | 1047 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1000 | 1048 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1001 | 1049 | const cond = try self.resolveInst(pl_op.operand); |
| ... | ... | @@ -1427,6 +1475,149 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1427 | 1475 | return MCValue{ .stack_offset = stack_offset }; |
| 1428 | 1476 | } |
| 1429 | 1477 | |
| 1478 | /// For all your binary operation needs, this function will generate |
| 1479 | /// the corresponding Mir instruction(s). Returns the location of the |
| 1480 | /// result. |
| 1481 | /// |
| 1482 | /// If the binary operation itself happens to be an Air instruction, |
| 1483 | /// pass the corresponding index in the inst parameter. That helps |
| 1484 | /// this function do stuff like reusing operands. |
| 1485 | /// |
| 1486 | /// This function does not do any lowering to Mir itself, but instead |
| 1487 | /// looks at the lhs and rhs and determines which kind of lowering |
| 1488 | /// would be best suitable and then delegates the lowering to other |
| 1489 | /// functions. |
| 1490 | fn binOp( |
| 1491 | self: *Self, |
| 1492 | tag: Air.Inst.Tag, |
| 1493 | maybe_inst: ?Air.Inst.Index, |
| 1494 | lhs: MCValue, |
| 1495 | rhs: MCValue, |
| 1496 | lhs_ty: Type, |
| 1497 | rhs_ty: Type, |
| 1498 | ) InnerError!MCValue { |
| 1499 | const mod = self.bin_file.options.module.?; |
| 1500 | switch (tag) { |
| 1501 | .cmp_eq => { |
| 1502 | switch (lhs_ty.zigTypeTag()) { |
| 1503 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1504 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1505 | .Int => { |
| 1506 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1507 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1508 | if (int_info.bits <= 64) { |
| 1509 | // TODO optimize for small (i13) values by putting them inside immediates |
| 1510 | |
| 1511 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1512 | .cmp_eq => .subcc, |
| 1513 | else => unreachable, |
| 1514 | }; |
| 1515 | |
| 1516 | return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1517 | } else { |
| 1518 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1519 | } |
| 1520 | }, |
| 1521 | else => unreachable, |
| 1522 | } |
| 1523 | }, |
| 1524 | |
| 1525 | else => return self.fail("TODO implement {} binOp for SPARCv9", .{tag}), |
| 1526 | } |
| 1527 | } |
| 1528 | |
| 1529 | /// Don't call this function directly. Use binOp instead. |
| 1530 | /// |
| 1531 | /// Calling this function signals an intention to generate a Mir |
| 1532 | /// instruction of the form |
| 1533 | /// |
| 1534 | /// op dest, lhs, rhs |
| 1535 | /// |
| 1536 | /// Asserts that generating an instruction of that form is possible. |
| 1537 | fn binOpRegister( |
| 1538 | self: *Self, |
| 1539 | mir_tag: Mir.Inst.Tag, |
| 1540 | maybe_inst: ?Air.Inst.Index, |
| 1541 | lhs: MCValue, |
| 1542 | rhs: MCValue, |
| 1543 | lhs_ty: Type, |
| 1544 | rhs_ty: Type, |
| 1545 | ) !MCValue { |
| 1546 | const lhs_is_register = lhs == .register; |
| 1547 | const rhs_is_register = rhs == .register; |
| 1548 | |
| 1549 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 1550 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 1551 | |
| 1552 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1553 | |
| 1554 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1555 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 1556 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1557 | break :inst Air.refToIndex(bin_op.lhs).?; |
| 1558 | } else null; |
| 1559 | |
| 1560 | const reg = try self.register_manager.allocReg(track_inst); |
| 1561 | self.register_manager.freezeRegs(&.{reg}); |
| 1562 | |
| 1563 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1564 | |
| 1565 | break :blk reg; |
| 1566 | }; |
| 1567 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 1568 | |
| 1569 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 1570 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 1571 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1572 | break :inst Air.refToIndex(bin_op.rhs).?; |
| 1573 | } else null; |
| 1574 | |
| 1575 | const reg = try self.register_manager.allocReg(track_inst); |
| 1576 | self.register_manager.freezeRegs(&.{reg}); |
| 1577 | |
| 1578 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1579 | |
| 1580 | break :blk reg; |
| 1581 | }; |
| 1582 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1583 | |
| 1584 | const dest_reg = switch (mir_tag) { |
| 1585 | else => if (maybe_inst) |inst| blk: { |
| 1586 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1587 | |
| 1588 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 1589 | break :blk lhs_reg; |
| 1590 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 1591 | break :blk rhs_reg; |
| 1592 | } else { |
| 1593 | break :blk try self.register_manager.allocReg(inst); |
| 1594 | } |
| 1595 | } else blk: { |
| 1596 | break :blk try self.register_manager.allocReg(null); |
| 1597 | }, |
| 1598 | }; |
| 1599 | |
| 1600 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1601 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 1602 | |
| 1603 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1604 | .subcc => .{ .arithmetic_3op = .{ |
| 1605 | .is_imm = false, |
| 1606 | .rd = dest_reg, |
| 1607 | .rs1 = lhs_reg, |
| 1608 | .rs2_or_imm = .{ .rs2 = rhs_reg }, |
| 1609 | } }, |
| 1610 | else => unreachable, |
| 1611 | }; |
| 1612 | |
| 1613 | _ = try self.addInst(.{ |
| 1614 | .tag = mir_tag, |
| 1615 | .data = mir_data, |
| 1616 | }); |
| 1617 | |
| 1618 | return MCValue{ .register = dest_reg }; |
| 1619 | } |
| 1620 | |
| 1430 | 1621 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 1431 | 1622 | const block_data = self.blocks.getPtr(block).?; |
| 1432 | 1623 | |
| ... | ... | @@ -2146,6 +2337,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 2146 | 2337 | .register => |reg| { |
| 2147 | 2338 | self.register_manager.freeReg(reg); |
| 2148 | 2339 | }, |
| 2340 | .compare_flags_signed, .compare_flags_unsigned => { |
| 2341 | self.compare_flags_inst = null; |
| 2342 | }, |
| 2149 | 2343 | else => {}, // TODO process stack allocation death |
| 2150 | 2344 | } |
| 2151 | 2345 | } |
| ... | ... | @@ -2338,6 +2532,29 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { |
| 2338 | 2532 | } |
| 2339 | 2533 | } |
| 2340 | 2534 | |
| 2535 | /// Save the current instruction stored in the compare flags if |
| 2536 | /// occupied |
| 2537 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 2538 | if (self.compare_flags_inst) |inst_to_save| { |
| 2539 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 2540 | switch (mcv) { |
| 2541 | .compare_flags_signed, |
| 2542 | .compare_flags_unsigned, |
| 2543 | => {}, |
| 2544 | else => unreachable, // mcv doesn't occupy the compare flags |
| 2545 | } |
| 2546 | |
| 2547 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 2548 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| 2549 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); |
| 2550 | |
| 2551 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 2552 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| 2553 | |
| 2554 | self.compare_flags_inst = null; |
| 2555 | } |
| 2556 | } |
| 2557 | |
| 2341 | 2558 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 2342 | 2559 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 2343 | 2560 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |