| ... | @@ -813,6 +813,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live | ... | @@ -813,6 +813,17 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 813 | self.register_manager.getRegAssumeFree(reg, inst); | 813 | self.register_manager.getRegAssumeFree(reg, inst); |
| 814 | } | 814 | } |
| 815 | }, | 815 | }, |
| | 816 | .register_c_flag, |
| | 817 | .register_v_flag, |
| | 818 | => |reg| { |
| | 819 | if (self.register_manager.isRegFree(reg)) { |
| | 820 | self.register_manager.getRegAssumeFree(reg, inst); |
| | 821 | } |
| | 822 | self.cpsr_flags_inst = inst; |
| | 823 | }, |
| | 824 | .cpsr_flags => { |
| | 825 | self.cpsr_flags_inst = inst; |
| | 826 | }, |
| 816 | else => {}, | 827 | else => {}, |
| 817 | } | 828 | } |
| 818 | } | 829 | } |
| ... | @@ -942,15 +953,6 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { | ... | @@ -942,15 +953,6 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 942 | return reg; | 953 | return reg; |
| 943 | } | 954 | } |
| 944 | | 955 | |
| 945 | /// Allocates a new register and copies `mcv` into it. | | |
| 946 | /// `reg_owner` is the instruction that gets associated with the register in the register table. | | |
| 947 | /// This can have a side effect of spilling instructions to the stack to free up a register. | | |
| 948 | fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { | | |
| 949 | const reg = try self.register_manager.allocReg(reg_owner, gp); | | |
| 950 | try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); | | |
| 951 | return MCValue{ .register = reg }; | | |
| 952 | } | | |
| 953 | | | |
| 954 | fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { | 956 | fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { |
| 955 | const stack_offset = try self.allocMemPtr(inst); | 957 | const stack_offset = try self.allocMemPtr(inst); |
| 956 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); | 958 | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| ... | @@ -2174,6 +2176,9 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind | ... | @@ -2174,6 +2176,9 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind |
| 2174 | .stack_offset => |off| { | 2176 | .stack_offset => |off| { |
| 2175 | log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); | 2177 | log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); |
| 2176 | }, | 2178 | }, |
| | 2179 | .cpsr_flags => { |
| | 2180 | log.debug("%{d} => cpsr_flags (reused)", .{inst}); |
| | 2181 | }, |
| 2177 | else => return false, | 2182 | else => return false, |
| 2178 | } | 2183 | } |
| 2179 | | 2184 | |
| ... | @@ -2462,56 +2467,28 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2462,56 +2467,28 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2462 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); | 2467 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| 2463 | defer self.register_manager.unlockReg(reg_lock); | 2468 | defer self.register_manager.unlockReg(reg_lock); |
| 2464 | | 2469 | |
| 2465 | switch (index) { | 2470 | const field: MCValue = switch (index) { |
| 2466 | 0 => { | 2471 | // get wrapped value: return register |
| 2467 | // get wrapped value: return register | 2472 | 0 => MCValue{ .register = reg }, |
| 2468 | break :result MCValue{ .register = reg }; | | |
| 2469 | }, | | |
| 2470 | 1 => { | | |
| 2471 | // get overflow bit: return C or V flag | | |
| 2472 | if (self.liveness.operandDies(inst, 0)) { | | |
| 2473 | self.cpsr_flags_inst = inst; | | |
| 2474 | | | |
| 2475 | const cond: Condition = switch (mcv) { | | |
| 2476 | .register_c_flag => .cs, | | |
| 2477 | .register_v_flag => .vs, | | |
| 2478 | else => unreachable, | | |
| 2479 | }; | | |
| 2480 | | 2473 | |
| 2481 | break :result MCValue{ .cpsr_flags = cond }; | 2474 | // get overflow bit: return C or V flag |
| 2482 | } else { | 2475 | 1 => MCValue{ .cpsr_flags = switch (mcv) { |
| 2483 | const dest_reg = try self.register_manager.allocReg(null, gp); | 2476 | .register_c_flag => .cs, |
| | 2477 | .register_v_flag => .vs, |
| | 2478 | else => unreachable, |
| | 2479 | } }, |
| 2484 | | 2480 | |
| 2485 | // mov reg, #0 | 2481 | else => unreachable, |
| 2486 | _ = try self.addInst(.{ | 2482 | }; |
| 2487 | .tag = .mov, | | |
| 2488 | .data = .{ .rr_op = .{ | | |
| 2489 | .rd = dest_reg, | | |
| 2490 | .rn = .r0, | | |
| 2491 | .op = Instruction.Operand.fromU32(0).?, | | |
| 2492 | } }, | | |
| 2493 | }); | | |
| 2494 | | 2483 | |
| 2495 | // C flag: movcs reg, #1 | 2484 | if (self.reuseOperand(inst, operand, 0, field)) { |
| 2496 | // V flag: movvs reg, #1 | 2485 | break :result field; |
| 2497 | _ = try self.addInst(.{ | 2486 | } else { |
| 2498 | .tag = .mov, | 2487 | // Copy to new register |
| 2499 | .cond = switch (mcv) { | 2488 | const dest_reg = try self.register_manager.allocReg(null, gp); |
| 2500 | .register_c_flag => .cs, | 2489 | try self.genSetReg(struct_ty.structFieldType(index), dest_reg, field); |
| 2501 | .register_v_flag => .vs, | | |
| 2502 | else => unreachable, | | |
| 2503 | }, | | |
| 2504 | .data = .{ .rr_op = .{ | | |
| 2505 | .rd = dest_reg, | | |
| 2506 | .rn = .r0, | | |
| 2507 | .op = Instruction.Operand.fromU32(1).?, | | |
| 2508 | } }, | | |
| 2509 | }); | | |
| 2510 | | 2490 | |
| 2511 | break :result MCValue{ .register = dest_reg }; | 2491 | break :result MCValue{ .register = dest_reg }; |
| 2512 | } | | |
| 2513 | }, | | |
| 2514 | else => unreachable, | | |
| 2515 | } | 2492 | } |
| 2516 | }, | 2493 | }, |
| 2517 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), | 2494 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| ... | @@ -2528,6 +2505,41 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2528,6 +2505,41 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2528 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 2505 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2529 | } | 2506 | } |
| 2530 | | 2507 | |
| | 2508 | /// Allocates a new register. If Inst in non-null, additionally tracks |
| | 2509 | /// this register and the corresponding int and removes all previous |
| | 2510 | /// tracking. Does not do the actual moving (that is handled by |
| | 2511 | /// genSetReg). |
| | 2512 | fn prepareNewRegForMoving( |
| | 2513 | self: *Self, |
| | 2514 | track_inst: ?Air.Inst.Index, |
| | 2515 | register_class: RegisterManager.RegisterBitSet, |
| | 2516 | mcv: MCValue, |
| | 2517 | ) !Register { |
| | 2518 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| | 2519 | const reg = try self.register_manager.allocReg(track_inst, register_class); |
| | 2520 | |
| | 2521 | if (track_inst) |inst| { |
| | 2522 | // Overwrite the MCValue associated with this inst |
| | 2523 | branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| | 2524 | |
| | 2525 | // If the previous MCValue occupied some space we track, we |
| | 2526 | // need to make sure it is marked as free now. |
| | 2527 | switch (mcv) { |
| | 2528 | .cpsr_flags => { |
| | 2529 | assert(self.cpsr_flags_inst.? == inst); |
| | 2530 | self.cpsr_flags_inst = null; |
| | 2531 | }, |
| | 2532 | .register => |prev_reg| { |
| | 2533 | assert(!self.register_manager.isRegFree(prev_reg)); |
| | 2534 | self.register_manager.freeReg(prev_reg); |
| | 2535 | }, |
| | 2536 | else => {}, |
| | 2537 | } |
| | 2538 | } |
| | 2539 | |
| | 2540 | return reg; |
| | 2541 | } |
| | 2542 | |
| 2531 | /// Don't call this function directly. Use binOp instead. | 2543 | /// Don't call this function directly. Use binOp instead. |
| 2532 | /// | 2544 | /// |
| 2533 | /// Calling this function signals an intention to generate a Mir | 2545 | /// Calling this function signals an intention to generate a Mir |
| ... | @@ -2554,18 +2566,12 @@ fn binOpRegister( | ... | @@ -2554,18 +2566,12 @@ fn binOpRegister( |
| 2554 | null; | 2566 | null; |
| 2555 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2567 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2556 | | 2568 | |
| 2557 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 2558 | | | |
| 2559 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | 2569 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 2560 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | 2570 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 2561 | break :inst Air.refToIndex(md.lhs).?; | 2571 | break :inst Air.refToIndex(md.lhs).?; |
| 2562 | } else null; | 2572 | } else null; |
| 2563 | | 2573 | |
| 2564 | const reg = try self.register_manager.allocReg(track_inst, gp); | 2574 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); |
| 2565 | | | |
| 2566 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | | |
| 2567 | | | |
| 2568 | break :blk reg; | | |
| 2569 | }; | 2575 | }; |
| 2570 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 2576 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 2571 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2577 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| ... | @@ -2575,11 +2581,7 @@ fn binOpRegister( | ... | @@ -2575,11 +2581,7 @@ fn binOpRegister( |
| 2575 | break :inst Air.refToIndex(md.rhs).?; | 2581 | break :inst Air.refToIndex(md.rhs).?; |
| 2576 | } else null; | 2582 | } else null; |
| 2577 | | 2583 | |
| 2578 | const reg = try self.register_manager.allocReg(track_inst, gp); | 2584 | break :blk try self.prepareNewRegForMoving(track_inst, gp, rhs); |
| 2579 | | | |
| 2580 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | | |
| 2581 | | | |
| 2582 | break :blk reg; | | |
| 2583 | }; | 2585 | }; |
| 2584 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); | 2586 | const new_rhs_lock = self.register_manager.lockReg(rhs_reg); |
| 2585 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); | 2587 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| ... | @@ -2669,8 +2671,6 @@ fn binOpImmediate( | ... | @@ -2669,8 +2671,6 @@ fn binOpImmediate( |
| 2669 | null; | 2671 | null; |
| 2670 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2672 | defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2671 | | 2673 | |
| 2672 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 2673 | | | |
| 2674 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { | 2674 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 2675 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { | 2675 | const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { |
| 2676 | break :inst Air.refToIndex( | 2676 | break :inst Air.refToIndex( |
| ... | @@ -2678,11 +2678,7 @@ fn binOpImmediate( | ... | @@ -2678,11 +2678,7 @@ fn binOpImmediate( |
| 2678 | ).?; | 2678 | ).?; |
| 2679 | } else null; | 2679 | } else null; |
| 2680 | | 2680 | |
| 2681 | const reg = try self.register_manager.allocReg(track_inst, gp); | 2681 | break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs); |
| 2682 | | | |
| 2683 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); | | |
| 2684 | | | |
| 2685 | break :blk reg; | | |
| 2686 | }; | 2682 | }; |
| 2687 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); | 2683 | const new_lhs_lock = self.register_manager.lockReg(lhs_reg); |
| 2688 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); | 2684 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| ... | @@ -3461,7 +3457,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3461,7 +3457,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3461 | if (RegisterManager.indexOfRegIntoTracked(reg) == null) { | 3457 | if (RegisterManager.indexOfRegIntoTracked(reg) == null) { |
| 3462 | // Save function return value into a tracked register | 3458 | // Save function return value into a tracked register |
| 3463 | log.debug("airCall: copying {} as it is not tracked", .{reg}); | 3459 | log.debug("airCall: copying {} as it is not tracked", .{reg}); |
| 3464 | break :result try self.copyToNewRegister(inst, info.return_value); | 3460 | const new_reg = try self.copyToTmpRegister(fn_ty.fnReturnType(), info.return_value); |
| | 3461 | break :result MCValue{ .register = new_reg }; |
| 3465 | } | 3462 | } |
| 3466 | }, | 3463 | }, |
| 3467 | else => {}, | 3464 | else => {}, |
| ... | @@ -3570,53 +3567,89 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3570,53 +3567,89 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3570 | | 3567 | |
| 3571 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | 3568 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3572 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 3569 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3573 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3570 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 3574 | const lhs = try self.resolveInst(bin_op.lhs); | 3571 | |
| 3575 | const rhs = try self.resolveInst(bin_op.rhs); | 3572 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 3576 | const lhs_ty = self.air.typeOf(bin_op.lhs); | 3573 | const operands: BinOpOperands = .{ .inst = .{ |
| 3577 | | 3574 | .inst = inst, |
| 3578 | var int_buffer: Type.Payload.Bits = undefined; | 3575 | .lhs = bin_op.lhs, |
| 3579 | const int_ty = switch (lhs_ty.zigTypeTag()) { | 3576 | .rhs = bin_op.rhs, |
| 3580 | .Optional => blk: { | 3577 | } }; |
| 3581 | var opt_buffer: Type.Payload.ElemType = undefined; | 3578 | break :blk try self.cmp(operands, lhs_ty, op); |
| 3582 | const payload_ty = lhs_ty.optionalChild(&opt_buffer); | 3579 | }; |
| 3583 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | | |
| 3584 | break :blk Type.initTag(.u1); | | |
| 3585 | } else if (lhs_ty.isPtrLikeOptional()) { | | |
| 3586 | break :blk Type.usize; | | |
| 3587 | } else { | | |
| 3588 | return self.fail("TODO ARM cmp non-pointer optionals", .{}); | | |
| 3589 | } | | |
| 3590 | }, | | |
| 3591 | .Float => return self.fail("TODO ARM cmp floats", .{}), | | |
| 3592 | .Enum => lhs_ty.intTagType(&int_buffer), | | |
| 3593 | .Int => lhs_ty, | | |
| 3594 | .Bool => Type.initTag(.u1), | | |
| 3595 | .Pointer => Type.usize, | | |
| 3596 | .ErrorSet => Type.initTag(.u16), | | |
| 3597 | else => unreachable, | | |
| 3598 | }; | | |
| 3599 | | 3580 | |
| 3600 | const int_info = int_ty.intInfo(self.target.*); | 3581 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3601 | if (int_info.bits <= 32) { | 3582 | } |
| 3602 | try self.spillCompareFlagsIfOccupied(); | | |
| 3603 | self.cpsr_flags_inst = inst; | | |
| 3604 | | 3583 | |
| 3605 | _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ | 3584 | const BinOpOperands = union(enum) { |
| 3606 | .lhs = bin_op.lhs, | 3585 | inst: struct { |
| 3607 | .rhs = bin_op.rhs, | 3586 | inst: Air.Inst.Index, |
| 3608 | .inst = inst, | 3587 | lhs: Air.Inst.Ref, |
| 3609 | }); | 3588 | rhs: Air.Inst.Ref, |
| | 3589 | }, |
| | 3590 | mcv: struct { |
| | 3591 | lhs: MCValue, |
| | 3592 | rhs: MCValue, |
| | 3593 | }, |
| | 3594 | }; |
| 3610 | | 3595 | |
| 3611 | break :result switch (int_info.signedness) { | 3596 | fn cmp( |
| 3612 | .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) }, | 3597 | self: *Self, |
| 3613 | .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) }, | 3598 | operands: BinOpOperands, |
| 3614 | }; | 3599 | lhs_ty: Type, |
| 3615 | } else { | 3600 | op: math.CompareOperator, |
| 3616 | return self.fail("TODO ARM cmp for ints > 32 bits", .{}); | 3601 | ) !MCValue { |
| 3617 | } | 3602 | var int_buffer: Type.Payload.Bits = undefined; |
| | 3603 | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| | 3604 | .Optional => blk: { |
| | 3605 | var opt_buffer: Type.Payload.ElemType = undefined; |
| | 3606 | const payload_ty = lhs_ty.optionalChild(&opt_buffer); |
| | 3607 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| | 3608 | break :blk Type.initTag(.u1); |
| | 3609 | } else if (lhs_ty.isPtrLikeOptional()) { |
| | 3610 | break :blk Type.usize; |
| | 3611 | } else { |
| | 3612 | return self.fail("TODO ARM cmp non-pointer optionals", .{}); |
| | 3613 | } |
| | 3614 | }, |
| | 3615 | .Float => return self.fail("TODO ARM cmp floats", .{}), |
| | 3616 | .Enum => lhs_ty.intTagType(&int_buffer), |
| | 3617 | .Int => lhs_ty, |
| | 3618 | .Bool => Type.initTag(.u1), |
| | 3619 | .Pointer => Type.usize, |
| | 3620 | .ErrorSet => Type.initTag(.u16), |
| | 3621 | else => unreachable, |
| 3618 | }; | 3622 | }; |
| 3619 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 3623 | |
| | 3624 | const int_info = int_ty.intInfo(self.target.*); |
| | 3625 | if (int_info.bits <= 32) { |
| | 3626 | try self.spillCompareFlagsIfOccupied(); |
| | 3627 | |
| | 3628 | switch (operands) { |
| | 3629 | .inst => |inst_op| { |
| | 3630 | const metadata: BinOpMetadata = .{ |
| | 3631 | .inst = inst_op.inst, |
| | 3632 | .lhs = inst_op.lhs, |
| | 3633 | .rhs = inst_op.rhs, |
| | 3634 | }; |
| | 3635 | const lhs = try self.resolveInst(inst_op.lhs); |
| | 3636 | const rhs = try self.resolveInst(inst_op.rhs); |
| | 3637 | |
| | 3638 | self.cpsr_flags_inst = inst_op.inst; |
| | 3639 | _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, metadata); |
| | 3640 | }, |
| | 3641 | .mcv => |mcv_op| { |
| | 3642 | _ = try self.binOp(.cmp_eq, mcv_op.lhs, mcv_op.rhs, int_ty, int_ty, null); |
| | 3643 | }, |
| | 3644 | } |
| | 3645 | |
| | 3646 | return switch (int_info.signedness) { |
| | 3647 | .signed => MCValue{ .cpsr_flags = Condition.fromCompareOperatorSigned(op) }, |
| | 3648 | .unsigned => MCValue{ .cpsr_flags = Condition.fromCompareOperatorUnsigned(op) }, |
| | 3649 | }; |
| | 3650 | } else { |
| | 3651 | return self.fail("TODO ARM cmp for ints > 32 bits", .{}); |
| | 3652 | } |
| 3620 | } | 3653 | } |
| 3621 | | 3654 | |
| 3622 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { | 3655 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -4081,10 +4114,137 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4081,10 +4114,137 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 4081 | | 4114 | |
| 4082 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | 4115 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 4083 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 4116 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 4084 | const condition = pl_op.operand; | 4117 | const condition_ty = self.air.typeOf(pl_op.operand); |
| 4085 | _ = condition; | 4118 | const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); |
| 4086 | return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}); | 4119 | const liveness = try self.liveness.getSwitchBr( |
| 4087 | // return self.finishAir(inst, .dead, .{ condition, .none, .none }); | 4120 | self.gpa, |
| | 4121 | inst, |
| | 4122 | switch_br.data.cases_len + 1, |
| | 4123 | ); |
| | 4124 | defer self.gpa.free(liveness.deaths); |
| | 4125 | |
| | 4126 | // If the condition dies here in this switch instruction, process |
| | 4127 | // that death now instead of later as this has an effect on |
| | 4128 | // whether it needs to be spilled in the branches |
| | 4129 | if (self.liveness.operandDies(inst, 0)) { |
| | 4130 | const op_int = @enumToInt(pl_op.operand); |
| | 4131 | if (op_int >= Air.Inst.Ref.typed_value_map.len) { |
| | 4132 | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); |
| | 4133 | self.processDeath(op_index); |
| | 4134 | } |
| | 4135 | } |
| | 4136 | |
| | 4137 | var extra_index: usize = switch_br.end; |
| | 4138 | var case_i: u32 = 0; |
| | 4139 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| | 4140 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| | 4141 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| | 4142 | assert(items.len > 0); |
| | 4143 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| | 4144 | extra_index = case.end + items.len + case_body.len; |
| | 4145 | |
| | 4146 | var relocs = try self.gpa.alloc(u32, items.len); |
| | 4147 | defer self.gpa.free(relocs); |
| | 4148 | |
| | 4149 | if (items.len == 1) { |
| | 4150 | const condition = try self.resolveInst(pl_op.operand); |
| | 4151 | const item = try self.resolveInst(items[0]); |
| | 4152 | |
| | 4153 | const operands: BinOpOperands = .{ .mcv = .{ |
| | 4154 | .lhs = condition, |
| | 4155 | .rhs = item, |
| | 4156 | } }; |
| | 4157 | const cmp_result = try self.cmp(operands, condition_ty, .neq); |
| | 4158 | |
| | 4159 | relocs[0] = try self.addInst(.{ |
| | 4160 | .tag = .b, |
| | 4161 | .cond = cmp_result.cpsr_flags, |
| | 4162 | .data = .{ .inst = undefined }, // populated later through performReloc |
| | 4163 | }); |
| | 4164 | } else { |
| | 4165 | return self.fail("TODO switch with multiple items", .{}); |
| | 4166 | } |
| | 4167 | |
| | 4168 | // Capture the state of register and stack allocation state so that we can revert to it. |
| | 4169 | const parent_next_stack_offset = self.next_stack_offset; |
| | 4170 | const parent_free_registers = self.register_manager.free_registers; |
| | 4171 | const parent_cpsr_flags_inst = self.cpsr_flags_inst; |
| | 4172 | var parent_stack = try self.stack.clone(self.gpa); |
| | 4173 | defer parent_stack.deinit(self.gpa); |
| | 4174 | const parent_registers = self.register_manager.registers; |
| | 4175 | |
| | 4176 | try self.branch_stack.append(.{}); |
| | 4177 | errdefer { |
| | 4178 | _ = self.branch_stack.pop(); |
| | 4179 | } |
| | 4180 | |
| | 4181 | try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); |
| | 4182 | for (liveness.deaths[case_i]) |operand| { |
| | 4183 | self.processDeath(operand); |
| | 4184 | } |
| | 4185 | try self.genBody(case_body); |
| | 4186 | |
| | 4187 | // Revert to the previous register and stack allocation state. |
| | 4188 | var saved_case_branch = self.branch_stack.pop(); |
| | 4189 | defer saved_case_branch.deinit(self.gpa); |
| | 4190 | |
| | 4191 | self.register_manager.registers = parent_registers; |
| | 4192 | self.cpsr_flags_inst = parent_cpsr_flags_inst; |
| | 4193 | self.stack.deinit(self.gpa); |
| | 4194 | self.stack = parent_stack; |
| | 4195 | parent_stack = .{}; |
| | 4196 | |
| | 4197 | self.next_stack_offset = parent_next_stack_offset; |
| | 4198 | self.register_manager.free_registers = parent_free_registers; |
| | 4199 | |
| | 4200 | for (relocs) |reloc| { |
| | 4201 | try self.performReloc(reloc); |
| | 4202 | } |
| | 4203 | } |
| | 4204 | |
| | 4205 | if (switch_br.data.else_body_len > 0) { |
| | 4206 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| | 4207 | |
| | 4208 | // Capture the state of register and stack allocation state so that we can revert to it. |
| | 4209 | const parent_next_stack_offset = self.next_stack_offset; |
| | 4210 | const parent_free_registers = self.register_manager.free_registers; |
| | 4211 | const parent_cpsr_flags_inst = self.cpsr_flags_inst; |
| | 4212 | var parent_stack = try self.stack.clone(self.gpa); |
| | 4213 | defer parent_stack.deinit(self.gpa); |
| | 4214 | const parent_registers = self.register_manager.registers; |
| | 4215 | |
| | 4216 | try self.branch_stack.append(.{}); |
| | 4217 | errdefer { |
| | 4218 | _ = self.branch_stack.pop(); |
| | 4219 | } |
| | 4220 | |
| | 4221 | const else_deaths = liveness.deaths.len - 1; |
| | 4222 | try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); |
| | 4223 | for (liveness.deaths[else_deaths]) |operand| { |
| | 4224 | self.processDeath(operand); |
| | 4225 | } |
| | 4226 | try self.genBody(else_body); |
| | 4227 | |
| | 4228 | // Revert to the previous register and stack allocation state. |
| | 4229 | var saved_case_branch = self.branch_stack.pop(); |
| | 4230 | defer saved_case_branch.deinit(self.gpa); |
| | 4231 | |
| | 4232 | self.register_manager.registers = parent_registers; |
| | 4233 | self.cpsr_flags_inst = parent_cpsr_flags_inst; |
| | 4234 | self.stack.deinit(self.gpa); |
| | 4235 | self.stack = parent_stack; |
| | 4236 | parent_stack = .{}; |
| | 4237 | |
| | 4238 | self.next_stack_offset = parent_next_stack_offset; |
| | 4239 | self.register_manager.free_registers = parent_free_registers; |
| | 4240 | |
| | 4241 | // TODO consolidate returned MCValues between prongs and else branch like we do |
| | 4242 | // in airCondBr. |
| | 4243 | } |
| | 4244 | |
| | 4245 | // We already took care of pl_op.operand earlier, so we're going |
| | 4246 | // to pass .none here |
| | 4247 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 4088 | } | 4248 | } |
| 4089 | | 4249 | |
| 4090 | fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { | 4250 | fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { |