| ... | @@ -50,6 +50,7 @@ src_loc: Module.SrcLoc, | ... | @@ -50,6 +50,7 @@ src_loc: Module.SrcLoc, |
| 50 | stack_align: u32, | 50 | stack_align: u32, |
| 51 | | 51 | |
| 52 | ret_backpatch: ?Mir.Inst.Index = null, | 52 | ret_backpatch: ?Mir.Inst.Index = null, |
| | 53 | compare_flags_inst: ?Air.Inst.Index = null, |
| 53 | | 54 | |
| 54 | /// MIR Instructions | 55 | /// MIR Instructions |
| 55 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | 56 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| ... | @@ -774,6 +775,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { | ... | @@ -774,6 +775,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 774 | const canon_reg = reg.to64(); | 775 | const canon_reg = reg.to64(); |
| 775 | self.register_manager.freeReg(canon_reg); | 776 | self.register_manager.freeReg(canon_reg); |
| 776 | }, | 777 | }, |
| | 778 | .compare_flags_signed, .compare_flags_unsigned => { |
| | 779 | self.compare_flags_inst = null; |
| | 780 | }, |
| 777 | else => {}, // TODO process stack allocation death | 781 | else => {}, // TODO process stack allocation death |
| 778 | } | 782 | } |
| 779 | } | 783 | } |
| ... | @@ -889,6 +893,22 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void | ... | @@ -889,6 +893,22 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 889 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); | 893 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{}); |
| 890 | } | 894 | } |
| 891 | | 895 | |
| | 896 | pub fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| | 897 | if (self.compare_flags_inst) |inst_to_save| { |
| | 898 | const mcv = self.getResolvedInstValue(inst_to_save); |
| | 899 | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); |
| | 900 | |
| | 901 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| | 902 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| | 903 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); |
| | 904 | |
| | 905 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| | 906 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| | 907 | |
| | 908 | self.compare_flags_inst = null; |
| | 909 | } |
| | 910 | } |
| | 911 | |
| 892 | /// Copies a value to a register without tracking the register. The register is not considered | 912 | /// Copies a value to a register without tracking the register. The register is not considered |
| 893 | /// allocated. A second call to `copyToTmpRegister` may return the same register. | 913 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 894 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 914 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| ... | @@ -2763,6 +2783,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC | ... | @@ -2763,6 +2783,8 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 2763 | .memory, | 2783 | .memory, |
| 2764 | .got_load, | 2784 | .got_load, |
| 2765 | .direct_load, | 2785 | .direct_load, |
| | 2786 | .compare_flags_signed, |
| | 2787 | .compare_flags_unsigned, |
| 2766 | => { | 2788 | => { |
| 2767 | assert(abi_size <= 8); | 2789 | assert(abi_size <= 8); |
| 2768 | self.register_manager.freezeRegs(&.{dst_reg}); | 2790 | self.register_manager.freezeRegs(&.{dst_reg}); |
| ... | @@ -2784,12 +2806,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC | ... | @@ -2784,12 +2806,6 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC |
| 2784 | .data = .{ .imm = @bitCast(u32, -off) }, | 2806 | .data = .{ .imm = @bitCast(u32, -off) }, |
| 2785 | }); | 2807 | }); |
| 2786 | }, | 2808 | }, |
| 2787 | .compare_flags_unsigned => { | | |
| 2788 | return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); | | |
| 2789 | }, | | |
| 2790 | .compare_flags_signed => { | | |
| 2791 | return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{}); | | |
| 2792 | }, | | |
| 2793 | } | 2809 | } |
| 2794 | }, | 2810 | }, |
| 2795 | .stack_offset => |off| { | 2811 | .stack_offset => |off| { |
| ... | @@ -3061,6 +3077,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3061,6 +3077,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 3061 | var info = try self.resolveCallingConventionValues(fn_ty); | 3077 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 3062 | defer info.deinit(self); | 3078 | defer info.deinit(self); |
| 3063 | | 3079 | |
| | 3080 | try self.spillCompareFlagsIfOccupied(); |
| | 3081 | |
| 3064 | if (info.return_value == .stack_offset) { | 3082 | if (info.return_value == .stack_offset) { |
| 3065 | const ret_ty = fn_ty.fnReturnType(); | 3083 | const ret_ty = fn_ty.fnReturnType(); |
| 3066 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | 3084 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| ... | @@ -3384,17 +3402,24 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -3384,17 +3402,24 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3384 | break :blk ty.intInfo(self.target.*).signedness; | 3402 | break :blk ty.intInfo(self.target.*).signedness; |
| 3385 | }; | 3403 | }; |
| 3386 | | 3404 | |
| 3387 | const lhs = try self.resolveInst(bin_op.lhs); | 3405 | try self.spillCompareFlagsIfOccupied(); |
| 3388 | const rhs = try self.resolveInst(bin_op.rhs); | 3406 | self.compare_flags_inst = inst; |
| | 3407 | |
| 3389 | const result: MCValue = result: { | 3408 | const result: MCValue = result: { |
| 3390 | // There are 2 operands, destination and source. | 3409 | // There are 2 operands, destination and source. |
| 3391 | // Either one, but not both, can be a memory operand. | 3410 | // Either one, but not both, can be a memory operand. |
| 3392 | // Source operand can be an immediate, 8 bits or 32 bits. | 3411 | // Source operand can be an immediate, 8 bits or 32 bits. |
| 3393 | // TODO this looks wrong. Why do simply reuse lhs without checking if it is dead or alive? | 3412 | // TODO look into reusing the operand |
| 3394 | const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory())) | 3413 | const lhs = try self.resolveInst(bin_op.lhs); |
| 3395 | MCValue{ .register = try self.copyToTmpRegister(ty, lhs) } | 3414 | lhs.freezeIfRegister(&self.register_manager); |
| 3396 | else | 3415 | defer lhs.unfreezeIfRegister(&self.register_manager); |
| 3397 | lhs; | 3416 | |
| | 3417 | const dst_reg = try self.copyToTmpRegister(ty, lhs); |
| | 3418 | self.register_manager.freezeRegs(&.{dst_reg}); |
| | 3419 | defer self.register_manager.unfreezeRegs(&.{dst_reg}); |
| | 3420 | |
| | 3421 | const dst_mcv = MCValue{ .register = dst_reg }; |
| | 3422 | |
| 3398 | // This instruction supports only signed 32-bit immediates at most. | 3423 | // This instruction supports only signed 32-bit immediates at most. |
| 3399 | const src_mcv = try self.limitImmediateType(bin_op.rhs, i32); | 3424 | const src_mcv = try self.limitImmediateType(bin_op.rhs, i32); |
| 3400 | | 3425 | |
| ... | @@ -3404,6 +3429,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -3404,6 +3429,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3404 | .unsigned => MCValue{ .compare_flags_unsigned = op }, | 3429 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 3405 | }; | 3430 | }; |
| 3406 | }; | 3431 | }; |
| | 3432 | |
| 3407 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 3433 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3408 | } | 3434 | } |
| 3409 | | 3435 | |
| ... | @@ -3451,6 +3477,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { | ... | @@ -3451,6 +3477,7 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { |
| 3451 | }); | 3477 | }); |
| 3452 | }, | 3478 | }, |
| 3453 | .register => |reg| { | 3479 | .register => |reg| { |
| | 3480 | try self.spillCompareFlagsIfOccupied(); |
| 3454 | _ = try self.addInst(.{ | 3481 | _ = try self.addInst(.{ |
| 3455 | .tag = .@"test", | 3482 | .tag = .@"test", |
| 3456 | .ops = (Mir.Ops{ | 3483 | .ops = (Mir.Ops{ |
| ... | @@ -3467,19 +3494,15 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { | ... | @@ -3467,19 +3494,15 @@ fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { |
| 3467 | .data = .{ .inst = undefined }, | 3494 | .data = .{ .inst = undefined }, |
| 3468 | }); | 3495 | }); |
| 3469 | }, | 3496 | }, |
| 3470 | .immediate => { | 3497 | .immediate, |
| 3471 | if (abi_size <= 8) { | 3498 | .stack_offset, |
| 3472 | const reg = try self.copyToTmpRegister(ty, mcv); | 3499 | => { |
| 3473 | return self.genCondBrMir(ty, .{ .register = reg }); | 3500 | try self.spillCompareFlagsIfOccupied(); |
| 3474 | } | | |
| 3475 | return self.fail("TODO implement condbr when condition is immediate larger than 4 bytes", .{}); | | |
| 3476 | }, | | |
| 3477 | .stack_offset => { | | |
| 3478 | if (abi_size <= 8) { | 3501 | if (abi_size <= 8) { |
| 3479 | const reg = try self.copyToTmpRegister(ty, mcv); | 3502 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3480 | return self.genCondBrMir(ty, .{ .register = reg }); | 3503 | return self.genCondBrMir(ty, .{ .register = reg }); |
| 3481 | } | 3504 | } |
| 3482 | return self.fail("TODO implement condbr when condition is stack offset with abi larger than 8 bytes", .{}); | 3505 | return self.fail("TODO implement condbr when condition is {} with abi larger than 8 bytes", .{mcv}); |
| 3483 | }, | 3506 | }, |
| 3484 | else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}), | 3507 | else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}), |
| 3485 | } | 3508 | } |
| ... | @@ -3496,9 +3519,23 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3496,9 +3519,23 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3496 | | 3519 | |
| 3497 | const reloc = try self.genCondBrMir(cond_ty, cond); | 3520 | const reloc = try self.genCondBrMir(cond_ty, cond); |
| 3498 | | 3521 | |
| | 3522 | // If the condition dies here in this condbr instruction, process |
| | 3523 | // that death now instead of later as this has an effect on |
| | 3524 | // whether it needs to be spilled in the branches |
| | 3525 | // TODO I need investigate how to make this work without removing |
| | 3526 | // an assertion from getResolvedInstValue() |
| | 3527 | if (self.liveness.operandDies(inst, 0)) { |
| | 3528 | const op_int = @enumToInt(pl_op.operand); |
| | 3529 | if (op_int >= Air.Inst.Ref.typed_value_map.len) { |
| | 3530 | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); |
| | 3531 | self.processDeath(op_index); |
| | 3532 | } |
| | 3533 | } |
| | 3534 | |
| 3499 | // Capture the state of register and stack allocation state so that we can revert to it. | 3535 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 3500 | const parent_next_stack_offset = self.next_stack_offset; | 3536 | const parent_next_stack_offset = self.next_stack_offset; |
| 3501 | const parent_free_registers = self.register_manager.free_registers; | 3537 | const parent_free_registers = self.register_manager.free_registers; |
| | 3538 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 3502 | var parent_stack = try self.stack.clone(self.gpa); | 3539 | var parent_stack = try self.stack.clone(self.gpa); |
| 3503 | defer parent_stack.deinit(self.gpa); | 3540 | defer parent_stack.deinit(self.gpa); |
| 3504 | const parent_registers = self.register_manager.registers; | 3541 | const parent_registers = self.register_manager.registers; |
| ... | @@ -3520,6 +3557,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3520,6 +3557,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3520 | defer saved_then_branch.deinit(self.gpa); | 3557 | defer saved_then_branch.deinit(self.gpa); |
| 3521 | | 3558 | |
| 3522 | self.register_manager.registers = parent_registers; | 3559 | self.register_manager.registers = parent_registers; |
| | 3560 | self.compare_flags_inst = parent_compare_flags_inst; |
| 3523 | | 3561 | |
| 3524 | self.stack.deinit(self.gpa); | 3562 | self.stack.deinit(self.gpa); |
| 3525 | self.stack = parent_stack; | 3563 | self.stack = parent_stack; |
| ... | @@ -3590,6 +3628,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3590,6 +3628,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3590 | // We already deleted the items from this table that matched the else_branch. | 3628 | // We already deleted the items from this table that matched the else_branch. |
| 3591 | // So these are all instructions that are only overridden in the then branch. | 3629 | // So these are all instructions that are only overridden in the then branch. |
| 3592 | parent_branch.inst_table.putAssumeCapacity(then_key, then_value); | 3630 | parent_branch.inst_table.putAssumeCapacity(then_key, then_value); |
| | 3631 | log.debug("then_value = {}", .{then_value}); |
| 3593 | if (then_value == .dead) | 3632 | if (then_value == .dead) |
| 3594 | continue; | 3633 | continue; |
| 3595 | const parent_mcv = blk: { | 3634 | const parent_mcv = blk: { |
| ... | @@ -3614,23 +3653,31 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3614,23 +3653,31 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3614 | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); | 3653 | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); |
| 3615 | } | 3654 | } |
| 3616 | | 3655 | |
| 3617 | fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | 3656 | fn isNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| | 3657 | try self.spillCompareFlagsIfOccupied(); |
| | 3658 | self.compare_flags_inst = inst; |
| | 3659 | |
| 3618 | try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 }); | 3660 | try self.genBinMathOpMir(.cmp, ty, operand, MCValue{ .immediate = 0 }); |
| 3619 | return MCValue{ .compare_flags_unsigned = .eq }; | 3661 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 3620 | } | 3662 | } |
| 3621 | | 3663 | |
| 3622 | fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | 3664 | fn isNonNull(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| 3623 | const is_null_res = try self.isNull(ty, operand); | 3665 | const is_null_res = try self.isNull(inst, ty, operand); |
| 3624 | assert(is_null_res.compare_flags_unsigned == .eq); | 3666 | assert(is_null_res.compare_flags_unsigned == .eq); |
| 3625 | return MCValue{ .compare_flags_unsigned = .neq }; | 3667 | return MCValue{ .compare_flags_unsigned = .neq }; |
| 3626 | } | 3668 | } |
| 3627 | | 3669 | |
| 3628 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 3670 | fn isErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| 3629 | const err_type = ty.errorUnionSet(); | 3671 | const err_type = ty.errorUnionSet(); |
| 3630 | const payload_type = ty.errorUnionPayload(); | 3672 | const payload_type = ty.errorUnionPayload(); |
| 3631 | if (!err_type.hasRuntimeBits()) { | 3673 | if (!err_type.hasRuntimeBits()) { |
| 3632 | return MCValue{ .immediate = 0 }; // always false | 3674 | return MCValue{ .immediate = 0 }; // always false |
| 3633 | } else if (!payload_type.hasRuntimeBits()) { | 3675 | } |
| | 3676 | |
| | 3677 | try self.spillCompareFlagsIfOccupied(); |
| | 3678 | self.compare_flags_inst = inst; |
| | 3679 | |
| | 3680 | if (!payload_type.hasRuntimeBits()) { |
| 3634 | if (err_type.abiSize(self.target.*) <= 8) { | 3681 | if (err_type.abiSize(self.target.*) <= 8) { |
| 3635 | try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); | 3682 | try self.genBinMathOpMir(.cmp, err_type, operand, MCValue{ .immediate = 0 }); |
| 3636 | return MCValue{ .compare_flags_unsigned = .gt }; | 3683 | return MCValue{ .compare_flags_unsigned = .gt }; |
| ... | @@ -3643,8 +3690,8 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | ... | @@ -3643,8 +3690,8 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3643 | } | 3690 | } |
| 3644 | } | 3691 | } |
| 3645 | | 3692 | |
| 3646 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 3693 | fn isNonErr(self: *Self, inst: Air.Inst.Index, ty: Type, operand: MCValue) !MCValue { |
| 3647 | const is_err_res = try self.isErr(ty, operand); | 3694 | const is_err_res = try self.isErr(inst, ty, operand); |
| 3648 | switch (is_err_res) { | 3695 | switch (is_err_res) { |
| 3649 | .compare_flags_unsigned => |op| { | 3696 | .compare_flags_unsigned => |op| { |
| 3650 | assert(op == .gt); | 3697 | assert(op == .gt); |
| ... | @@ -3663,7 +3710,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3663,7 +3710,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 3663 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3710 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3664 | const operand = try self.resolveInst(un_op); | 3711 | const operand = try self.resolveInst(un_op); |
| 3665 | const ty = self.air.typeOf(un_op); | 3712 | const ty = self.air.typeOf(un_op); |
| 3666 | break :result try self.isNull(ty, operand); | 3713 | break :result try self.isNull(inst, ty, operand); |
| 3667 | }; | 3714 | }; |
| 3668 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3715 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3669 | } | 3716 | } |
| ... | @@ -3684,7 +3731,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3684,7 +3731,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3684 | }; | 3731 | }; |
| 3685 | const ptr_ty = self.air.typeOf(un_op); | 3732 | const ptr_ty = self.air.typeOf(un_op); |
| 3686 | try self.load(operand, operand_ptr, ptr_ty); | 3733 | try self.load(operand, operand_ptr, ptr_ty); |
| 3687 | break :result try self.isNull(ptr_ty.elemType(), operand); | 3734 | break :result try self.isNull(inst, ptr_ty.elemType(), operand); |
| 3688 | }; | 3735 | }; |
| 3689 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3736 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3690 | } | 3737 | } |
| ... | @@ -3694,7 +3741,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3694,7 +3741,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 3694 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3741 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3695 | const operand = try self.resolveInst(un_op); | 3742 | const operand = try self.resolveInst(un_op); |
| 3696 | const ty = self.air.typeOf(un_op); | 3743 | const ty = self.air.typeOf(un_op); |
| 3697 | break :result try self.isNonNull(ty, operand); | 3744 | break :result try self.isNonNull(inst, ty, operand); |
| 3698 | }; | 3745 | }; |
| 3699 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3746 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3700 | } | 3747 | } |
| ... | @@ -3715,7 +3762,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3715,7 +3762,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3715 | }; | 3762 | }; |
| 3716 | const ptr_ty = self.air.typeOf(un_op); | 3763 | const ptr_ty = self.air.typeOf(un_op); |
| 3717 | try self.load(operand, operand_ptr, ptr_ty); | 3764 | try self.load(operand, operand_ptr, ptr_ty); |
| 3718 | break :result try self.isNonNull(ptr_ty.elemType(), operand); | 3765 | break :result try self.isNonNull(inst, ptr_ty.elemType(), operand); |
| 3719 | }; | 3766 | }; |
| 3720 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3767 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3721 | } | 3768 | } |
| ... | @@ -3725,7 +3772,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3725,7 +3772,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { |
| 3725 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3772 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3726 | const operand = try self.resolveInst(un_op); | 3773 | const operand = try self.resolveInst(un_op); |
| 3727 | const ty = self.air.typeOf(un_op); | 3774 | const ty = self.air.typeOf(un_op); |
| 3728 | break :result try self.isErr(ty, operand); | 3775 | break :result try self.isErr(inst, ty, operand); |
| 3729 | }; | 3776 | }; |
| 3730 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3777 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3731 | } | 3778 | } |
| ... | @@ -3746,7 +3793,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3746,7 +3793,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3746 | }; | 3793 | }; |
| 3747 | const ptr_ty = self.air.typeOf(un_op); | 3794 | const ptr_ty = self.air.typeOf(un_op); |
| 3748 | try self.load(operand, operand_ptr, ptr_ty); | 3795 | try self.load(operand, operand_ptr, ptr_ty); |
| 3749 | break :result try self.isErr(ptr_ty.elemType(), operand); | 3796 | break :result try self.isErr(inst, ptr_ty.elemType(), operand); |
| 3750 | }; | 3797 | }; |
| 3751 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3798 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3752 | } | 3799 | } |
| ... | @@ -3756,7 +3803,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3756,7 +3803,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { |
| 3756 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 3803 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3757 | const operand = try self.resolveInst(un_op); | 3804 | const operand = try self.resolveInst(un_op); |
| 3758 | const ty = self.air.typeOf(un_op); | 3805 | const ty = self.air.typeOf(un_op); |
| 3759 | break :result try self.isNonErr(ty, operand); | 3806 | break :result try self.isNonErr(inst, ty, operand); |
| 3760 | }; | 3807 | }; |
| 3761 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3808 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3762 | } | 3809 | } |
| ... | @@ -3777,7 +3824,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3777,7 +3824,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3777 | }; | 3824 | }; |
| 3778 | const ptr_ty = self.air.typeOf(un_op); | 3825 | const ptr_ty = self.air.typeOf(un_op); |
| 3779 | try self.load(operand, operand_ptr, ptr_ty); | 3826 | try self.load(operand, operand_ptr, ptr_ty); |
| 3780 | break :result try self.isNonErr(ptr_ty.elemType(), operand); | 3827 | break :result try self.isNonErr(inst, ptr_ty.elemType(), operand); |
| 3781 | }; | 3828 | }; |
| 3782 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 3829 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3783 | } | 3830 | } |
| ... | @@ -3832,6 +3879,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u | ... | @@ -3832,6 +3879,7 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u |
| 3832 | .compare_flags_signed => unreachable, | 3879 | .compare_flags_signed => unreachable, |
| 3833 | .compare_flags_unsigned => unreachable, | 3880 | .compare_flags_unsigned => unreachable, |
| 3834 | .register => |cond_reg| { | 3881 | .register => |cond_reg| { |
| | 3882 | try self.spillCompareFlagsIfOccupied(); |
| 3835 | switch (case) { | 3883 | switch (case) { |
| 3836 | .none => unreachable, | 3884 | .none => unreachable, |
| 3837 | .undef => unreachable, | 3885 | .undef => unreachable, |
| ... | @@ -3916,9 +3964,23 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3916,9 +3964,23 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 3916 | relocs[item_i] = try self.genCondSwitchMir(condition_ty, condition, item_mcv); | 3964 | relocs[item_i] = try self.genCondSwitchMir(condition_ty, condition, item_mcv); |
| 3917 | } | 3965 | } |
| 3918 | | 3966 | |
| | 3967 | // If the condition dies here in this condbr instruction, process |
| | 3968 | // that death now instead of later as this has an effect on |
| | 3969 | // whether it needs to be spilled in the branches |
| | 3970 | // TODO I need investigate how to make this work without removing |
| | 3971 | // an assertion from getResolvedInstValue() |
| | 3972 | if (self.liveness.operandDies(inst, 0)) { |
| | 3973 | const op_int = @enumToInt(pl_op.operand); |
| | 3974 | if (op_int >= Air.Inst.Ref.typed_value_map.len) { |
| | 3975 | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); |
| | 3976 | self.processDeath(op_index); |
| | 3977 | } |
| | 3978 | } |
| | 3979 | |
| 3919 | // Capture the state of register and stack allocation state so that we can revert to it. | 3980 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 3920 | const parent_next_stack_offset = self.next_stack_offset; | 3981 | const parent_next_stack_offset = self.next_stack_offset; |
| 3921 | const parent_free_registers = self.register_manager.free_registers; | 3982 | const parent_free_registers = self.register_manager.free_registers; |
| | 3983 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 3922 | var parent_stack = try self.stack.clone(self.gpa); | 3984 | var parent_stack = try self.stack.clone(self.gpa); |
| 3923 | defer parent_stack.deinit(self.gpa); | 3985 | defer parent_stack.deinit(self.gpa); |
| 3924 | const parent_registers = self.register_manager.registers; | 3986 | const parent_registers = self.register_manager.registers; |
| ... | @@ -3940,6 +4002,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3940,6 +4002,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 3940 | defer saved_case_branch.deinit(self.gpa); | 4002 | defer saved_case_branch.deinit(self.gpa); |
| 3941 | | 4003 | |
| 3942 | self.register_manager.registers = parent_registers; | 4004 | self.register_manager.registers = parent_registers; |
| | 4005 | self.compare_flags_inst = parent_compare_flags_inst; |
| 3943 | self.stack.deinit(self.gpa); | 4006 | self.stack.deinit(self.gpa); |
| 3944 | self.stack = parent_stack; | 4007 | self.stack = parent_stack; |
| 3945 | parent_stack = .{}; | 4008 | parent_stack = .{}; |
| ... | @@ -5208,7 +5271,8 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { | ... | @@ -5208,7 +5271,8 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| 5208 | while (true) { | 5271 | while (true) { |
| 5209 | i -= 1; | 5272 | i -= 1; |
| 5210 | if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { | 5273 | if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { |
| 5211 | assert(mcv != .dead); | 5274 | // TODO see comment in `airCondBr` and `airSwitch` |
| | 5275 | // assert(mcv != .dead); |
| 5212 | return mcv; | 5276 | return mcv; |
| 5213 | } | 5277 | } |
| 5214 | } | 5278 | } |