| ... | @@ -521,12 +521,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -521,12 +521,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 521 | | 521 | |
| 522 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), | 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)"), | 524 | .cmp_lt => try self.airCmp(inst, .lt), |
| 525 | .cmp_lte => @panic("TODO try self.airCmp(inst, .lte)"), | 525 | .cmp_lte => try self.airCmp(inst, .lte), |
| 526 | .cmp_eq => @panic("TODO try self.airCmp(inst, .eq)"), | 526 | .cmp_eq => try self.airCmp(inst, .eq), |
| 527 | .cmp_gte => @panic("TODO try self.airCmp(inst, .gte)"), | 527 | .cmp_gte => try self.airCmp(inst, .gte), |
| 528 | .cmp_gt => @panic("TODO try self.airCmp(inst, .gt)"), | 528 | .cmp_gt => try self.airCmp(inst, .gt), |
| 529 | .cmp_neq => @panic("TODO try self.airCmp(inst, .neq)"), | 529 | .cmp_neq => try self.airCmp(inst, .neq), |
| 530 | .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), | 530 | .cmp_vector => @panic("TODO try self.airCmpVector(inst)"), |
| 531 | .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"), | 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,6 +996,54 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 996 | @panic("TODO handle return value with BigTomb"); | 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 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | 1047 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1000 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 1048 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1001 | const cond = try self.resolveInst(pl_op.operand); | 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,6 +1475,149 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1427 | return MCValue{ .stack_offset = stack_offset }; | 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 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { | 1621 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 1431 | const block_data = self.blocks.getPtr(block).?; | 1622 | const block_data = self.blocks.getPtr(block).?; |
| 1432 | | 1623 | |
| ... | @@ -2146,6 +2337,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { | ... | @@ -2146,6 +2337,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 2146 | .register => |reg| { | 2337 | .register => |reg| { |
| 2147 | self.register_manager.freeReg(reg); | 2338 | self.register_manager.freeReg(reg); |
| 2148 | }, | 2339 | }, |
| | 2340 | .compare_flags_signed, .compare_flags_unsigned => { |
| | 2341 | self.compare_flags_inst = null; |
| | 2342 | }, |
| 2149 | else => {}, // TODO process stack allocation death | 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,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 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { | 2558 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 2342 | const stack_mcv = try self.allocRegOrMem(inst, false); | 2559 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 2343 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); | 2560 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |