authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2023-01-04 16:38:15+07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-28 16:46:04-07:00
log83e6223192acd635275e67614e55b7a4c579a969
treeaa6786e2626f2d8afba4c40698b5a71e6b4d79eb
parent486ab3852e22f8d5ba474691a5b068f1f1729f2e

stage2: sparc64: Implement airByteSwap


3 files changed, 189 insertions(+), 7 deletions(-)

src/arch/sparc64/CodeGen.zig+149-7
......@@ -22,6 +22,7 @@ const Type = @import("../../type.zig").Type;
2222const CodeGenError = codegen.CodeGenError;
2323const Result = @import("../../codegen.zig").Result;
2424const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
25const Endian = std.builtin.Endian;
2526
2627const build_options = @import("build_options");
2728
......@@ -30,6 +31,7 @@ const abi = @import("abi.zig");
3031const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
3132const errUnionErrorOffset = codegen.errUnionErrorOffset;
3233const Instruction = bits.Instruction;
34const ASI = Instruction.ASI;
3335const ShiftWidth = Instruction.ShiftWidth;
3436const RegisterManager = abi.RegisterManager;
3537const RegisterLock = RegisterManager.RegisterLock;
......@@ -615,7 +617,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
615617 .clz => try self.airClz(inst),
616618 .ctz => try self.airCtz(inst),
617619 .popcount => try self.airPopcount(inst),
618 .byte_swap => @panic("TODO try self.airByteSwap(inst)"),
620 .byte_swap => try self.airByteSwap(inst),
619621 .bit_reverse => try self.airBitReverse(inst),
620622 .tag_name => try self.airTagName(inst),
621623 .error_name => try self.airErrorName(inst),
......@@ -1200,6 +1202,90 @@ fn airBreakpoint(self: *Self) !void {
12001202 return self.finishAirBookkeeping();
12011203}
12021204
1205fn 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
12031289fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
12041290 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});
12051291
......@@ -3583,6 +3669,34 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty
35833669 }
35843670}
35853671
3672fn 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
35863700fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
35873701 switch (mcv) {
35883702 .dead => unreachable,
......@@ -3942,6 +4056,34 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t
39424056 }
39434057}
39444058
4059fn 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
39454087fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
39464088 const mcv: MCValue = switch (try codegen.genTypedValue(
39474089 self.bin_file,
......@@ -4257,12 +4399,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
42574399/// Turns stack_offset MCV into a real SPARCv9 stack offset usable for asm.
42584400fn realStackOffset(off: u32) u32 {
42594401 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.
42664408}
42674409
42684410/// Caller must call `CallMCValues.deinit`.
src/arch/sparc64/Emit.zig+10
......@@ -91,6 +91,11 @@ pub fn emitMir(
9191 .lduw => try emit.mirArithmetic3Op(inst),
9292 .ldx => try emit.mirArithmetic3Op(inst),
9393
94 .lduba => unreachable,
95 .lduha => unreachable,
96 .lduwa => unreachable,
97 .ldxa => unreachable,
98
9499 .@"and" => try emit.mirArithmetic3Op(inst),
95100 .@"or" => try emit.mirArithmetic3Op(inst),
96101 .xor => try emit.mirArithmetic3Op(inst),
......@@ -127,6 +132,11 @@ pub fn emitMir(
127132 .stw => try emit.mirArithmetic3Op(inst),
128133 .stx => try emit.mirArithmetic3Op(inst),
129134
135 .stba => unreachable,
136 .stha => unreachable,
137 .stwa => unreachable,
138 .stxa => unreachable,
139
130140 .sub => try emit.mirArithmetic3Op(inst),
131141 .subcc => try emit.mirArithmetic3Op(inst),
132142
src/arch/sparc64/Mir.zig+30
......@@ -15,6 +15,7 @@ const bits = @import("bits.zig");
1515const Air = @import("../../Air.zig");
1616
1717const Instruction = bits.Instruction;
18const ASI = bits.Instruction.ASI;
1819const Register = bits.Register;
1920
2021instructions: std.MultiArrayList(Inst).Slice,
......@@ -70,6 +71,16 @@ pub const Inst = struct {
7071 lduw,
7172 ldx,
7273
74 /// A.28 Load Integer from Alternate Space
75 /// This uses the mem_asi field.
76 /// Note that the ldda variant of this instruction is deprecated, so do not emit
77 /// it unless specifically requested (e.g. by inline assembly).
78 // TODO add other operations.
79 lduba,
80 lduha,
81 lduwa,
82 ldxa,
83
7384 /// A.31 Logical Operations
7485 /// This uses the arithmetic_3op field.
7586 // TODO add other operations.
......@@ -132,6 +143,16 @@ pub const Inst = struct {
132143 stw,
133144 stx,
134145
146 /// A.55 Store Integer into Alternate Space
147 /// This uses the mem_asi field.
148 /// Note that the stda variant of this instruction is deprecated, so do not emit
149 /// it unless specifically requested (e.g. by inline assembly).
150 // TODO add other operations.
151 stba,
152 stha,
153 stwa,
154 stxa,
155
135156 /// A.56 Subtract
136157 /// This uses the arithmetic_3op field.
137158 // TODO add other operations.
......@@ -241,6 +262,15 @@ pub const Inst = struct {
241262 inst: Index,
242263 },
243264
265 /// ASI-tagged memory operations.
266 /// Used by e.g. ldxa, stxa
267 mem_asi: struct {
268 rd: Register,
269 rs1: Register,
270 rs2: Register = .g0,
271 asi: ASI,
272 },
273
244274 /// Membar mask, controls the barrier behavior
245275 /// Used by e.g. membar
246276 membar_mask: struct {