https://github.com/aobolensk created 
https://github.com/llvm/llvm-project/pull/234124

Without the freeze, an undef argument reaches a noundef parameter and the 
optimizer miscompiles the call

>From e8453854957f93fdf08999f1544eb4dacfe84e04 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <[email protected]>
Date: Wed, 21 Oct 2026 11:12:04 +0200
Subject: [PATCH] [CIR] Freeze maybe_undef call arguments

Classic codegen freezes arguments passed to maybe_undef parameters, so do the 
same in CIR
---
 clang/lib/CIR/CodeGen/CIRGenCall.cpp       | 25 +++++++++++
 clang/test/CIR/CodeGen/attr-maybeundef.cpp | 51 ++++++++++++++++++++++
 2 files changed, 76 insertions(+)
 create mode 100644 clang/test/CIR/CodeGen/attr-maybeundef.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 2d9113c2789933..357b269f514923 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -1241,6 +1241,27 @@ 
CIRGenTypes::arrangeFreeFunctionType(CanQual<FunctionNoProtoType> fnpt) {
                                 fnpt->getExtInfo(), RequiredArgs(0));
 }
 
+/// Check if the argument of a function has maybe_undef attribute.
+static bool isArgumentMaybeUndef(const Decl *targetDecl,
+                                 unsigned numRequiredArgs, unsigned argNo) {
+  const auto *fd = dyn_cast_or_null<FunctionDecl>(targetDecl);
+  if (!fd)
+    return false;
+
+  // Assume variadic arguments do not have maybe_undef attribute.
+  if (argNo >= numRequiredArgs)
+    return false;
+
+  // Check if argument has maybe_undef attribute.
+  if (argNo < fd->getNumParams()) {
+    const ParmVarDecl *param = fd->getParamDecl(argNo);
+    if (param && param->hasAttr<MaybeUndefAttr>())
+      return true;
+  }
+
+  return false;
+}
+
 RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
                                 const CIRGenCallee &callee,
                                 ReturnValueSlot returnValue,
@@ -1359,6 +1380,10 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo 
&funcInfo,
       assert(!cir::MissingFeatures::lowerAggregateLoadStore());
       cirCallArgs[argNo] = builder.createLoad(argLoc, src);
     }
+
+    if (isArgumentMaybeUndef(targetDecl, funcInfo.getNumRequiredArgs(), argNo))
+      cirCallArgs[argNo] =
+          cir::FreezeOp::create(builder, loc, cirCallArgs[argNo]);
   }
 
   const CIRGenCallee &concreteCallee = callee.prepareConcreteCallee(*this);
diff --git a/clang/test/CIR/CodeGen/attr-maybeundef.cpp 
b/clang/test/CIR/CodeGen/attr-maybeundef.cpp
new file mode 100644
index 00000000000000..af2f0348617abe
--- /dev/null
+++ b/clang/test/CIR/CodeGen/attr-maybeundef.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
+
+#define __maybe_undef __attribute__((maybe_undef))
+
+struct S { int a, b; };
+void scalar(int a, int __maybe_undef b, int c);
+void variadic(int __maybe_undef x, ...);
+void record(S __maybe_undef s);
+
+void test() {
+  int x, y, z;
+  S s;
+  scalar(x, y, z);
+  variadic(x, y);
+  record(s);
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z4testv()
+// CIR:         %[[X:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR:         %[[Y:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR:         %[[Z:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR:         %[[FY:.*]] = cir.freeze %[[Y]] : !s32i
+// CIR-NEXT:    cir.call @_Z6scalariii(%[[X]], %[[FY]], %[[Z]])
+// CIR:         %[[X2:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR:         %[[Y2:.*]] = cir.load{{.*}} : !cir.ptr<!s32i>, !s32i
+// CIR:         %[[FX2:.*]] = cir.freeze %[[X2]] : !s32i
+// CIR-NEXT:    cir.call @_Z8variadiciz(%[[FX2]], %[[Y2]])
+// CIR:         %[[SV:.*]] = cir.load{{.*}} : !cir.ptr<!rec_S>, !rec_S
+// CIR:         cir.freeze %[[SV]] : !rec_S
+// CIR:         cir.call @_Z6record1S(
+
+// LLVM-LABEL: define{{.*}} void @_Z4testv()
+// LLVM:         %[[X:.*]] = load i32
+// LLVM:         %[[Y:.*]] = load i32
+// LLVM:         %[[Z:.*]] = load i32
+// LLVM:         %[[FY:.*]] = freeze i32 %[[Y]]
+// LLVM-NEXT:    call void @_Z6scalariii(i32 noundef %[[X]], i32 noundef 
%[[FY]], i32 noundef %[[Z]])
+// LLVM:         %[[X2:.*]] = load i32
+// LLVM:         %[[Y2:.*]] = load i32
+// LLVM:         %[[FX2:.*]] = freeze i32 %[[X2]]
+// LLVM-NEXT:    call void (i32, ...) @_Z8variadiciz(i32 noundef %[[FX2]], i32 
noundef %[[Y2]])
+// LLVMCIR:      %[[SV:.*]] = load %struct.S
+// LLVMCIR:      freeze %struct.S %[[SV]]
+// OGCG:         %[[SV:.*]] = load i64
+// OGCG:         freeze i64 %[[SV]]
+// LLVM:         call void @_Z6record1S(i64

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

Reply via email to