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
