| ... | ... | @@ -22,6 +22,7 @@ const Type = @import("../../type.zig").Type; |
| 22 | 22 | const CodeGenError = codegen.CodeGenError; |
| 23 | 23 | const Result = @import("../../codegen.zig").Result; |
| 24 | 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 25 | const Endian = std.builtin.Endian; |
| 25 | 26 | |
| 26 | 27 | const build_options = @import("build_options"); |
| 27 | 28 | |
| ... | ... | @@ -30,6 +31,7 @@ const abi = @import("abi.zig"); |
| 30 | 31 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 31 | 32 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 32 | 33 | const Instruction = bits.Instruction; |
| 34 | const ASI = Instruction.ASI; |
| 33 | 35 | const ShiftWidth = Instruction.ShiftWidth; |
| 34 | 36 | const RegisterManager = abi.RegisterManager; |
| 35 | 37 | const RegisterLock = RegisterManager.RegisterLock; |
| ... | ... | @@ -615,7 +617,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 615 | 617 | .clz => try self.airClz(inst), |
| 616 | 618 | .ctz => try self.airCtz(inst), |
| 617 | 619 | .popcount => try self.airPopcount(inst), |
| 618 | | .byte_swap => @panic("TODO try self.airByteSwap(inst)"), |
| 620 | .byte_swap => try self.airByteSwap(inst), |
| 619 | 621 | .bit_reverse => try self.airBitReverse(inst), |
| 620 | 622 | .tag_name => try self.airTagName(inst), |
| 621 | 623 | .error_name => try self.airErrorName(inst), |
| ... | ... | @@ -1200,6 +1202,90 @@ fn airBreakpoint(self: *Self) !void { |
| 1200 | 1202 | return self.finishAirBookkeeping(); |
| 1201 | 1203 | } |
| 1202 | 1204 | |
| 1205 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { |
| 1206 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1207 | |
| 1208 | // We have hardware byteswapper in SPARCv9, don't let mainstream compilers mislead you. |
| 1209 | // That being said, the strategy to lower this is: |
| 1210 | // - If src is an immediate, comptime-swap it. |
| 1211 | // - If src is in memory then issue an LD*A with #ASI_P_[oppposite-endian] |
| 1212 | // - If src is a register then issue an ST*A with #ASI_P_[oppposite-endian] |
| 1213 | // to a stack slot, then follow with a normal load from said stack slot. |
| 1214 | // This is because on some implementations, ASI-tagged memory operations are non-piplelinable |
| 1215 | // and loads tend to have longer latency than stores, so the sequence will minimize stall. |
| 1216 | // The result will always be either another immediate or stored in a register. |
| 1217 | // TODO: Fold byteswap+store into a single ST*A and load+byteswap into a single LD*A. |
| 1218 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1219 | const operand = try self.resolveInst(ty_op.operand); |
| 1220 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 1221 | switch (operand_ty.zigTypeTag()) { |
| 1222 | .Vector => return self.fail("TODO byteswap for vectors", .{}), |
| 1223 | .Int => { |
| 1224 | const int_info = operand_ty.intInfo(self.target.*); |
| 1225 | if (int_info.bits == 8) break :result operand; |
| 1226 | |
| 1227 | const abi_size = int_info.bits >> 3; |
| 1228 | const abi_align = operand_ty.abiAlignment(self.target.*); |
| 1229 | const opposite_endian_asi = switch (self.target.cpu.arch.endian()) { |
| 1230 | Endian.Big => ASI.asi_primary_little, |
| 1231 | Endian.Little => ASI.asi_primary, |
| 1232 | }; |
| 1233 | |
| 1234 | switch (operand) { |
| 1235 | .immediate => |imm| { |
| 1236 | const swapped = switch (int_info.bits) { |
| 1237 | 16 => @byteSwap(@intCast(u16, imm)), |
| 1238 | 24 => @byteSwap(@intCast(u24, imm)), |
| 1239 | 32 => @byteSwap(@intCast(u32, imm)), |
| 1240 | 40 => @byteSwap(@intCast(u40, imm)), |
| 1241 | 48 => @byteSwap(@intCast(u48, imm)), |
| 1242 | 56 => @byteSwap(@intCast(u56, imm)), |
| 1243 | 64 => @byteSwap(@intCast(u64, imm)), |
| 1244 | else => return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}), |
| 1245 | }; |
| 1246 | break :result .{ .immediate = swapped }; |
| 1247 | }, |
| 1248 | .register => |reg| { |
| 1249 | if (int_info.bits > 64 or @popCount(int_info.bits) != 1) |
| 1250 | return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}); |
| 1251 | |
| 1252 | const off = try self.allocMem(inst, abi_size, abi_align); |
| 1253 | const off_reg = try self.copyToTmpRegister(operand_ty, .{ .immediate = realStackOffset(off) }); |
| 1254 | |
| 1255 | try self.genStoreASI(reg, .sp, off_reg, abi_size, opposite_endian_asi); |
| 1256 | try self.genLoad(reg, .sp, Register, off_reg, abi_size); |
| 1257 | break :result reg; |
| 1258 | }, |
| 1259 | .memory => { |
| 1260 | if (int_info.bits > 64 or @popCount(int_info.bits) != 1) |
| 1261 | return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}); |
| 1262 | |
| 1263 | const addr_reg = try self.copyToTmpRegister(operand_ty, operand); |
| 1264 | const dst_reg = try self.register_manager.allocReg(null, gp); |
| 1265 | |
| 1266 | try self.genLoadASI(dst_reg, addr_reg, .g0, abi_size, opposite_endian_asi); |
| 1267 | break :result dst_reg; |
| 1268 | }, |
| 1269 | .stack_offset => |off| { |
| 1270 | if (int_info.bits > 64 or @popCount(int_info.bits) != 1) |
| 1271 | return self.fail("TODO synthesize SPARCv9 byteswap for other integer sizes", .{}); |
| 1272 | |
| 1273 | const off_reg = try self.copyToTmpRegister(operand_ty, .{ .immediate = realStackOffset(off) }); |
| 1274 | const dst_reg = try self.register_manager.allocReg(null, gp); |
| 1275 | |
| 1276 | try self.genLoadASI(dst_reg, .sp, off_reg, abi_size, opposite_endian_asi); |
| 1277 | break :result dst_reg; |
| 1278 | }, |
| 1279 | else => unreachable, |
| 1280 | } |
| 1281 | }, |
| 1282 | else => unreachable, |
| 1283 | } |
| 1284 | }; |
| 1285 | |
| 1286 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1287 | } |
| 1288 | |
| 1203 | 1289 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 1204 | 1290 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); |
| 1205 | 1291 | |
| ... | ... | @@ -3583,6 +3669,34 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty |
| 3583 | 3669 | } |
| 3584 | 3670 | } |
| 3585 | 3671 | |
| 3672 | fn genLoadASI(self: *Self, value_reg: Register, addr_reg: Register, off_reg: Register, abi_size: u64, asi: ASI) !void { |
| 3673 | switch (abi_size) { |
| 3674 | 1, 2, 4, 8 => { |
| 3675 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 3676 | 1 => .lduba, |
| 3677 | 2 => .lduha, |
| 3678 | 4 => .lduwa, |
| 3679 | 8 => .ldxa, |
| 3680 | else => unreachable, // unexpected abi size |
| 3681 | }; |
| 3682 | |
| 3683 | _ = try self.addInst(.{ |
| 3684 | .tag = tag, |
| 3685 | .data = .{ |
| 3686 | .mem_asi = .{ |
| 3687 | .rd = value_reg, |
| 3688 | .rs1 = addr_reg, |
| 3689 | .rs2 = off_reg, |
| 3690 | .asi = asi, |
| 3691 | }, |
| 3692 | }, |
| 3693 | }); |
| 3694 | }, |
| 3695 | 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), |
| 3696 | else => unreachable, |
| 3697 | } |
| 3698 | } |
| 3699 | |
| 3586 | 3700 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { |
| 3587 | 3701 | switch (mcv) { |
| 3588 | 3702 | .dead => unreachable, |
| ... | ... | @@ -3942,6 +4056,34 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t |
| 3942 | 4056 | } |
| 3943 | 4057 | } |
| 3944 | 4058 | |
| 4059 | fn genStoreASI(self: *Self, value_reg: Register, addr_reg: Register, off_reg: Register, abi_size: u64, asi: ASI) !void { |
| 4060 | switch (abi_size) { |
| 4061 | 1, 2, 4, 8 => { |
| 4062 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 4063 | 1 => .stba, |
| 4064 | 2 => .stha, |
| 4065 | 4 => .stwa, |
| 4066 | 8 => .stxa, |
| 4067 | else => unreachable, // unexpected abi size |
| 4068 | }; |
| 4069 | |
| 4070 | _ = try self.addInst(.{ |
| 4071 | .tag = tag, |
| 4072 | .data = .{ |
| 4073 | .mem_asi = .{ |
| 4074 | .rd = value_reg, |
| 4075 | .rs1 = addr_reg, |
| 4076 | .rs2 = off_reg, |
| 4077 | .asi = asi, |
| 4078 | }, |
| 4079 | }, |
| 4080 | }); |
| 4081 | }, |
| 4082 | 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), |
| 4083 | else => unreachable, |
| 4084 | } |
| 4085 | } |
| 4086 | |
| 3945 | 4087 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 3946 | 4088 | const mcv: MCValue = switch (try codegen.genTypedValue( |
| 3947 | 4089 | self.bin_file, |
| ... | ... | @@ -4257,12 +4399,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 4257 | 4399 | /// Turns stack_offset MCV into a real SPARCv9 stack offset usable for asm. |
| 4258 | 4400 | fn realStackOffset(off: u32) u32 { |
| 4259 | 4401 | return off |
| 4260 | | // SPARCv9 %sp points away from the stack by some amount. |
| 4261 | | + abi.stack_bias |
| 4262 | | // The first couple bytes of each stack frame is reserved |
| 4263 | | // for ABI and hardware purposes. |
| 4264 | | + abi.stack_reserved_area; |
| 4265 | | // Only after that we have the usable stack frame portion. |
| 4402 | // SPARCv9 %sp points away from the stack by some amount. |
| 4403 | + abi.stack_bias |
| 4404 | // The first couple bytes of each stack frame is reserved |
| 4405 | // for ABI and hardware purposes. |
| 4406 | + abi.stack_reserved_area; |
| 4407 | // Only after that we have the usable stack frame portion. |
| 4266 | 4408 | } |
| 4267 | 4409 | |
| 4268 | 4410 | /// Caller must call `CallMCValues.deinit`. |