[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