https://github.com/midhuncodes7 updated 
https://github.com/llvm/llvm-project/pull/212297

>From 30ea53a4b734b9b6b128f4efd93eec2cc88fcbf6 Mon Sep 17 00:00:00 2001
From: Midhunesh <[email protected]>
Date: Thu, 16 Jul 2026 11:47:59 +0530
Subject: [PATCH 1/4] Implement ContainsReturnsTwiceCall attribute to handle
 returns_twice in Alias Analysis

---
 clang/lib/CodeGen/CGCall.cpp             |  4 ++++
 llvm/include/llvm/IR/Attributes.td       |  3 +++
 llvm/lib/Analysis/BasicAliasAnalysis.cpp | 11 ++++++++++-
 llvm/lib/IR/Attributes.cpp               |  5 +++++
 4 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 08cb9860f2f92..0919fb9171800 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -6321,6 +6321,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo 
&CallInfo,
   CI->setAttributes(Attrs);
   CI->setCallingConv(static_cast<llvm::CallingConv::ID>(CallingConv));
 
+  // If this call can return twice (e.g. setjmp), mark the enclosing function
+  if (CI->hasFnAttr(llvm::Attribute::ReturnsTwice))
+    CurFn->addFnAttr(llvm::Attribute::ContainsReturnsTwiceCall);
+
   // Apply various metadata.
 
   if (!CI->getType()->isVoidTy())
diff --git a/llvm/include/llvm/IR/Attributes.td 
b/llvm/include/llvm/IR/Attributes.td
index 4e45100b54d38..6f8ea1d72143f 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -297,6 +297,9 @@ def ImmArg : EnumAttr<"immarg", IntersectPreserve, 
[ParamAttr]>;
 /// Function can return twice.
 def ReturnsTwice : EnumAttr<"returns_twice", IntersectPreserve, [FnAttr]>;
 
+/// Function contains a call to a function that can return twice (e.g. setjmp).
+def ContainsReturnsTwiceCall : EnumAttr<"contains-returns-twice-call", 
IntersectPreserve, [FnAttr]>;
+
 /// Safe Stack protection.
 def SafeStack : EnumAttr<"safestack", IntersectPreserve, [FnAttr]>;
 
diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp 
b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 7df62577e04db..5b47d1a6782c1 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -257,7 +257,16 @@ CaptureComponents 
EarliestEscapeAnalysis::getCapturesBefore(
       return isNotInCycle(I, &DT, LI, CI);
     }
 
-    return !isPotentiallyReachable(CaptureInst, I, nullptr, &DT, LI, CI);
+    if (isPotentiallyReachable(CaptureInst, I, nullptr, &DT, LI, CI))
+      return false;
+
+    // A `longjmp` may re-enter the function at any `returns_twice` call
+    // (e.g. `setjmp`), If the function contains such a call, conservatively
+    // treat the object as captured.
+    if 
(DT.getRoot()->getParent()->hasFnAttr(Attribute::ContainsReturnsTwiceCall))
+      return false;
+
+    return true;
   };
   if (IsNotCapturedBefore())
     return CaptureComponents::None;
diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp
index 4087b25951a1c..bf38b21cf573c 100644
--- a/llvm/lib/IR/Attributes.cpp
+++ b/llvm/lib/IR/Attributes.cpp
@@ -2793,6 +2793,11 @@ bool AttributeFuncs::areOutlineCompatible(const Function 
&A,
 void AttributeFuncs::mergeAttributesForInlining(Function &Caller,
                                                 const Function &Callee) {
   mergeFnAttrs(Caller, Callee);
+
+  // If the callee contained a returns_twice call (e.g. setjmp), those calls
+  // are now directly in the caller's body, so propagate the attribute.
+  if (Callee.hasFnAttribute(Attribute::ContainsReturnsTwiceCall))
+    Caller.addFnAttr(Attribute::ContainsReturnsTwiceCall);
 }
 
 void AttributeFuncs::mergeAttributesForOutlining(Function &Base,

>From e06312c1e5f93da08be7f9d004e1d799c6f155eb Mon Sep 17 00:00:00 2001
From: Midhunesh <[email protected]>
Date: Thu, 16 Jul 2026 12:36:18 +0530
Subject: [PATCH 2/4] Fix attribute

---
 llvm/include/llvm/IR/Attributes.td | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/IR/Attributes.td 
b/llvm/include/llvm/IR/Attributes.td
index 6f8ea1d72143f..072244c733412 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -298,7 +298,7 @@ def ImmArg : EnumAttr<"immarg", IntersectPreserve, 
[ParamAttr]>;
 def ReturnsTwice : EnumAttr<"returns_twice", IntersectPreserve, [FnAttr]>;
 
 /// Function contains a call to a function that can return twice (e.g. setjmp).
-def ContainsReturnsTwiceCall : EnumAttr<"contains-returns-twice-call", 
IntersectPreserve, [FnAttr]>;
+def ContainsReturnsTwiceCall : EnumAttr<"contains_returns_twice_call", 
IntersectPreserve, [FnAttr]>;
 
 /// Safe Stack protection.
 def SafeStack : EnumAttr<"safestack", IntersectPreserve, [FnAttr]>;

>From 99f20a33415a8ac8186982733e93de65fb8801aa Mon Sep 17 00:00:00 2001
From: Midhunesh <[email protected]>
Date: Thu, 16 Jul 2026 12:55:28 +0530
Subject: [PATCH 3/4] Fix attribute usage

---
 llvm/lib/Analysis/BasicAliasAnalysis.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp 
b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
index 5b47d1a6782c1..95279f10d5b32 100644
--- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp
+++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp
@@ -263,7 +263,7 @@ CaptureComponents EarliestEscapeAnalysis::getCapturesBefore(
     // A `longjmp` may re-enter the function at any `returns_twice` call
     // (e.g. `setjmp`), If the function contains such a call, conservatively
     // treat the object as captured.
-    if 
(DT.getRoot()->getParent()->hasFnAttr(Attribute::ContainsReturnsTwiceCall))
+    if 
(DT.getRoot()->getParent()->hasFnAttribute(Attribute::ContainsReturnsTwiceCall))
       return false;
 
     return true;

>From 3afa336e6ea6be47533b1c9b6f1ec9df6fb6f58b Mon Sep 17 00:00:00 2001
From: Midhunesh <[email protected]>
Date: Mon, 27 Jul 2026 21:48:09 +0530
Subject: [PATCH 4/4] Add tests to check returns_twice

---
 .../setjmp-contains-returns-twice-attr.c      |  60 ++++++++++
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |   1 +
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |   2 +
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |   2 +
 llvm/lib/Transforms/Utils/CodeExtractor.cpp   |   1 +
 llvm/test/Analysis/BasicAA/setjmp-longjmp.ll  | 104 ++++++++++++++++++
 6 files changed, 170 insertions(+)
 create mode 100644 clang/test/CodeGen/setjmp-contains-returns-twice-attr.c
 create mode 100644 llvm/test/Analysis/BasicAA/setjmp-longjmp.ll

diff --git a/clang/test/CodeGen/setjmp-contains-returns-twice-attr.c 
b/clang/test/CodeGen/setjmp-contains-returns-twice-attr.c
new file mode 100644
index 0000000000000..be3f5dffc75a6
--- /dev/null
+++ b/clang/test/CodeGen/setjmp-contains-returns-twice-attr.c
@@ -0,0 +1,60 @@
+// Test that functions containing setjmp / sigsetjmp / _setjmp calls, and
+// functions containing calls to user-declared __attribute__((returns_twice))
+// functions, are annotated with the contains_returns_twice_call attribute.
+//
+// The attribute is used by BasicAA to conservatively return MayAlias for
+// local allocas in such functions, preventing miscompilation via longjmp
+// re-entry paths invisible in the forward CFG.
+//
+// See: https://github.com/llvm/llvm-project/issues/198967
+
+// RUN: %clang_cc1 -x c %s -triple x86_64-linux-gnu -emit-llvm -o - | 
FileCheck %s
+
+struct __jmp_buf_tag { int n; };
+typedef struct __jmp_buf_tag jmp_buf[1];
+typedef struct __jmp_buf_tag sigjmp_buf[1];
+
+int setjmp(struct __jmp_buf_tag *);
+int sigsetjmp(struct __jmp_buf_tag *, int);
+int _setjmp(struct __jmp_buf_tag *);
+
+__attribute__((__returns_twice__)) void checkpoint(void);
+
+// CHECK: ; Function Attrs:{{.*}}contains_returns_twice_call
+// CHECK-NEXT: define{{.*}}@bar_setjmp(
+void bar_setjmp(void) {
+  jmp_buf buf;
+  setjmp(buf);
+}
+
+// CHECK: ; Function Attrs:{{.*}}contains_returns_twice_call
+// CHECK-NEXT: define{{.*}}@bar_sigsetjmp(
+void bar_sigsetjmp(void) {
+  sigjmp_buf buf;
+  sigsetjmp(buf, 0);
+}
+
+// CHECK: ; Function Attrs:{{.*}}contains_returns_twice_call
+// CHECK-NEXT: define{{.*}}@bar_setjmp_underscore(
+void bar_setjmp_underscore(void) {
+  jmp_buf buf;
+  _setjmp(buf);
+}
+
+// A user-declared returns_twice function must also trigger the attribute.
+// CHECK: ; Function Attrs:{{.*}}contains_returns_twice_call
+// CHECK-NEXT: define{{.*}}@bar_checkpoint(
+void bar_checkpoint(void) {
+  checkpoint();
+}
+
+// A function with no returns_twice call must NOT have the attribute.
+// The emitted "; Function Attrs:" line for bar_plain will contain only the
+// default function attributes (noinline nounwind optnone), not
+// contains_returns_twice_call.
+// CHECK: ; Function Attrs: noinline nounwind optnone
+// CHECK-NEXT: define{{.*}}@bar_plain(
+void bar_plain(void) {
+  int x = 42;
+  (void)x;
+}
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h 
b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 358f9a65a80af..ea365382aae76 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -826,6 +826,7 @@ enum AttributeKindCodes {
   ATTR_KIND_NOOUTLINE = 107,
   ATTR_KIND_FLATTEN = 108,
   ATTR_KIND_NOIPA = 109,
+  ATTR_KIND_CONTAINS_RETURNS_TWICE_CALL = 110,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp 
b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index c57323ed41525..6cba4b6ae7fac 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2334,6 +2334,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) 
{
     return Attribute::NoOutline;
   case bitc::ATTR_KIND_NOIPA:
     return Attribute::NoIPA;
+  case bitc::ATTR_KIND_CONTAINS_RETURNS_TWICE_CALL:
+    return Attribute::ContainsReturnsTwiceCall;
   }
 }
 
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp 
b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index f4ce522c23b8e..005f744bb0b0e 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1014,6 +1014,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind 
Kind) {
     return bitc::ATTR_KIND_NOOUTLINE;
   case Attribute::NoIPA:
     return bitc::ATTR_KIND_NOIPA;
+  case Attribute::ContainsReturnsTwiceCall:
+    return bitc::ATTR_KIND_CONTAINS_RETURNS_TWICE_CALL;
   case Attribute::EndAttrKinds:
     llvm_unreachable("Can not encode end-attribute kinds marker.");
   case Attribute::None:
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp 
b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 4c33848dacf51..bfd19b1cce779 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -954,6 +954,7 @@ Function *CodeExtractor::constructFunctionDeclaration(
       case Attribute::NoReturn:
       case Attribute::NoSync:
       case Attribute::ReturnsTwice:
+      case Attribute::ContainsReturnsTwiceCall:
       case Attribute::Speculatable:
       case Attribute::StackAlignment:
       case Attribute::WillReturn:
diff --git a/llvm/test/Analysis/BasicAA/setjmp-longjmp.ll 
b/llvm/test/Analysis/BasicAA/setjmp-longjmp.ll
new file mode 100644
index 0000000000000..63a73f2422d68
--- /dev/null
+++ b/llvm/test/Analysis/BasicAA/setjmp-longjmp.ll
@@ -0,0 +1,104 @@
+; Test that BasicAA conservatively returns MayAlias for local allocas in
+; functions containing returns_twice calls (e.g. setjmp/sigsetjmp), to prevent
+; miscompilation via longjmp re-entry paths invisible in the forward CFG.
+;
+; Reproduces: https://github.com/llvm/llvm-project/issues/198967
+;
+; Without the fix, GVN incorrectly concludes that the store through %p_val
+; (which may alias %i via a longjmp re-entry) cannot modify %i, then DSE
+; eliminates the "store i32 13, ptr %i" as dead. The fix adds an O(1)
+; check for the contains_returns_twice_call function attribute at the point
+; where isNotCapturedBefore would otherwise return true.
+
+; RUN: opt < %s -aa-pipeline=basic-aa -passes=gvn,dse -S | FileCheck %s
+
+target datalayout = 
"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@x  = external global i32
+@ii = external global i32
+@p  = external global ptr
+
+declare void @redo()
+declare void @checkpoint() #0
+
+; Control case: function has the same CFG structure but no 
contains_returns_twice_call
+; attribute. GVN/DSE can legally eliminate "store i32 13, ptr %i" because in 
the
+; forward CFG the only path to if.else does not pass through if.then (where &i 
is
+; stored to @p). BasicAA correctly returns NoAlias for (%i, %p_val) here.
+;
+; Fix case CHECK directives are listed here so that CHECK-NOT is correctly 
scoped
+; between the two CHECK-LABELs: it checks that the store is absent in 
bar_no_attr
+; and present in bar_with_attr.
+;
+; CHECK-LABEL: define void @bar_no_attr(
+; CHECK-NOT:   store i32 13, ptr %i
+; CHECK-LABEL: define void @bar_with_attr(
+; CHECK:       store i32 13, ptr %i
+define void @bar_no_attr() {
+entry:
+  %i = alloca i32, align 4
+  %x_val = load i32, ptr @x
+  %cond = icmp ne i32 %x_val, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  store ptr %i, ptr @p
+  call void @redo()
+  br label %if.end
+
+if.else:
+  ; Bug: without the attribute, GVN sees "store i32 42, ptr %p_val" as
+  ; NoAlias with %i (the capture in if.then is not visible in the forward
+  ; CFG), so it forwards 13 through the load and DSE removes this store.
+  store i32 13, ptr %i
+  %p_val = load ptr, ptr @p
+  store i32 42, ptr %p_val
+  %ii_val = load i32, ptr %i
+  store i32 %ii_val, ptr @ii
+  br label %if.end
+
+if.end:
+  ret void
+}
+
+; Fix case: same structure with contains_returns_twice_call on the function
+; (set automatically by Clang when emitting setjmp / sigsetjmp / any
+; returns_twice call). BasicAA conservatively returns MayAlias for (%i,
+; %p_val) because a longjmp can re-enter at the checkpoint() site with @p
+; already holding &i, making the if.then capture visible to if.else.
+;
+; The "store i32 13, ptr %i" MUST be preserved; removing it is a
+; miscompilation. (CHECKs for this function are listed above to correctly
+; scope the CHECK-NOT between the two CHECK-LABELs.)
+define void @bar_with_attr() #1 {
+entry:
+  %i = alloca i32, align 4
+  call void @checkpoint() #0
+  %x_val = load i32, ptr @x
+  %cond = icmp ne i32 %x_val, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  ; On the first pass: stores &i into @p, then calls redo().
+  ; redo() performs longjmp(buf) which re-enters at checkpoint() above.
+  ; On re-entry: if.then runs again setting p = &i, then if.else runs
+  ; with @p == &i, making "store i32 42, ptr %p_val" alias "store i32 13, ptr 
%i".
+  store ptr %i, ptr @p
+  call void @redo()
+  br label %if.end
+
+if.else:
+  store i32 13, ptr %i
+  %p_val = load ptr, ptr @p
+  store i32 42, ptr %p_val   ; may write to %i via longjmp re-entry path
+  %ii_val = load i32, ptr %i
+  store i32 %ii_val, ptr @ii
+  br label %if.end
+
+if.end:
+  ret void
+}
+
+attributes #0 = { returns_twice }
+attributes #1 = { contains_returns_twice_call }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to