https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/233671
>From 19f43db687f175908974776191dc36e19d9d8990 Mon Sep 17 00:00:00 2001 From: Arseniy Obolenskiy <[email protected]> Date: Tue, 20 Oct 2026 06:34:44 +0200 Subject: [PATCH] [CIR] Narrow blanket -fopenmp NYI guards to OGCG conditions Ordinary code compiled with -fopenmp hit errorNYI on every assignment, inc/dec, member access and global reference --- clang/include/clang/CIR/MissingFeatures.h | 2 + clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 22 +++-- clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp | 6 +- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 6 +- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 11 +-- clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp | 33 ++++++++ clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h | 6 ++ .../CIR/CodeGenOpenMP/non-openmp-code.cpp | 80 +++++++++++++++++++ 8 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 clang/test/CIR/CodeGenOpenMP/non-openmp-code.cpp diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 0ff6c6d44d191..0a3c594e1a8bf 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -274,6 +274,8 @@ struct MissingFeatures { static bool msvcBuiltins() { return false; } static bool openCL() { return false; } static bool openMP() { return false; } + static bool openMPLastprivateConditional() { return false; } + static bool openMPNontemporalDecl() { return false; } static bool sycl() { return false; } static bool opTBAA() { return false; } static bool peepholeProtection() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 9072a22cef52b..b790e2e73cdfd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -463,8 +463,17 @@ static LValue emitGlobalVarDeclLValue(CIRGenFunction &cgf, const Expr *e, // Check if the variable is marked as declare target with link clause in // device codegen. - if (cgf.getLangOpts().OpenMP) - cgf.cgm.errorNYI(e->getSourceRange(), "emitGlobalVarDeclLValue: OpenMP"); + if (cgf.getLangOpts().OpenMPIsTargetDevice) { + std::optional<OMPDeclareTargetDeclAttr::MapTypeTy> res = + OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(vd); + if (res && (*res == OMPDeclareTargetDeclAttr::MT_Link || + ((*res == OMPDeclareTargetDeclAttr::MT_To || + *res == OMPDeclareTargetDeclAttr::MT_Enter) && + cgf.cgm.getOpenMPRuntime().hasRequiresUnifiedSharedMemory()))) + cgf.cgm.errorNYI(e->getSourceRange(), + "emitGlobalVarDeclLValue: OpenMP declare target " + "indirect access"); + } // Traditional LLVM codegen handles thread local separately, CIR handles // as part of getAddrOfGlobalVar. @@ -1906,7 +1915,9 @@ LValue CIRGenFunction::emitMemberExpr(const MemberExpr *e) { // If the member was explicitly marked as nontemporal, mark it as // nontemporal. If the base lvalue is marked as nontemporal, mark access // to children as nontemporal too. - cgm.errorNYI(e->getSourceRange(), "emitMemberExpr: OpenMP"); + assert(!cir::MissingFeatures::openMPNontemporalDecl()); + if (baseLV.isNontemporal()) + lv.setNontemporal(true); } return lv; } @@ -2272,10 +2283,7 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) { else emitStoreThroughLValue(rv, lv); - if (getLangOpts().OpenMP) { - cgm.errorNYI(e->getSourceRange(), "openmp"); - return {}; - } + assert(!cir::MissingFeatures::openMPLastprivateConditional()); return lv; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp index 7dd24a6d076e8..a17c3c340a417 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp @@ -1086,8 +1086,7 @@ LValue CIRGenFunction::emitComplexAssignmentLValue(const BinaryOperator *e) { mlir::Value value; // ignored LValue lvalue = ComplexExprEmitter(*this).emitBinAssignLValue(e, value); - if (getLangOpts().OpenMP) - cgm.errorNYI("emitComplexAssignmentLValue OpenMP"); + assert(!cir::MissingFeatures::openMPLastprivateConditional()); return lvalue; } @@ -1143,8 +1142,7 @@ mlir::Value CIRGenFunction::emitComplexPrePostIncDec(const UnaryOperator *e, // Store the updated result through the lvalue. emitStoreOfComplex(loc, incVal, lv, /*isInit=*/false); - if (getLangOpts().OpenMP) - cgm.errorNYI(loc, "emitComplexPrePostIncDec OpenMP"); + assert(!cir::MissingFeatures::openMPLastprivateConditional()); // If this is a postinc, return the value read from memory, otherwise use the // updated value. diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 2082963025238..7173feda7759f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -627,8 +627,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { mlir::Value emitFixedPointIncDec(const UnaryOperator *e, mlir::Value value, QualType type); mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv) { - if (cgf.getLangOpts().OpenMP) - cgf.cgm.errorNYI(e->getSourceRange(), "inc/dec OpenMP"); + assert(!cir::MissingFeatures::openMPLastprivateConditional()); QualType type = e->getSubExpr()->getType(); @@ -1731,8 +1730,7 @@ LValue ScalarExprEmitter::emitCompoundAssignLValue( else cgf.emitStoreThroughLValue(RValue::get(result), lhsLV); - if (cgf.getLangOpts().OpenMP) - cgf.cgm.errorNYI(e->getSourceRange(), "openmp"); + assert(!cir::MissingFeatures::openMPLastprivateConditional()); return lhsLV; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 614a731158e45..d1a748d86777f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1023,8 +1023,8 @@ LangAS CIRGenModule::getGlobalVarAddressSpace(const VarDecl *d) { return LangAS::cuda_device; } - if (langOpts.OpenMP) - errorNYI("OpenMP global address space"); + if (langOpts.OpenMP && d && d->hasAttr<OMPAllocateDeclAttr>()) + errorNYI(d->getSourceRange(), "OpenMP global address space"); return getTargetCIRGenInfo().getGlobalVarAddressSpace(*this, d); } @@ -1253,7 +1253,9 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty, assert(!cir::MissingFeatures::opGlobalWeakRef()); assert(!cir::MissingFeatures::setDLLStorageClass()); - assert(!cir::MissingFeatures::openMP()); + + if (langOpts.OpenMP && !langOpts.OpenMPSimd && d) + getOpenMPRuntime().registerTargetGlobalVariable(d); if (entry.getSymType() == ty && cir::normalizeDefaultAddressSpace(entryCIRAS) == @@ -1329,8 +1331,7 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty, // Handle things which are present even on external declarations. if (d) { if (langOpts.OpenMP && !langOpts.OpenMPSimd) - errorNYI(d->getSourceRange(), - "getOrCreateCIRGlobal: OpenMP target global variable"); + getOpenMPRuntime().registerTargetGlobalVariable(d); gv.setAlignmentAttr(getSize(astContext.getDeclAlign(d))); diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp index 93ec2248f76eb..db747fa112ebc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp @@ -16,6 +16,7 @@ #include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/Dialect/OpenMP/OpenMPInterfaces.h" #include "clang/AST/OpenMPClause.h" +#include "clang/CIR/MissingFeatures.h" using namespace clang; using namespace clang::CIRGen; @@ -172,6 +173,38 @@ bool CIRGenOpenMPRuntime::markAsGlobalTarget(GlobalDecl gd) { return !alreadyEmittedTargetDecls.insert(d).second; } +// Mirrors CGOpenMPRuntime::registerTargetGlobalVariable. +void CIRGenOpenMPRuntime::registerTargetGlobalVariable(const VarDecl *vd) { + if (cgm.getLangOpts().OMPTargetTriples.empty() && + !cgm.getLangOpts().OpenMPIsTargetDevice) + return; + + std::optional<OMPDeclareTargetDeclAttr::MapTypeTy> res = + OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(vd); + + if (res && *res != OMPDeclareTargetDeclAttr::MT_Link && + vd->hasExternalStorage()) + return; + + if (res && *res == OMPDeclareTargetDeclAttr::MT_Local) + return; + + if (!res) { + // OGCG records non-target variables emitted in device code. + assert(!cir::MissingFeatures::openMP()); + return; + } + + cgm.errorNYI(vd->getSourceRange(), + "registerTargetGlobalVariable: declare target variable"); +} + +bool CIRGenOpenMPRuntime::hasRequiresUnifiedSharedMemory() const { + // OGCG also sets this from '#pragma omp requires unified_shared_memory'. + assert(!cir::MissingFeatures::openMP()); + return cgm.getLangOpts().OpenMPForceUSM; +} + void CIRGenOpenMPRuntime::emitDeclareTargetFunction(const FunctionDecl *fd, cir::FuncOp funcOp) { const auto *attr = fd->getAttr<OMPDeclareTargetDeclAttr>(); diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h index d22200512e483..fb368e06c5b2a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h +++ b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.h @@ -48,6 +48,12 @@ class CIRGenOpenMPRuntime { /// handled and should be skipped. bool markAsGlobalTarget(GlobalDecl gd); + /// Register a global variable that may need an offload entry. + void registerTargetGlobalVariable(const VarDecl *vd); + + /// Return whether unified shared memory is required. + bool hasRequiresUnifiedSharedMemory() const; + /// If the function has an OMPDeclareTargetDeclAttr, set the corresponding /// omp.declare_target attribute on the emitted cir.func op. void emitDeclareTargetFunction(const FunctionDecl *fd, cir::FuncOp funcOp); diff --git a/clang/test/CIR/CodeGenOpenMP/non-openmp-code.cpp b/clang/test/CIR/CodeGenOpenMP/non-openmp-code.cpp new file mode 100644 index 0000000000000..43a009073a76f --- /dev/null +++ b/clang/test/CIR/CodeGenOpenMP/non-openmp-code.cpp @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -fopenmp -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -fopenmp -fopenmp-targets=amdgcn-amd-amdhsa -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-offload.cir +// RUN: FileCheck --input-file=%t-offload.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -fopenmp -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefixes=LLVM,LLVMCIR +// RUN: %clang_cc1 -fopenmp -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,OGCG + +// Ordinary code compiled with -fopenmp. + +int g; + +// CIR: cir.global external @g = #cir.int<0> : !s32i +// LLVM: @g = global i32 0 + +struct S { + int x; +}; + +void scalar_ops(S *s, int v) { + g = v; + g += v; + ++g; + s->x = g; +} + +// CIR: cir.func{{.*}} @_Z10scalar_opsP1Si +// CIR: %[[G:.*]] = cir.get_global @g : !cir.ptr<!s32i> +// CIR: cir.store{{.*}} %{{.*}}, %[[G]] : !s32i, !cir.ptr<!s32i> +// CIR: %[[G1:.*]] = cir.get_global @g : !cir.ptr<!s32i> +// CIR: %[[G1_VAL:.*]] = cir.load{{.*}} %[[G1]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[ADD:.*]] = cir.add nsw %[[G1_VAL]], %{{.*}} : !s32i +// CIR: cir.store{{.*}} %[[ADD]], %[[G1]] : !s32i, !cir.ptr<!s32i> +// CIR: %[[G2:.*]] = cir.get_global @g : !cir.ptr<!s32i> +// CIR: %[[G2_VAL:.*]] = cir.load{{.*}} %[[G2]] : !cir.ptr<!s32i>, !s32i +// CIR: %[[INC:.*]] = cir.inc nsw %[[G2_VAL]] : !s32i +// CIR: cir.store{{.*}} %[[INC]], %[[G2]] : !s32i, !cir.ptr<!s32i> +// CIR: %[[X:.*]] = cir.get_member %{{.*}}[0] {name = "x"} : !cir.ptr<!rec_S> -> !cir.ptr<!s32i> +// CIR: cir.store{{.*}} %{{.*}}, %[[X]] : !s32i, !cir.ptr<!s32i> + +// LLVM: define{{.*}} void @_Z10scalar_opsP1Si +// LLVM: store i32 %{{.*}}, ptr @g +// LLVM: %[[G1:.*]] = load i32, ptr @g +// LLVM: %[[ADD:.*]] = add nsw i32 %[[G1]], %{{.*}} +// LLVM: store i32 %[[ADD]], ptr @g +// LLVM: %[[G2:.*]] = load i32, ptr @g +// LLVM: %[[INC:.*]] = add nsw i32 %[[G2]], 1 +// LLVM: store i32 %[[INC]], ptr @g +// LLVM: %[[X:.*]] = getelementptr inbounds nuw %struct.S, ptr %{{.*}}, i32 0, i32 0 +// LLVM: store i32 %{{.*}}, ptr %[[X]] + +void complex_ops(float _Complex *c, float _Complex v) { + *c = v; + ++*c; +} + +// CIR: cir.func{{.*}} @_Z11complex_opsPCfS_ +// CIR: %[[V:.*]] = cir.load align(4) %{{.*}} : !cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float> +// CIR: %[[C_PTR:.*]] = cir.load deref{{.*}} %{{.*}} : !cir.ptr<!cir.ptr<!cir.complex<!cir.float>>>, !cir.ptr<!cir.complex<!cir.float>> +// CIR: cir.store{{.*}} %[[V]], %[[C_PTR]] : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>> +// CIR: %[[C_PTR2:.*]] = cir.load deref{{.*}} %{{.*}} : !cir.ptr<!cir.ptr<!cir.complex<!cir.float>>>, !cir.ptr<!cir.complex<!cir.float>> +// CIR: %[[C:.*]] = cir.load{{.*}} %[[C_PTR2]] : !cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float> +// CIR: %[[REAL:.*]] = cir.complex.real %[[C]] : !cir.complex<!cir.float> -> !cir.float +// CIR: %[[IMAG:.*]] = cir.complex.imag %[[C]] : !cir.complex<!cir.float> -> !cir.float +// CIR: %[[INC:.*]] = cir.fadd %[[REAL]], %{{.*}} : !cir.float +// CIR: %[[RES:.*]] = cir.complex.create %[[INC]], %[[IMAG]] : !cir.float -> !cir.complex<!cir.float> +// CIR: cir.store{{.*}} %[[RES]], %{{.*}} : !cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>> + +// LLVMCIR: define{{.*}} void @_Z11complex_opsPCfS_ +// LLVMCIR: store { float, float } %{{.*}}, ptr %{{.*}} +// LLVMCIR: %[[REAL:.*]] = extractvalue { float, float } %{{.*}}, 0 +// LLVMCIR: %[[INC:.*]] = fadd float %[[REAL]], 1.000000e+00 +// LLVMCIR: store { float, float } %{{.*}}, ptr %{{.*}} + +// OGCG: define{{.*}} void @_Z11complex_opsPCfS_ +// OGCG: store float %{{.*}}, ptr %{{.*}} +// OGCG: store float %{{.*}}, ptr %{{.*}} +// OGCG: %[[REAL:.*]] = load float, ptr %{{.*}} +// OGCG: %[[INC:.*]] = fadd float %[[REAL]], 1.000000e+00 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
