[clang] [Clang][CodeGen] Fix crash when compiling naked lambdas (PR #165524)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 29 01:08:22 PDT 2025
https://github.com/alcxpr updated https://github.com/llvm/llvm-project/pull/165524
>From f303f139966b6753718aa3e12a2b3e2dc4ef5a4c Mon Sep 17 00:00:00 2001
From: typeal <type.alplusplus at gmail.com>
Date: Wed, 29 Oct 2025 14:59:42 +0700
Subject: [PATCH] [Clang][CodeGen] Fix crash when compiling naked lambdas
Skip instance and lambda prologue emission when a lambda is marked `naked`,
preventing invalid access to `this` during code generation.
---
clang/lib/CodeGen/CodeGenFunction.cpp | 82 +++++++++++++++-----------
clang/test/CodeGenCXX/naked-lambda.cpp | 20 +++++++
2 files changed, 66 insertions(+), 36 deletions(-)
create mode 100644 clang/test/CodeGenCXX/naked-lambda.cpp
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 88628530cf66b..11e9e708f9b73 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -46,6 +46,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/Support/CRC.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/xxhash.h"
#include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
@@ -1271,50 +1272,59 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
}
}
- EmitFunctionProlog(*CurFnInfo, CurFn, Args);
+ EmitFunctionProlog(*CurFnInfo, CurFn, Args);
if (const CXXMethodDecl *MD = dyn_cast_if_present<CXXMethodDecl>(D);
MD && !MD->isStatic()) {
bool IsInLambda =
MD->getParent()->isLambda() && MD->getOverloadedOperator() == OO_Call;
- if (MD->isImplicitObjectMemberFunction())
- CGM.getCXXABI().EmitInstanceFunctionProlog(*this);
- if (IsInLambda) {
- // We're in a lambda; figure out the captures.
- MD->getParent()->getCaptureFields(LambdaCaptureFields,
- LambdaThisCaptureField);
- if (LambdaThisCaptureField) {
- // If the lambda captures the object referred to by '*this' - either by
- // value or by reference, make sure CXXThisValue points to the correct
- // object.
-
- // Get the lvalue for the field (which is a copy of the enclosing object
- // or contains the address of the enclosing object).
- LValue ThisFieldLValue = EmitLValueForLambdaField(LambdaThisCaptureField);
- if (!LambdaThisCaptureField->getType()->isPointerType()) {
- // If the enclosing object was captured by value, just use its
- // address. Sign this pointer.
- CXXThisValue = ThisFieldLValue.getPointer(*this);
- } else {
- // Load the lvalue pointed to by the field, since '*this' was captured
- // by reference.
- CXXThisValue =
- EmitLoadOfLValue(ThisFieldLValue, SourceLocation()).getScalarVal();
+
+ const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(D);
+ bool IsNaked = FD && FD->hasAttr<NakedAttr>();
+
+ if (!IsNaked) {
+ if (MD->isImplicitObjectMemberFunction())
+ CGM.getCXXABI().EmitInstanceFunctionProlog(*this);
+
+ if (IsInLambda) {
+ // We're in a lambda; figure out the captures.
+ MD->getParent()->getCaptureFields(LambdaCaptureFields,
+ LambdaThisCaptureField);
+ if (LambdaThisCaptureField) {
+ // If the lambda captures the object referred to by '*this' - either by
+ // value or by reference, make sure CXXThisValue points to the correct
+ // object.
+
+ // Get the lvalue for the field (which is a copy of the enclosing object
+ // or contains the address of the enclosing object).
+ LValue ThisFieldLValue =
+ EmitLValueForLambdaField(LambdaThisCaptureField);
+ if (!LambdaThisCaptureField->getType()->isPointerType()) {
+ // If the enclosing object was captured by value, just use its
+ // address. Sign this pointer.
+ CXXThisValue = ThisFieldLValue.getPointer(*this);
+ } else {
+ // Load the lvalue pointed to by the field, since '*this' was captured
+ // by reference.
+ CXXThisValue =
+ EmitLoadOfLValue(ThisFieldLValue, SourceLocation()).getScalarVal();
+ }
}
- }
- for (auto *FD : MD->getParent()->fields()) {
- if (FD->hasCapturedVLAType()) {
- auto *ExprArg = EmitLoadOfLValue(EmitLValueForLambdaField(FD),
- SourceLocation()).getScalarVal();
- auto VAT = FD->getCapturedVLAType();
- VLASizeMap[VAT->getSizeExpr()] = ExprArg;
+
+ for (auto *FD : MD->getParent()->fields()) {
+ if (FD->hasCapturedVLAType()) {
+ auto *ExprArg = EmitLoadOfLValue(EmitLValueForLambdaField(FD),
+ SourceLocation()).getScalarVal();
+ auto VAT = FD->getCapturedVLAType();
+ VLASizeMap[VAT->getSizeExpr()] = ExprArg;
+ }
}
+ } else if (MD->isImplicitObjectMemberFunction()) {
+ // Not in a lambda; just use 'this' from the method.
+ // FIXME: Should we generate a new load for each use of 'this'? The
+ // fast register allocator would be happier...
+ CXXThisValue = CXXABIThisValue;
}
- } else if (MD->isImplicitObjectMemberFunction()) {
- // Not in a lambda; just use 'this' from the method.
- // FIXME: Should we generate a new load for each use of 'this'? The
- // fast register allocator would be happier...
- CXXThisValue = CXXABIThisValue;
}
// Check the 'this' pointer once per function, if it's available.
diff --git a/clang/test/CodeGenCXX/naked-lambda.cpp b/clang/test/CodeGenCXX/naked-lambda.cpp
new file mode 100644
index 0000000000000..27ea1af09da01
--- /dev/null
+++ b/clang/test/CodeGenCXX/naked-lambda.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -S %s -o - | FileCheck %s --check-prefix=ASM
+
+void test_naked_lambda() {
+ auto l = []() __attribute__((naked)) {
+ asm volatile("retq");
+ };
+ l();
+}
+
+// CHECK: define internal void @"_ZZ17test_naked_lambdavENK3$_0clEv"
+// CHECK-NOT: alloca
+// CHECK-NOT: store
+// CHECK-NOT: call void @_ZN
+// ASM-LABEL: _ZZ17test_naked_lambdavENK3$_0clEv:
+// ASM-NOT: push
+// ASM-NOT: pop
+// ASM: retq
+
+
More information about the cfe-commits
mailing list