| ... | ... | @@ -939,63 +939,85 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 939 | 939 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 940 | 940 | } |
| 941 | 941 | |
| 942 | | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 943 | | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 944 | | if (self.liveness.isUnused(inst)) |
| 945 | | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| 942 | fn truncRegister( |
| 943 | self: *Self, |
| 944 | operand_reg: Register, |
| 945 | dest_reg: Register, |
| 946 | int_signedness: std.builtin.Signedness, |
| 947 | int_bits: u16, |
| 948 | ) !void { |
| 949 | // TODO check if sxtb/uxtb/sxth/uxth are more efficient |
| 950 | _ = try self.addInst(.{ |
| 951 | .tag = switch (int_signedness) { |
| 952 | .signed => .sbfx, |
| 953 | .unsigned => .ubfx, |
| 954 | }, |
| 955 | .data = .{ .rr_lsb_width = .{ |
| 956 | .rd = dest_reg, |
| 957 | .rn = operand_reg, |
| 958 | .lsb = 0, |
| 959 | .width = @intCast(u6, int_bits), |
| 960 | } }, |
| 961 | }); |
| 962 | } |
| 946 | 963 | |
| 947 | | const operand_ty = self.air.typeOf(ty_op.operand); |
| 948 | | const operand = try self.resolveInst(ty_op.operand); |
| 964 | fn trunc( |
| 965 | self: *Self, |
| 966 | maybe_inst: ?Air.Inst.Index, |
| 967 | operand: MCValue, |
| 968 | operand_ty: Type, |
| 969 | dest_ty: Type, |
| 970 | ) !MCValue { |
| 949 | 971 | const info_a = operand_ty.intInfo(self.target.*); |
| 950 | | const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*); |
| 951 | | |
| 952 | | const result: MCValue = blk: { |
| 953 | | if (info_b.bits <= 32) { |
| 954 | | const operand_reg = switch (operand) { |
| 955 | | .register => |r| r, |
| 956 | | else => operand_reg: { |
| 957 | | if (info_a.bits <= 32) { |
| 958 | | break :operand_reg try self.copyToTmpRegister(operand_ty, operand); |
| 959 | | } else { |
| 960 | | return self.fail("TODO load least significant word into register", .{}); |
| 961 | | } |
| 962 | | }, |
| 963 | | }; |
| 964 | | self.register_manager.freezeRegs(&.{operand_reg}); |
| 965 | | defer self.register_manager.unfreezeRegs(&.{operand_reg}); |
| 966 | | |
| 967 | | const dest_reg = dest_reg: { |
| 968 | | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { |
| 969 | | break :dest_reg operand_reg; |
| 972 | const info_b = dest_ty.intInfo(self.target.*); |
| 973 | |
| 974 | if (info_b.bits <= 32) { |
| 975 | const operand_reg = switch (operand) { |
| 976 | .register => |r| r, |
| 977 | else => operand_reg: { |
| 978 | if (info_a.bits <= 32) { |
| 979 | break :operand_reg try self.copyToTmpRegister(operand_ty, operand); |
| 980 | } else { |
| 981 | return self.fail("TODO load least significant word into register", .{}); |
| 970 | 982 | } |
| 983 | }, |
| 984 | }; |
| 985 | self.register_manager.freezeRegs(&.{operand_reg}); |
| 986 | defer self.register_manager.unfreezeRegs(&.{operand_reg}); |
| 971 | 987 | |
| 972 | | break :dest_reg try self.register_manager.allocReg(null); |
| 973 | | }; |
| 988 | const dest_reg = if (maybe_inst) |inst| blk: { |
| 989 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 974 | 990 | |
| 975 | | switch (info_b.bits) { |
| 976 | | 32 => { |
| 977 | | try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg }); |
| 978 | | break :blk MCValue{ .register = dest_reg }; |
| 979 | | }, |
| 980 | | else => { |
| 981 | | _ = try self.addInst(.{ |
| 982 | | .tag = switch (info_b.signedness) { |
| 983 | | .signed => .sbfx, |
| 984 | | .unsigned => .ubfx, |
| 985 | | }, |
| 986 | | .data = .{ .rr_lsb_width = .{ |
| 987 | | .rd = dest_reg, |
| 988 | | .rn = operand_reg, |
| 989 | | .lsb = 0, |
| 990 | | .width = @intCast(u6, info_b.bits), |
| 991 | | } }, |
| 992 | | }); |
| 993 | | break :blk MCValue{ .register = dest_reg }; |
| 994 | | }, |
| 991 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { |
| 992 | break :blk operand_reg; |
| 993 | } else { |
| 994 | break :blk try self.register_manager.allocReg(inst); |
| 995 | 995 | } |
| 996 | | } else { |
| 997 | | return self.fail("TODO: truncate to ints > 32 bits", .{}); |
| 996 | } else try self.register_manager.allocReg(null); |
| 997 | |
| 998 | switch (info_b.bits) { |
| 999 | 32 => { |
| 1000 | try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg }); |
| 1001 | return MCValue{ .register = dest_reg }; |
| 1002 | }, |
| 1003 | else => { |
| 1004 | try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); |
| 1005 | return MCValue{ .register = dest_reg }; |
| 1006 | }, |
| 998 | 1007 | } |
| 1008 | } else { |
| 1009 | return self.fail("TODO: truncate to ints > 32 bits", .{}); |
| 1010 | } |
| 1011 | } |
| 1012 | |
| 1013 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1014 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1015 | const operand = try self.resolveInst(ty_op.operand); |
| 1016 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 1017 | const dest_ty = self.air.typeOfIndex(inst); |
| 1018 | |
| 1019 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { |
| 1020 | break :blk try self.trunc(inst, operand, operand_ty, dest_ty); |
| 999 | 1021 | }; |
| 1000 | 1022 | |
| 1001 | 1023 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -1099,6 +1121,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1099 | 1121 | } }, |
| 1100 | 1122 | }); |
| 1101 | 1123 | |
| 1124 | if (int_info.bits < 32) { |
| 1125 | try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); |
| 1126 | } |
| 1127 | |
| 1102 | 1128 | break :result MCValue{ .register = dest_reg }; |
| 1103 | 1129 | } else { |
| 1104 | 1130 | return self.fail("TODO ARM not on integers > u32/i32", .{}); |