[clang] [Clang][CodeGen] Inherit calling convention from runtime function declarations (PR #222200)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 8 17:04:43 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: AZero13 (AZero13)

<details>
<summary>Changes</summary>

On 32-bit x86 targets using the Microsoft C++ ABI, `_CxxThrowException` requires the `__stdcall` (`x86_stdcallcc`) calling convention. While `MicrosoftCXXABI::getThrowFn()` correctly set this calling convention on the function declaration, the generated LLVM IR `call` instructions were incorrectly emitted using the default `__cdecl` calling convention. This mismatch corrupted the stack (since `stdcall` requires the callee to clean up arguments, but `cdecl` expects the caller to do it), leading to crashes when throwing exceptions.

Fixes #<!-- -->158302

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


2 Files Affected:

- (modified) clang/lib/CodeGen/CGCall.cpp (+15-3) 
- (added) clang/test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp (+13) 


``````````diff
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 1221829871b9f..50e6403846259 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5487,15 +5487,22 @@ void CodeGenFunction::EmitNoreturnRuntimeCallOrInvoke(
   SmallVector<llvm::OperandBundleDef, 1> BundleList =
       getBundlesForFunclet(callee.getCallee());
 
+  llvm::CallingConv::ID CC;
+  // Get the calling convention from the callee if it's a function.
+  if (auto *Fn = dyn_cast<llvm::Function>(callee.getCallee()))
+    CC = Fn->getCallingConv();
+  else
+    CC = getRuntimeCC();
+
   if (getInvokeDest()) {
     llvm::InvokeInst *invoke = Builder.CreateInvoke(
         callee, getUnreachableBlock(), getInvokeDest(), args, BundleList);
     invoke->setDoesNotReturn();
-    invoke->setCallingConv(getRuntimeCC());
+    invoke->setCallingConv(CC);
   } else {
     llvm::CallInst *call = Builder.CreateCall(callee, args, BundleList);
     call->setDoesNotReturn();
-    call->setCallingConv(getRuntimeCC());
+    call->setCallingConv(CC);
     Builder.CreateUnreachable();
   }
 }
@@ -5513,7 +5520,12 @@ CodeGenFunction::EmitRuntimeCallOrInvoke(llvm::FunctionCallee callee,
                                          ArrayRef<llvm::Value *> args,
                                          const Twine &name) {
   llvm::CallBase *call = EmitCallOrInvoke(callee, args, name);
-  call->setCallingConv(getRuntimeCC());
+  llvm::CallingConv::ID CC;
+  if (auto *Fn = dyn_cast<llvm::Function>(callee.getCallee()))
+    CC = Fn->getCallingConv();
+  else
+    CC = getRuntimeCC();
+  call->setCallingConv(CC);
   return call;
 }
 
diff --git a/clang/test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp b/clang/test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp
new file mode 100644
index 0000000000000..fa4e33b5a018d
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp
@@ -0,0 +1,13 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-pc-windows-msvc19.0.0 -fcxx-exceptions -fms-extensions -fexceptions | FileCheck %s
+
+struct MyException {};
+// CHECK-LABEL: @f(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[TMP:%.*]] = alloca [[STRUCT_MYEXCEPTION:%.*]], align 1
+// CHECK-NEXT:    call x86_stdcallcc void @_CxxThrowException(ptr [[TMP]], ptr @"_TI1?AUMyException@@") #[[ATTR1:[0-9]+]]
+// CHECK-NEXT:    unreachable
+//
+extern "C" void f() {
+  throw MyException();
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/222200


More information about the cfe-commits mailing list