| ... | ... | @@ -1548,7 +1548,7 @@ fn allocRegs( |
| 1548 | 1548 | }; |
| 1549 | 1549 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1550 | 1550 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); |
| 1551 | | read_locks[i] = self.register_manager.lockReg(arg.reg.*); |
| 1551 | read_locks[i] = self.register_manager.lockRegAssumeUnused(arg.reg.*); |
| 1552 | 1552 | } |
| 1553 | 1553 | } |
| 1554 | 1554 | |
| ... | ... | @@ -2882,10 +2882,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2882 | 2882 | |
| 2883 | 2883 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 2884 | 2884 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2885 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); |
| 2885 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2886 | const optional_ty = self.air.typeOf(ty_op.operand); |
| 2887 | const mcv = try self.resolveInst(ty_op.operand); |
| 2888 | break :result try self.optionalPayload(inst, mcv, optional_ty); |
| 2889 | }; |
| 2886 | 2890 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2887 | 2891 | } |
| 2888 | 2892 | |
| 2893 | fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: Type) !MCValue { |
| 2894 | var opt_buf: Type.Payload.ElemType = undefined; |
| 2895 | const payload_ty = optional_ty.optionalChild(&opt_buf); |
| 2896 | if (!payload_ty.hasRuntimeBits()) return MCValue.none; |
| 2897 | if (optional_ty.isPtrLikeOptional()) { |
| 2898 | // TODO should we reuse the operand here? |
| 2899 | const raw_reg = try self.register_manager.allocReg(inst, gp); |
| 2900 | const reg = self.registerAlias(raw_reg, payload_ty); |
| 2901 | try self.genSetReg(payload_ty, reg, mcv); |
| 2902 | return MCValue{ .register = reg }; |
| 2903 | } |
| 2904 | |
| 2905 | const offset = @intCast(u32, optional_ty.abiSize(self.target.*) - payload_ty.abiSize(self.target.*)); |
| 2906 | switch (mcv) { |
| 2907 | .register => |source_reg| { |
| 2908 | // TODO should we reuse the operand here? |
| 2909 | const raw_reg = try self.register_manager.allocReg(inst, gp); |
| 2910 | const dest_reg = raw_reg.toX(); |
| 2911 | |
| 2912 | const shift = @intCast(u6, offset * 8); |
| 2913 | if (shift == 0) { |
| 2914 | try self.genSetReg(payload_ty, dest_reg, mcv); |
| 2915 | } else { |
| 2916 | _ = try self.addInst(.{ |
| 2917 | .tag = if (payload_ty.isSignedInt()) |
| 2918 | Mir.Inst.Tag.asr_immediate |
| 2919 | else |
| 2920 | Mir.Inst.Tag.lsr_immediate, |
| 2921 | .data = .{ .rr_shift = .{ |
| 2922 | .rd = dest_reg, |
| 2923 | .rn = source_reg.toX(), |
| 2924 | .shift = shift, |
| 2925 | } }, |
| 2926 | }); |
| 2927 | } |
| 2928 | |
| 2929 | return MCValue{ .register = self.registerAlias(dest_reg, payload_ty) }; |
| 2930 | }, |
| 2931 | .stack_argument_offset => |off| { |
| 2932 | return MCValue{ .stack_argument_offset = off + offset }; |
| 2933 | }, |
| 2934 | .stack_offset => |off| { |
| 2935 | return MCValue{ .stack_offset = off - offset }; |
| 2936 | }, |
| 2937 | .memory => |addr| { |
| 2938 | return MCValue{ .memory = addr + offset }; |
| 2939 | }, |
| 2940 | else => unreachable, // invalid MCValue for an error union |
| 2941 | } |
| 2942 | } |
| 2943 | |
| 2889 | 2944 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2890 | 2945 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2891 | 2946 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); |
| ... | ... | @@ -3012,15 +3067,45 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { |
| 3012 | 3067 | |
| 3013 | 3068 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 3014 | 3069 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3015 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3016 | | const optional_ty = self.air.typeOfIndex(inst); |
| 3017 | 3070 | |
| 3018 | | // Optional with a zero-bit payload type is just a boolean true |
| 3019 | | if (optional_ty.abiSize(self.target.*) == 1) |
| 3071 | if (self.liveness.isUnused(inst)) { |
| 3072 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| 3073 | } |
| 3074 | |
| 3075 | const result: MCValue = result: { |
| 3076 | const payload_ty = self.air.typeOf(ty_op.operand); |
| 3077 | if (!payload_ty.hasRuntimeBits()) { |
| 3020 | 3078 | break :result MCValue{ .immediate = 1 }; |
| 3079 | } |
| 3080 | |
| 3081 | const optional_ty = self.air.typeOfIndex(inst); |
| 3082 | const operand = try self.resolveInst(ty_op.operand); |
| 3083 | const operand_lock: ?RegisterLock = switch (operand) { |
| 3084 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 3085 | else => null, |
| 3086 | }; |
| 3087 | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 3088 | |
| 3089 | if (optional_ty.isPtrLikeOptional()) { |
| 3090 | // TODO should we check if we can reuse the operand? |
| 3091 | const raw_reg = try self.register_manager.allocReg(inst, gp); |
| 3092 | const reg = self.registerAlias(raw_reg, payload_ty); |
| 3093 | try self.genSetReg(payload_ty, raw_reg, operand); |
| 3094 | break :result MCValue{ .register = reg }; |
| 3095 | } |
| 3096 | |
| 3097 | const optional_abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); |
| 3098 | const optional_abi_align = optional_ty.abiAlignment(self.target.*); |
| 3099 | const payload_abi_size = @intCast(u32, payload_ty.abiSize(self.target.*)); |
| 3100 | const offset = optional_abi_size - payload_abi_size; |
| 3101 | |
| 3102 | const stack_offset = try self.allocMem(optional_abi_size, optional_abi_align, inst); |
| 3103 | try self.genSetStack(Type.bool, stack_offset, .{ .immediate = 1 }); |
| 3104 | try self.genSetStack(payload_ty, stack_offset - @intCast(u32, offset), operand); |
| 3021 | 3105 | |
| 3022 | | return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); |
| 3106 | break :result MCValue{ .stack_offset = stack_offset }; |
| 3023 | 3107 | }; |
| 3108 | |
| 3024 | 3109 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3025 | 3110 | } |
| 3026 | 3111 | |
| ... | ... | @@ -4562,18 +4647,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4562 | 4647 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 4563 | 4648 | } |
| 4564 | 4649 | |
| 4565 | | fn isNull(self: *Self, operand: MCValue) !MCValue { |
| 4566 | | _ = operand; |
| 4567 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 4568 | | // will call isNonNull and invert the result. |
| 4569 | | return self.fail("TODO call isNonNull and invert the result", .{}); |
| 4650 | fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { |
| 4651 | const sentinel_ty: Type = if (!operand_ty.isPtrLikeOptional()) blk: { |
| 4652 | var buf: Type.Payload.ElemType = undefined; |
| 4653 | const payload_ty = operand_ty.optionalChild(&buf); |
| 4654 | break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime()) Type.bool else operand_ty; |
| 4655 | } else operand_ty; |
| 4656 | const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } }; |
| 4657 | return self.cmp(operand_bind, imm_bind, sentinel_ty, .eq); |
| 4570 | 4658 | } |
| 4571 | 4659 | |
| 4572 | | fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 4573 | | _ = operand; |
| 4574 | | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 4575 | | // will call isNull and invert the result. |
| 4576 | | return self.fail("TODO call isNull and invert the result", .{}); |
| 4660 | fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { |
| 4661 | const is_null_res = try self.isNull(operand_bind, operand_ty); |
| 4662 | assert(is_null_res.compare_flags == .eq); |
| 4663 | return MCValue{ .compare_flags = is_null_res.compare_flags.negate() }; |
| 4577 | 4664 | } |
| 4578 | 4665 | |
| 4579 | 4666 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| ... | ... | @@ -4606,7 +4693,9 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4606 | 4693 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4607 | 4694 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4608 | 4695 | const operand = try self.resolveInst(un_op); |
| 4609 | | break :result try self.isNull(operand); |
| 4696 | const operand_ty = self.air.typeOf(un_op); |
| 4697 | |
| 4698 | break :result try self.isNull(.{ .mcv = operand }, operand_ty); |
| 4610 | 4699 | }; |
| 4611 | 4700 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4612 | 4701 | } |
| ... | ... | @@ -4621,7 +4710,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4621 | 4710 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4622 | 4711 | try self.load(operand, operand_ptr, ptr_ty); |
| 4623 | 4712 | |
| 4624 | | break :result try self.isNull(operand); |
| 4713 | break :result try self.isNull(.{ .mcv = operand }, elem_ty); |
| 4625 | 4714 | }; |
| 4626 | 4715 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4627 | 4716 | } |
| ... | ... | @@ -4630,7 +4719,9 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4630 | 4719 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4631 | 4720 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4632 | 4721 | const operand = try self.resolveInst(un_op); |
| 4633 | | break :result try self.isNonNull(operand); |
| 4722 | const operand_ty = self.air.typeOf(un_op); |
| 4723 | |
| 4724 | break :result try self.isNonNull(.{ .mcv = operand }, operand_ty); |
| 4634 | 4725 | }; |
| 4635 | 4726 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4636 | 4727 | } |
| ... | ... | @@ -4645,7 +4736,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4645 | 4736 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4646 | 4737 | try self.load(operand, operand_ptr, ptr_ty); |
| 4647 | 4738 | |
| 4648 | | break :result try self.isNonNull(operand); |
| 4739 | break :result try self.isNonNull(.{ .mcv = operand }, elem_ty); |
| 4649 | 4740 | }; |
| 4650 | 4741 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4651 | 4742 | } |