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;...@@ -22,6 +22,7 @@ const Type = @import("../../type.zig").Type;
22const CodeGenError = codegen.CodeGenError;22const CodeGenError = codegen.CodeGenError;
23const Result = @import("../../codegen.zig").Result;23const Result = @import("../../codegen.zig").Result;
24const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;24const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
25const Endian = std.builtin.Endian;
2526
26const build_options = @import("build_options");27const build_options = @import("build_options");
2728
...@@ -30,6 +31,7 @@ const abi = @import("abi.zig");...@@ -30,6 +31,7 @@ const abi = @import("abi.zig");
30const errUnionPayloadOffset = codegen.errUnionPayloadOffset;31const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
31const errUnionErrorOffset = codegen.errUnionErrorOffset;32const errUnionErrorOffset = codegen.errUnionErrorOffset;
32const Instruction = bits.Instruction;33const Instruction = bits.Instruction;
34const ASI = Instruction.ASI;
33const ShiftWidth = Instruction.ShiftWidth;35const ShiftWidth = Instruction.ShiftWidth;
34const RegisterManager = abi.RegisterManager;36const RegisterManager = abi.RegisterManager;
35const RegisterLock = RegisterManager.RegisterLock;37const RegisterLock = RegisterManager.RegisterLock;
...@@ -615,7 +617,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -615,7 +617,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
615 .clz => try self.airClz(inst),617 .clz => try self.airClz(inst),
616 .ctz => try self.airCtz(inst),618 .ctz => try self.airCtz(inst),
617 .popcount => try self.airPopcount(inst),619 .popcount => try self.airPopcount(inst),
618 .byte_swap => @panic("TODO try self.airByteSwap(inst)"),620 .byte_swap => try self.airByteSwap(inst),
619 .bit_reverse => try self.airBitReverse(inst),621 .bit_reverse => try self.airBitReverse(inst),
620 .tag_name => try self.airTagName(inst),622 .tag_name => try self.airTagName(inst),
621 .error_name => try self.airErrorName(inst),623 .error_name => try self.airErrorName(inst),
...@@ -1200,6 +1202,90 @@ fn airBreakpoint(self: *Self) !void {...@@ -1200,6 +1202,90 @@ fn airBreakpoint(self: *Self) !void {
1200 return self.finishAirBookkeeping();1202 return self.finishAirBookkeeping();
1201}1203}
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
1203fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {1289fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
1204 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});1290 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...@@ -3583,6 +3669,34 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty
3583 }3669 }
3584}3670}
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
3586fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {3700fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3587 switch (mcv) {3701 switch (mcv) {
3588 .dead => unreachable,3702 .dead => unreachable,
...@@ -3942,6 +4056,34 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t...@@ -3942,6 +4056,34 @@ fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_t
3942 }4056 }
3943}4057}
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
3945fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {4087fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
3946 const mcv: MCValue = switch (try codegen.genTypedValue(4088 const mcv: MCValue = switch (try codegen.genTypedValue(
3947 self.bin_file,4089 self.bin_file,
...@@ -4257,12 +4399,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {...@@ -4257,12 +4399,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
4257/// Turns stack_offset MCV into a real SPARCv9 stack offset usable for asm.4399/// Turns stack_offset MCV into a real SPARCv9 stack offset usable for asm.
4258fn realStackOffset(off: u32) u32 {4400fn realStackOffset(off: u32) u32 {
4259 return off4401 return off
4260 // SPARCv9 %sp points away from the stack by some amount.4402 // SPARCv9 %sp points away from the stack by some amount.
4261 + abi.stack_bias4403 + abi.stack_bias
4262 // The first couple bytes of each stack frame is reserved4404 // The first couple bytes of each stack frame is reserved
4263 // for ABI and hardware purposes.4405 // for ABI and hardware purposes.
4264 + abi.stack_reserved_area;4406 + abi.stack_reserved_area;
4265 // Only after that we have the usable stack frame portion.4407 // Only after that we have the usable stack frame portion.
4266}4408}
42674409
4268/// Caller must call `CallMCValues.deinit`.4410/// Caller must call `CallMCValues.deinit`.
src/arch/sparc64/Emit.zig+10
...@@ -91,6 +91,11 @@ pub fn emitMir(...@@ -91,6 +91,11 @@ pub fn emitMir(
91 .lduw => try emit.mirArithmetic3Op(inst),91 .lduw => try emit.mirArithmetic3Op(inst),
92 .ldx => try emit.mirArithmetic3Op(inst),92 .ldx => try emit.mirArithmetic3Op(inst),
9393
94 .lduba => unreachable,
95 .lduha => unreachable,
96 .lduwa => unreachable,
97 .ldxa => unreachable,
98
94 .@"and" => try emit.mirArithmetic3Op(inst),99 .@"and" => try emit.mirArithmetic3Op(inst),
95 .@"or" => try emit.mirArithmetic3Op(inst),100 .@"or" => try emit.mirArithmetic3Op(inst),
96 .xor => try emit.mirArithmetic3Op(inst),101 .xor => try emit.mirArithmetic3Op(inst),
...@@ -127,6 +132,11 @@ pub fn emitMir(...@@ -127,6 +132,11 @@ pub fn emitMir(
127 .stw => try emit.mirArithmetic3Op(inst),132 .stw => try emit.mirArithmetic3Op(inst),
128 .stx => try emit.mirArithmetic3Op(inst),133 .stx => try emit.mirArithmetic3Op(inst),
129134
135 .stba => unreachable,
136 .stha => unreachable,
137 .stwa => unreachable,
138 .stxa => unreachable,
139
130 .sub => try emit.mirArithmetic3Op(inst),140 .sub => try emit.mirArithmetic3Op(inst),
131 .subcc => try emit.mirArithmetic3Op(inst),141 .subcc => try emit.mirArithmetic3Op(inst),
132142
src/arch/sparc64/Mir.zig+30
...@@ -15,6 +15,7 @@ const bits = @import("bits.zig");...@@ -15,6 +15,7 @@ const bits = @import("bits.zig");
15const Air = @import("../../Air.zig");15const Air = @import("../../Air.zig");
1616
17const Instruction = bits.Instruction;17const Instruction = bits.Instruction;
18const ASI = bits.Instruction.ASI;
18const Register = bits.Register;19const Register = bits.Register;
1920
20instructions: std.MultiArrayList(Inst).Slice,21instructions: std.MultiArrayList(Inst).Slice,
...@@ -70,6 +71,16 @@ pub const Inst = struct {...@@ -70,6 +71,16 @@ pub const Inst = struct {
70 lduw,71 lduw,
71 ldx,72 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
73 /// A.31 Logical Operations84 /// A.31 Logical Operations
74 /// This uses the arithmetic_3op field.85 /// This uses the arithmetic_3op field.
75 // TODO add other operations.86 // TODO add other operations.
...@@ -132,6 +143,16 @@ pub const Inst = struct {...@@ -132,6 +143,16 @@ pub const Inst = struct {
132 stw,143 stw,
133 stx,144 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
135 /// A.56 Subtract156 /// A.56 Subtract
136 /// This uses the arithmetic_3op field.157 /// This uses the arithmetic_3op field.
137 // TODO add other operations.158 // TODO add other operations.
...@@ -241,6 +262,15 @@ pub const Inst = struct {...@@ -241,6 +262,15 @@ pub const Inst = struct {
241 inst: Index,262 inst: Index,
242 },263 },
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
244 /// Membar mask, controls the barrier behavior274 /// Membar mask, controls the barrier behavior
245 /// Used by e.g. membar275 /// Used by e.g. membar
246 membar_mask: struct {276 membar_mask: struct {