Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions llvm/include/llvm/MC/MCRegisterInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define LLVM_MC_MCREGISTERINFO_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/MC/LaneBitmask.h"
Expand Down Expand Up @@ -190,6 +191,7 @@ class LLVM_ABI MCRegisterInfo {
const DwarfLLVMRegPair *EHDwarf2LRegs; // Dwarf to LLVM regs mapping EH
DenseMap<MCRegister, int> L2SEHRegs; // LLVM to SEH regs mapping
DenseMap<MCRegister, int> L2CVRegs; // LLVM to CV regs mapping
DenseSet<MCRegister> IgnoredCVRegs;

mutable std::vector<std::vector<MCPhysReg>> RegAliasesCache;
ArrayRef<MCPhysReg> getCachedAliasesOf(MCRegister R) const;
Expand Down Expand Up @@ -353,6 +355,12 @@ class LLVM_ABI MCRegisterInfo {
L2CVRegs[LLVMReg] = CVReg;
}

void ignoreCVReg(MCRegister LLVMReg) { IgnoredCVRegs.insert(LLVMReg); }

bool isIgnoredCVReg(MCRegister LLVMReg) const {
return IgnoredCVRegs.contains(LLVMReg);
}
Copy link
Member

@MacDue MacDue Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could skip the DenseSet and just do:

Suggested change
bool isIgnoredCVReg(MCRegister LLVMReg) const {
return IgnoredCVRegs.contains(LLVMReg);
}
bool isIgnoredCVReg(MCRegister LLVMReg) const {
return (LLVMReg >= AArch64::Z0 && LLVMReg <= AArch64::Z31)
|| (LLVMReg >= AArch64::P0 && LLVMReg <= AArch64::P15);
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure that breaks layering; the AArch64 register names are only defined in the AArch64 backend. I could make it a virtual method, I guess?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't spot this was outside the AArch64 backend, but yes, you could make it virtual or allow registering a std::function/llvm::function_ref callback to filter registers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to virtual method.


/// This method should return the register where the return
/// address can be found.
MCRegister getRARegister() const {
Expand Down
11 changes: 9 additions & 2 deletions llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1318,8 +1318,10 @@ void CodeViewDebug::collectVariableInfoFromMFTable(
TFI->getFrameIndexReference(*Asm->MF, VI.getStackSlot(), FrameReg);
uint16_t CVReg = TRI->getCodeViewRegNum(FrameReg);

assert(!FrameOffset.getScalable() &&
"Frame offsets with a scalable component are not supported");
if (FrameOffset.getScalable()) {
// No encoding currently exists for scalable offsets; bail out.
continue;
}

// Calculate the label ranges.
LocalVarDef DefRange =
Expand Down Expand Up @@ -1410,6 +1412,11 @@ void CodeViewDebug::calculateRanges(
if (Location->FragmentInfo->OffsetInBits % 8)
continue;

if (TRI->isIgnoredCVReg(Location->Register)) {
// No encoding currently exists for this register; bail out.
continue;
}

LocalVarDef DR;
DR.CVRegister = TRI->getCodeViewRegNum(Location->Register);
DR.InMemory = !Location->LoadChain.empty();
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2958,9 +2958,11 @@ StackOffset AArch64FrameLowering::resolveFrameOffsetReference(
StackOffset::get(MFI.getStackSize() - AFI->getCalleeSavedStackSize(),
ObjectOffset);
if (FPAfterSVECalleeSaves) {
assert(-ObjectOffset > (int64_t)AFI->getSVECalleeSavedStackSize() &&
"Math isn't correct for CSRs with FPAfterSVECalleeSaves");
FPOffset += StackOffset::getScalable(AFI->getSVECalleeSavedStackSize());
if (-ObjectOffset <= (int64_t)AFI->getSVECalleeSavedStackSize()) {
FPOffset += StackOffset::getFixed(AFI->getCalleeSavedStackSize());
SPOffset += StackOffset::getFixed(AFI->getCalleeSavedStackSize());
}
}
// Always use the FP for SVE spills if available and beneficial.
if (hasFP(MF) && (SPOffset.getFixed() ||
Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCTargetDesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,20 @@ void AArch64_MC::initLLVMToCVRegMapping(MCRegisterInfo *MRI) {
};
for (const auto &I : RegMap)
MRI->mapLLVMRegToCVReg(I.Reg, static_cast<int>(I.CVReg));
static constexpr MCRegister IgnoredRegs[] = {
AArch64::Z0, AArch64::Z1, AArch64::Z2, AArch64::Z3, AArch64::Z4,
AArch64::Z5, AArch64::Z6, AArch64::Z7, AArch64::Z8, AArch64::Z9,
AArch64::Z10, AArch64::Z11, AArch64::Z12, AArch64::Z13, AArch64::Z14,
AArch64::Z15, AArch64::Z16, AArch64::Z17, AArch64::Z18, AArch64::Z19,
AArch64::Z20, AArch64::Z21, AArch64::Z22, AArch64::Z23, AArch64::Z24,
AArch64::Z25, AArch64::Z26, AArch64::Z27, AArch64::Z28, AArch64::Z29,
AArch64::Z30, AArch64::Z31, AArch64::P0, AArch64::P1, AArch64::P2,
AArch64::P3, AArch64::P4, AArch64::P5, AArch64::P6, AArch64::P7,
AArch64::P8, AArch64::P9, AArch64::P10, AArch64::P11, AArch64::P12,
AArch64::P13, AArch64::P14, AArch64::P15,
};
for (const auto &R : IgnoredRegs)
MRI->ignoreCVReg(R);
}

bool AArch64_MC::isHForm(const MCInst &MI, const MCInstrInfo *MCII) {
Expand Down
161 changes: 161 additions & 0 deletions llvm/test/DebugInfo/COFF/AArch64/codeview-sve.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
; RUN: llc < %s -filetype=obj | llvm-readobj --codeview - | FileCheck %s

; The point of this is mostly just to avoid crashing... there isn't any way
; to encode most of the information we want to encode. But we try to do what
; we can.
;
; Generated from:
;
; #include <arm_sve.h>
; void g();
; svint32_t f(svint32_t aaa, svint32_t bbb, svint32_t *ccc) {
; asm("":::"z0", "z1", "z2", "z3", "z4", "z5", "z6", "z7", "z8", "z9",
; "z10", "z11", "z12", "z13", "z14", "z15", "z16", "z17", "z18",
; "z19", "z20", "z21", "z22", "z23", "z24", "z25", "z26", "z27",
; "z28", "z29", "z30", "z31");
; return aaa**ccc+bbb;
;}

; Emit the SVE type. We represent this as an array with unknown bound.

; CHECK: Array (0x1000) {
; CHECK-NEXT: TypeLeafKind: LF_ARRAY (0x1503)
; CHECK-NEXT: ElementType: int (0x74)
; CHECK-NEXT: IndexType: unsigned __int64 (0x23)
; CHECK-NEXT: SizeOf: 0
; CHECK-NEXT: Name:
; CHECK-NEXT: }

; Emit frame information. This is missing the size of the SVE
; variables, but we can't really do anything about that.

; CHECK: FrameProcSym {
; CHECK-NEXT: Kind: S_FRAMEPROC (0x1012)
; CHECK-NEXT: TotalFrameBytes: 0x10
; CHECK-NEXT: PaddingFrameBytes: 0x0
; CHECK-NEXT: OffsetToPadding: 0x0
; CHECK-NEXT: BytesOfCalleeSavedRegisters: 0x0
; CHECK-NEXT: OffsetOfExceptionHandler: 0x0
; CHECK-NEXT: SectionIdOfExceptionHandler: 0x0
; CHECK-NEXT: Flags [ (0x116008)
; CHECK-NEXT: HasInlineAssembly (0x8)
; CHECK-NEXT: OptimizedForSpeed (0x100000)
; CHECK-NEXT: SafeBuffers (0x2000)
; CHECK-NEXT: ]
; CHECK-NEXT: LocalFramePtrReg: ARM64_NOREG (0x0)
; CHECK-NEXT: ParamFramePtrReg: ARM64_NOREG (0x0)
; CHECK-NEXT: }

; Emit the symbols for the local variables.
;
; ccc is a normal pointer.
;
; We can't represent bbb anywhere in its range; there's no way to name Z
; registers, and no way to express its location on the stack relative
; to the stack pointer when it's spilled.
;
; In the middle of the range, aaa happens to have a scalable offset of zero,
; so we can represent it while it's on the stack.

; CHECK-NEXT: LocalSym {
; CHECK-NEXT: Kind: S_LOCAL (0x113E)
; CHECK-NEXT: Type: 0x1000
; CHECK-NEXT: Flags [ (0x1)
; CHECK-NEXT: IsParameter (0x1)
; CHECK-NEXT: ]
; CHECK-NEXT: VarName: aaa
; CHECK-NEXT: }
; CHECK-NEXT: DefRangeRegisterRelSym {
; CHECK-NEXT: Kind: S_DEFRANGE_REGISTER_REL (0x1145)
; CHECK-NEXT: BaseRegister: ARM64_SP (0x51)
; CHECK-NEXT: HasSpilledUDTMember: No
; CHECK-NEXT: OffsetInParent: 0
; CHECK-NEXT: BasePointerOffset: 0
; CHECK-NEXT: LocalVariableAddrRange {
; CHECK-NEXT: OffsetStart: .text+0x58
; CHECK-NEXT: ISectStart: 0x0
; CHECK-NEXT: Range: 0xC
; CHECK-NEXT: }
; CHECK-NEXT: }
; CHECK-NEXT: LocalSym {
; CHECK-NEXT: Kind: S_LOCAL (0x113E)
; CHECK-NEXT: Type: 0x1000
; CHECK-NEXT: Flags [ (0x101)
; CHECK-NEXT: IsOptimizedOut (0x100)
; CHECK-NEXT: IsParameter (0x1)
; CHECK-NEXT: ]
; CHECK-NEXT: VarName: bbb
; CHECK-NEXT: }
; CHECK-NEXT: LocalSym {
; CHECK-NEXT: Kind: S_LOCAL (0x113E)
; CHECK-NEXT: Type: * (0x1001)
; CHECK-NEXT: Flags [ (0x1)
; CHECK-NEXT: IsParameter (0x1)
; CHECK-NEXT: ]
; CHECK-NEXT: VarName: ccc
; CHECK-NEXT: }
; CHECK-NEXT: DefRangeRegisterSym {
; CHECK-NEXT: Kind: S_DEFRANGE_REGISTER (0x1141)
; CHECK-NEXT: Register: ARM64_X0 (0x32)
; CHECK-NEXT: MayHaveNoName: 0
; CHECK-NEXT: LocalVariableAddrRange {
; CHECK-NEXT: OffsetStart: .text+0x0
; CHECK-NEXT: ISectStart: 0x0
; CHECK-NEXT: Range: 0xBC
; CHECK-NEXT: }
; CHECK-NEXT: }
; CHECK-NEXT: ProcEnd {
; CHECK-NEXT: Kind: S_PROC_ID_END (0x114F)
; CHECK-NEXT: }

target triple = "aarch64-unknown-windows-msvc19.33.0"

; Function Attrs: mustprogress nounwind uwtable vscale_range(1,16)
define dso_local <vscale x 4 x i32> @"?f@@YAU__SVInt32_t@__clang@@U12@0PEAU12@@Z"(<vscale x 4 x i32> %aaa, <vscale x 4 x i32> %bbb, ptr noundef readonly captures(none) %ccc) local_unnamed_addr #0 !dbg !10 {
entry:
#dbg_value(ptr %ccc, !23, !DIExpression(), !26)
#dbg_value(<vscale x 4 x i32> %bbb, !24, !DIExpression(), !26)
#dbg_value(<vscale x 4 x i32> %aaa, !25, !DIExpression(), !26)
tail call void asm sideeffect "", "~{z0},~{z1},~{z2},~{z3},~{z4},~{z5},~{z6},~{z7},~{z8},~{z9},~{z10},~{z11},~{z12},~{z13},~{z14},~{z15},~{z16},~{z17},~{z18},~{z19},~{z20},~{z21},~{z22},~{z23},~{z24},~{z25},~{z26},~{z27},~{z28},~{z29},~{z30},~{z31}"() #1, !dbg !27, !srcloc !28
%0 = load <vscale x 4 x i32>, ptr %ccc, align 16, !dbg !27
%mul = mul <vscale x 4 x i32> %0, %aaa, !dbg !27
%add = add <vscale x 4 x i32> %mul, %bbb, !dbg !27
ret <vscale x 4 x i32> %add, !dbg !27
}

attributes #0 = { mustprogress nounwind uwtable vscale_range(1,16) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+fp-armv8,+fullfp16,+neon,+sve,+v8a,-fmv" }
attributes #1 = { nounwind }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
!llvm.ident = !{!9}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 21.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "-", directory: "", checksumkind: CSK_MD5, checksum: "e54fc2ba768e4a43f64b8a9d03a374d6")
!2 = !{i32 2, !"CodeView", i32 1}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 2}
!5 = !{i32 8, !"PIC Level", i32 2}
!6 = !{i32 7, !"uwtable", i32 2}
!7 = !{i32 7, !"frame-pointer", i32 1}
!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
!9 = !{!"clang version 21.0.0git"}
!10 = distinct !DISubprogram(name: "f", linkageName: "?f@@YAU__SVInt32_t@__clang@@U12@0PEAU12@@Z", scope: !11, file: !11, line: 2, type: !12, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !22)
!11 = !DIFile(filename: "<stdin>", directory: "", checksumkind: CSK_MD5, checksum: "e54fc2ba768e4a43f64b8a9d03a374d6")
!12 = !DISubroutineType(types: !13)
!13 = !{!14, !14, !14, !21}
!14 = !DIDerivedType(tag: DW_TAG_typedef, name: "svint32_t", file: !15, line: 30, baseType: !16)
!15 = !DIFile(filename: "arm_sve.h", directory: "", checksumkind: CSK_MD5, checksum: "34027e9d24f4b03c6e5370869d5cc907")
!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SVInt32_t", file: !1, baseType: !17)
!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, flags: DIFlagVector, elements: !19)
!18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!19 = !{!20}
!20 = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 2, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus))
!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
!22 = !{!23, !24, !25}
!23 = !DILocalVariable(name: "ccc", arg: 3, scope: !10, file: !11, line: 2, type: !21)
!24 = !DILocalVariable(name: "bbb", arg: 2, scope: !10, file: !11, line: 2, type: !14)
!25 = !DILocalVariable(name: "aaa", arg: 1, scope: !10, file: !11, line: 2, type: !14)
!26 = !DILocation(line: 0, scope: !10)
!27 = !DILocation(line: 2, scope: !10)
!28 = !{i64 98}
Loading