Skip to content

Conversation

@momo5502
Copy link
Contributor

Changing the CC of local functions to fastcc in GlobalOpt causes the PDB to misalign.
Updating the debug info is requried to reflect the change in the PDB.

This fixes #144301

@llvmbot
Copy link
Member

llvmbot commented Jun 16, 2025

@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-llvm-transforms

Author: Maurice Heumann (momo5502)

Changes

Changing the CC of local functions to fastcc in GlobalOpt causes the PDB to misalign.
Updating the debug info is requried to reflect the change in the PDB.

This fixes #144301


Full diff: https://github.com/llvm/llvm-project/pull/144303.diff

1 Files Affected:

  • (modified) llvm/lib/Transforms/IPO/GlobalOpt.cpp (+18)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 7db0586386506..b0f1dee415efd 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -1920,6 +1920,14 @@ static void RemovePreallocated(Function *F) {
   }
 }
 
+static unsigned char GetDebugInfoFastCC(const Triple &Triple) {
+  if (Triple.isOSWindows() && Triple.isArch32Bit()) {
+    return llvm::dwarf::DW_CC_BORLAND_msfastcall;
+  }
+
+  return llvm::dwarf::DW_CC_normal;
+}
+
 static bool
 OptimizeFunctions(Module &M,
                   function_ref<TargetLibraryInfo &(Function &)> GetTLI,
@@ -1938,6 +1946,9 @@ OptimizeFunctions(Module &M,
     if (hasOnlyColdCalls(F, GetBFI, ChangeableCCCache))
       AllCallsCold.push_back(&F);
 
+  unsigned char DebugInfoFastCC =
+      GetDebugInfoFastCC(Triple(M.getTargetTriple()));
+
   // Optimize functions.
   for (Function &F : llvm::make_early_inc_range(M)) {
     // Don't perform global opt pass on naked functions; we don't want fast
@@ -2021,6 +2032,13 @@ OptimizeFunctions(Module &M,
       // Fast calling convention.
       F.setCallingConv(CallingConv::Fast);
       ChangeCalleesToFastCall(&F);
+
+      if (F.getSubprogram()) {
+        DISubprogram *SP = F.getSubprogram();
+        auto Temp = SP->getType()->cloneWithCC(DebugInfoFastCC);
+        SP->replaceType(MDNode::replaceWithPermanent(std::move(Temp)));
+      }
+
       ++NumFastCallFns;
       Changed = true;
     }

Comment on lines +1923 to +1930
static unsigned char GetDebugInfoFastCC(const Triple &Triple) {
if (Triple.isOSWindows() && Triple.isArch32Bit()) {
return llvm::dwarf::DW_CC_BORLAND_msfastcall;
}

return llvm::dwarf::DW_CC_normal;
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems a bit brittle, if other targets had other calling convention choices - perhaps this logic should go wherever the CC is determined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. However, I think the mapping for fastcc is determined by a table definition for each architecture. I feel like integrating this there is not easily feasible.

Do you have a recommendation on how this could be done better?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you point to the table definition, we/someone could take a look to see how practical it is.

Copy link
Contributor Author

@momo5502 momo5502 Jul 3, 2025

Choose a reason for hiding this comment

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

The decision how FastCC is being lowered happens here:

CCIfCC<"CallingConv::Fast", CCDelegateTo<CC_X86_32_FastCC>>,

Here is where the dwarf CC is translated to the CodeView format:

case dwarf::DW_CC_BORLAND_msfastcall: return CallingConvention::NearFast;

I feel like the reason this issue arises in the first place is the redundancy for calling convention specifications: the information is stored in the function, as well as the debug info.

What would also work would be a pseudo dwarf calling convention for fastCC that can then be mapped onto the real CC for each respective platform. However, I assume that for most platforms this is just the normal calling convention, which is why this approach seemed to cause the least overhead.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Perhaps @zmodem has some thoughts on the PDB side of things, and @nikic has some thoughts on the broader LLVM architectural issue.

Yeah, I'd be open to the right direction being each architecture having a mapping from their real CCs to the DWARF CC they want to use for them, and removing the CC from the LLVM IR debug info metadata.

Copy link
Collaborator

Choose a reason for hiding this comment

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

No PDB specific thoughts really, but it does sound redundant to store the calling convention in the debug info metadata as well (and error prone as it needs to stay in sync).

When we lower the debug metadata (or translate it to codeview), do we have an easy way to get to the llvm::Function? If so it sounds like we should just get the calling convention from there?

Copy link
Collaborator

Choose a reason for hiding this comment

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

We do - the subprogram data is attached to the llvm::Function (so, technically, you could be looking at a subprogram without knowing what function it came from, but that's fixable)

I guess in the case where a DWARF function declaration is emitted with no associated LLVM IR, it might be a problem - in theory we could/may need to still carry a calling convention down through the debug info metadata to handle cases like that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

If we don't go the route of dropping it from the metadata, maybe the verifier could check for Function/metadata calling convention consistency?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah - I'm not 100% sure we carry CC on declarations, so someone'd have to check that/see if we need it anyway. And if we do, yeah, checking for consistency would be good - and maybe the metadata would/could carry the same type of CC as the IR, rather than the debug info one - and then only convert from real CC to DWARF CC at the backend.

@momo5502
Copy link
Contributor Author

I'm going to close this. Turns out DW_CC_BORLAND_msfastcall is not at all the same as fastcc. Seems like there is no way to represent fastcc in the PDB, so it doesn't matter right now.

@momo5502 momo5502 closed this Jul 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PDB shows wrong calling convention when compiling static functions on Windows x86

5 participants