| ... | @@ -1548,7 +1548,7 @@ fn allocRegs( | ... | @@ -1548,7 +1548,7 @@ fn allocRegs( |
| 1548 | }; | 1548 | }; |
| 1549 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); | 1549 | const raw_reg = try self.register_manager.allocReg(track_inst, gp); |
| 1550 | arg.reg.* = self.registerAlias(raw_reg, arg.ty); | 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,10 +2882,65 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 2882 | | 2882 | |
| 2883 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | 2883 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 2884 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { | 2944 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2890 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2945 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2891 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); | 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,15 +3067,45 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { |
| 3012 | | 3067 | |
| 3013 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { | 3068 | fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 3014 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | 3071 | if (self.liveness.isUnused(inst)) { |
| 3019 | if (optional_ty.abiSize(self.target.*) == 1) | 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 | break :result MCValue{ .immediate = 1 }; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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,18 +4647,20 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4562 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); | 4647 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 4563 | } | 4648 | } |
| 4564 | | 4649 | |
| 4565 | fn isNull(self: *Self, operand: MCValue) !MCValue { | 4650 | fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { |
| 4566 | _ = operand; | 4651 | const sentinel_ty: Type = if (!operand_ty.isPtrLikeOptional()) blk: { |
| 4567 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 4652 | var buf: Type.Payload.ElemType = undefined; |
| 4568 | // will call isNonNull and invert the result. | 4653 | const payload_ty = operand_ty.optionalChild(&buf); |
| 4569 | return self.fail("TODO call isNonNull and invert the result", .{}); | 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 { | 4660 | fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { |
| 4573 | _ = operand; | 4661 | const is_null_res = try self.isNull(operand_bind, operand_ty); |
| 4574 | // Here you can specialize this instruction if it makes sense to, otherwise the default | 4662 | assert(is_null_res.compare_flags == .eq); |
| 4575 | // will call isNull and invert the result. | 4663 | return MCValue{ .compare_flags = is_null_res.compare_flags.negate() }; |
| 4576 | return self.fail("TODO call isNull and invert the result", .{}); | | |
| 4577 | } | 4664 | } |
| 4578 | | 4665 | |
| 4579 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | 4666 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| ... | @@ -4606,7 +4693,9 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4606,7 +4693,9 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4606 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4693 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4607 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4694 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4608 | const operand = try self.resolveInst(un_op); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +4710,7 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4621 | const operand = try self.allocRegOrMem(elem_ty, true, null); | 4710 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4622 | try self.load(operand, operand_ptr, ptr_ty); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +4719,9 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { |
| 4630 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4719 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 4631 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 4720 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 4632 | const operand = try self.resolveInst(un_op); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 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,7 +4736,7 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 4645 | const operand = try self.allocRegOrMem(elem_ty, true, null); | 4736 | const operand = try self.allocRegOrMem(elem_ty, true, null); |
| 4646 | try self.load(operand, operand_ptr, ptr_ty); | 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 | return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4741 | return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 4651 | } | 4742 | } |