| ... | @@ -22,6 +22,7 @@ const Type = @import("../../type.zig").Type; | ... | @@ -22,6 +22,7 @@ const Type = @import("../../type.zig").Type; |
| 22 | const CodeGenError = codegen.CodeGenError; | 22 | const CodeGenError = codegen.CodeGenError; |
| 23 | const Result = @import("../../codegen.zig").Result; | 23 | const Result = @import("../../codegen.zig").Result; |
| 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| | 25 | const Endian = std.builtin.Endian; |
| 25 | | 26 | |
| 26 | const build_options = @import("build_options"); | 27 | const build_options = @import("build_options"); |
| 27 | | 28 | |
| ... | @@ -30,6 +31,7 @@ const abi = @import("abi.zig"); | ... | @@ -30,6 +31,7 @@ const abi = @import("abi.zig"); |
| 30 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | 31 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 31 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | 32 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 32 | const Instruction = bits.Instruction; | 33 | const Instruction = bits.Instruction; |
| | 34 | const ASI = Instruction.ASI; |
| 33 | const ShiftWidth = Instruction.ShiftWidth; | 35 | const ShiftWidth = Instruction.ShiftWidth; |
| 34 | const RegisterManager = abi.RegisterManager; | 36 | const RegisterManager = abi.RegisterManager; |
| 35 | const RegisterLock = RegisterManager.RegisterLock; | 37 | const 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 | } |
| 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 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 1289 | fn 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}); |
| 1205 | | 1291 | |
| ... | @@ -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 | } |
| 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 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { | 3700 | fn 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 | } |
| 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 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { | 4087 | fn 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. |
| 4258 | fn realStackOffset(off: u32) u32 { | 4400 | fn realStackOffset(off: u32) u32 { |
| 4259 | return off | 4401 | 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_bias | 4403 | + abi.stack_bias |
| 4262 | // The first couple bytes of each stack frame is reserved | 4404 | // 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 | } |
| 4267 | | 4409 | |
| 4268 | /// Caller must call `CallMCValues.deinit`. | 4410 | /// Caller must call `CallMCValues.deinit`. |