[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:01 PDT 2026


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

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

>From a3d34de2dda66030268a876e749b1994c38d1e7d Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Tue, 8 Sep 2026 19:33:58 -0400
Subject: [PATCH 1/2] Precommit test for Microsoft C++ ABI exception throwing
 on 32-bit x86

---
 .../test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 clang/test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp

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..a761b35318c12
--- /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 void @_CxxThrowException(ptr [[TMP]], ptr @"_TI1?AUMyException@@") #[[ATTR1:[0-9]+]]
+// CHECK-NEXT:    unreachable
+//
+extern "C" void f() {
+  throw MyException();
+}

>From 01eafd1d7a746873532fcd6b0bbabc72edca8284 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Tue, 8 Sep 2026 20:01:38 -0400
Subject: [PATCH 2/2] [Clang][CodeGen] Inherit calling convention from runtime
 function declarations

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.
---
 clang/lib/CodeGen/CGCall.cpp                   | 18 +++++++++++++++---
 .../CodeGenCXX/microsoft-abi-throw-stdcall.cpp |  2 +-
 2 files changed, 16 insertions(+), 4 deletions(-)

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
index a761b35318c12..fa4e33b5a018d 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-throw-stdcall.cpp
@@ -5,7 +5,7 @@ struct MyException {};
 // CHECK-LABEL: @f(
 // CHECK-NEXT:  entry:
 // CHECK-NEXT:    [[TMP:%.*]] = alloca [[STRUCT_MYEXCEPTION:%.*]], align 1
-// CHECK-NEXT:    call void @_CxxThrowException(ptr [[TMP]], ptr @"_TI1?AUMyException@@") #[[ATTR1:[0-9]+]]
+// CHECK-NEXT:    call x86_stdcallcc void @_CxxThrowException(ptr [[TMP]], ptr @"_TI1?AUMyException@@") #[[ATTR1:[0-9]+]]
 // CHECK-NEXT:    unreachable
 //
 extern "C" void f() {



More information about the cfe-commits mailing list