[clang] [llvm] [mlir] [WIP][mlir][OpenMP] Translate omp.declare_simd to LLVM IR (PR #178087)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 27 10:28:35 PST 2026
https://github.com/chichunchen updated https://github.com/llvm/llvm-project/pull/178087
>From 1c9e5c48ef491fb5406edd58ed92a559749423f2 Mon Sep 17 00:00:00 2001
From: cchen <chichun.chen at hpe.com>
Date: Wed, 21 Jan 2026 23:19:22 -0600
Subject: [PATCH] [mlir][OpenMP] Translate omp.declare_simd to LLVM IR
---
clang/lib/CodeGen/CGOpenMPRuntime.cpp | 114 ++-------
.../llvm/Frontend/OpenMP/OMPDeclareSimd.h | 62 +++++
.../llvm/Frontend/OpenMP/OMPIRBuilder.h | 1 +
llvm/lib/Frontend/OpenMP/CMakeLists.txt | 1 +
llvm/lib/Frontend/OpenMP/OMPDeclareSimd.cpp | 170 ++++++++++++
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 1 +
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 241 ++++++++++++++++++
.../Target/LLVMIR/openmp-declare-simd.mlir | 218 ++++++++++++++++
8 files changed, 721 insertions(+), 87 deletions(-)
create mode 100644 llvm/include/llvm/Frontend/OpenMP/OMPDeclareSimd.h
create mode 100644 llvm/lib/Frontend/OpenMP/OMPDeclareSimd.cpp
create mode 100644 mlir/test/Target/LLVMIR/openmp-declare-simd.mlir
diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index ac16ce6b6e768..6c19193e179ef 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -33,6 +33,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Frontend/OpenMP/OMPDeclareSimd.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalValue.h"
@@ -11812,27 +11813,8 @@ void CGOpenMPRuntime::emitTargetDataStandAloneCall(
}
}
-namespace {
- /// Kind of parameter in a function with 'declare simd' directive.
-enum ParamKindTy {
- Linear,
- LinearRef,
- LinearUVal,
- LinearVal,
- Uniform,
- Vector,
-};
-/// Attribute set of the parameter.
-struct ParamAttrTy {
- ParamKindTy Kind = Vector;
- llvm::APSInt StrideOrArg;
- llvm::APSInt Alignment;
- bool HasVarStride = false;
-};
-} // namespace
-
static unsigned evaluateCDTSize(const FunctionDecl *FD,
- ArrayRef<ParamAttrTy> ParamAttrs) {
+ ArrayRef<DeclareSimdAttrTy> ParamAttrs) {
// Every vector variant of a SIMD-enabled function has a vector length (VLEN).
// If OpenMP clause "simdlen" is used, the VLEN is the value of the argument
// of that clause. The VLEN value must be power of 2.
@@ -11862,13 +11844,13 @@ static unsigned evaluateCDTSize(const FunctionDecl *FD,
} else {
unsigned Offset = 0;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
- if (ParamAttrs[Offset].Kind == Vector)
+ if (ParamAttrs[Offset].Kind == DeclareSimdKindTy::Vector)
CDT = C.getPointerType(C.getCanonicalTagType(MD->getParent()));
++Offset;
}
if (CDT.isNull()) {
for (unsigned I = 0, E = FD->getNumParams(); I < E; ++I) {
- if (ParamAttrs[I + Offset].Kind == Vector) {
+ if (ParamAttrs[I + Offset].Kind == DeclareSimdKindTy::Vector) {
CDT = FD->getParamDecl(I)->getType();
break;
}
@@ -11883,56 +11865,10 @@ static unsigned evaluateCDTSize(const FunctionDecl *FD,
return C.getTypeSize(CDT);
}
-/// Mangle the parameter part of the vector function name according to
-/// their OpenMP classification. The mangling function is defined in
-/// section 4.5 of the AAVFABI(2021Q1).
-static std::string mangleVectorParameters(ArrayRef<ParamAttrTy> ParamAttrs) {
- SmallString<256> Buffer;
- llvm::raw_svector_ostream Out(Buffer);
- for (const auto &ParamAttr : ParamAttrs) {
- switch (ParamAttr.Kind) {
- case Linear:
- Out << 'l';
- break;
- case LinearRef:
- Out << 'R';
- break;
- case LinearUVal:
- Out << 'U';
- break;
- case LinearVal:
- Out << 'L';
- break;
- case Uniform:
- Out << 'u';
- break;
- case Vector:
- Out << 'v';
- break;
- }
- if (ParamAttr.HasVarStride)
- Out << "s" << ParamAttr.StrideOrArg;
- else if (ParamAttr.Kind == Linear || ParamAttr.Kind == LinearRef ||
- ParamAttr.Kind == LinearUVal || ParamAttr.Kind == LinearVal) {
- // Don't print the step value if it is not present or if it is
- // equal to 1.
- if (ParamAttr.StrideOrArg < 0)
- Out << 'n' << -ParamAttr.StrideOrArg;
- else if (ParamAttr.StrideOrArg != 1)
- Out << ParamAttr.StrideOrArg;
- }
-
- if (!!ParamAttr.Alignment)
- Out << 'a' << ParamAttr.Alignment;
- }
-
- return std::string(Out.str());
-}
-
static void
emitX86DeclareSimdFunction(const FunctionDecl *FD, llvm::Function *Fn,
const llvm::APSInt &VLENVal,
- ArrayRef<ParamAttrTy> ParamAttrs,
+ ArrayRef<DeclareSimdAttrTy> ParamAttrs,
OMPDeclareSimdDeclAttr::BranchStateTy State) {
struct ISADataTy {
char ISA;
@@ -11977,7 +11913,7 @@ emitX86DeclareSimdFunction(const FunctionDecl *FD, llvm::Function *Fn,
} else {
Out << VLENVal;
}
- Out << mangleVectorParameters(ParamAttrs);
+ Out << llvm::omp::mangleVectorParameters(ParamAttrs);
Out << '_' << Fn->getName();
Fn->addFnAttr(Out.str());
}
@@ -11991,19 +11927,21 @@ emitX86DeclareSimdFunction(const FunctionDecl *FD, llvm::Function *Fn,
// https://developer.arm.com/products/software-development-tools/hpc/arm-compiler-for-hpc/vector-function-abi.
/// Maps To Vector (MTV), as defined in 4.1.1 of the AAVFABI (2021Q1).
-static bool getAArch64MTV(QualType QT, ParamKindTy Kind) {
+static bool getAArch64MTV(QualType QT, DeclareSimdKindTy Kind) {
QT = QT.getCanonicalType();
if (QT->isVoidType())
return false;
- if (Kind == ParamKindTy::Uniform)
+ if (Kind == DeclareSimdKindTy::Uniform)
return false;
- if (Kind == ParamKindTy::LinearUVal || Kind == ParamKindTy::LinearRef)
+ if (Kind == DeclareSimdKindTy::LinearUVal ||
+ Kind == DeclareSimdKindTy::LinearRef)
return false;
- if ((Kind == ParamKindTy::Linear || Kind == ParamKindTy::LinearVal) &&
+ if ((Kind == DeclareSimdKindTy::Linear ||
+ Kind == DeclareSimdKindTy::LinearVal) &&
!QT->isReferenceType())
return false;
@@ -12036,7 +11974,8 @@ static bool getAArch64PBV(QualType QT, ASTContext &C) {
/// Computes the lane size (LS) of a return type or of an input parameter,
/// as defined by `LS(P)` in 3.2.1 of the AAVFABI.
/// TODO: Add support for references, section 3.2.1, item 1.
-static unsigned getAArch64LS(QualType QT, ParamKindTy Kind, ASTContext &C) {
+static unsigned getAArch64LS(QualType QT, DeclareSimdKindTy Kind,
+ ASTContext &C) {
if (!getAArch64MTV(QT, Kind) && QT.getCanonicalType()->isPointerType()) {
QualType PTy = QT.getCanonicalType()->getPointeeType();
if (getAArch64PBV(PTy, C))
@@ -12052,7 +11991,7 @@ static unsigned getAArch64LS(QualType QT, ParamKindTy Kind, ASTContext &C) {
// signature of the scalar function, as defined in 3.2.2 of the
// AAVFABI.
static std::tuple<unsigned, unsigned, bool>
-getNDSWDS(const FunctionDecl *FD, ArrayRef<ParamAttrTy> ParamAttrs) {
+getNDSWDS(const FunctionDecl *FD, ArrayRef<DeclareSimdAttrTy> ParamAttrs) {
QualType RetType = FD->getReturnType().getCanonicalType();
ASTContext &C = FD->getASTContext();
@@ -12061,7 +12000,7 @@ getNDSWDS(const FunctionDecl *FD, ArrayRef<ParamAttrTy> ParamAttrs) {
llvm::SmallVector<unsigned, 8> Sizes;
if (!RetType->isVoidType()) {
- Sizes.push_back(getAArch64LS(RetType, ParamKindTy::Vector, C));
+ Sizes.push_back(getAArch64LS(RetType, DeclareSimdKindTy::Vector, C));
if (!getAArch64PBV(RetType, C) && getAArch64MTV(RetType, {}))
OutputBecomesInput = true;
}
@@ -12140,7 +12079,7 @@ static void addAArch64AdvSIMDNDSNames(unsigned NDS, StringRef Mask,
/// Emit vector function attributes for AArch64, as defined in the AAVFABI.
static void emitAArch64DeclareSimdFunction(
CodeGenModule &CGM, const FunctionDecl *FD, unsigned UserVLEN,
- ArrayRef<ParamAttrTy> ParamAttrs,
+ ArrayRef<DeclareSimdAttrTy> ParamAttrs,
OMPDeclareSimdDeclAttr::BranchStateTy State, StringRef MangledName,
char ISA, unsigned VecRegSize, llvm::Function *Fn, SourceLocation SLoc) {
@@ -12174,7 +12113,7 @@ static void emitAArch64DeclareSimdFunction(
}
// Sort out parameter sequence.
- const std::string ParSeq = mangleVectorParameters(ParamAttrs);
+ const std::string ParSeq = llvm::omp::mangleVectorParameters(ParamAttrs);
StringRef Prefix = "_ZGV";
// Generate simdlen from user input (if any).
if (UserVLEN) {
@@ -12250,7 +12189,7 @@ void CGOpenMPRuntime::emitDeclareSimdFunction(const FunctionDecl *FD,
++ParamPos;
}
for (const auto *Attr : FD->specific_attrs<OMPDeclareSimdDeclAttr>()) {
- llvm::SmallVector<ParamAttrTy, 8> ParamAttrs(ParamPositions.size());
+ llvm::SmallVector<DeclareSimdAttrTy, 8> ParamAttrs(ParamPositions.size());
// Mark uniform parameters.
for (const Expr *E : Attr->uniforms()) {
E = E->IgnoreParenImpCasts();
@@ -12264,7 +12203,7 @@ void CGOpenMPRuntime::emitDeclareSimdFunction(const FunctionDecl *FD,
assert(It != ParamPositions.end() && "Function parameter not found");
Pos = It->second;
}
- ParamAttrs[Pos].Kind = Uniform;
+ ParamAttrs[Pos].Kind = DeclareSimdKindTy::Uniform;
}
// Get alignment info.
auto *NI = Attr->alignments_begin();
@@ -12325,15 +12264,15 @@ void CGOpenMPRuntime::emitDeclareSimdFunction(const FunctionDecl *FD,
.getQuantity();
}
}
- ParamAttrTy &ParamAttr = ParamAttrs[Pos];
+ DeclareSimdAttrTy &ParamAttr = ParamAttrs[Pos];
if (*MI == OMPC_LINEAR_ref)
- ParamAttr.Kind = LinearRef;
+ ParamAttr.Kind = DeclareSimdKindTy::LinearRef;
else if (*MI == OMPC_LINEAR_uval)
- ParamAttr.Kind = LinearUVal;
+ ParamAttr.Kind = DeclareSimdKindTy::LinearUVal;
else if (IsReferenceType)
- ParamAttr.Kind = LinearVal;
+ ParamAttr.Kind = DeclareSimdKindTy::LinearVal;
else
- ParamAttr.Kind = Linear;
+ ParamAttr.Kind = DeclareSimdKindTy::Linear;
// Assuming a stride of 1, for `linear` without modifiers.
ParamAttr.StrideOrArg = llvm::APSInt::getUnsigned(1);
if (*SI) {
@@ -12358,7 +12297,8 @@ void CGOpenMPRuntime::emitDeclareSimdFunction(const FunctionDecl *FD,
// rescale the value of linear_step with the byte size of the
// pointee type.
if (!ParamAttr.HasVarStride &&
- (ParamAttr.Kind == Linear || ParamAttr.Kind == LinearRef))
+ (ParamAttr.Kind == DeclareSimdKindTy::Linear ||
+ ParamAttr.Kind == DeclareSimdKindTy::LinearRef))
ParamAttr.StrideOrArg = ParamAttr.StrideOrArg * PtrRescalingFactor;
++SI;
++MI;
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPDeclareSimd.h b/llvm/include/llvm/Frontend/OpenMP/OMPDeclareSimd.h
new file mode 100644
index 0000000000000..0e27d4d48f98f
--- /dev/null
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPDeclareSimd.h
@@ -0,0 +1,62 @@
+//===- OMPDeclareSimd.h - OpenMP declare simd related types and helpers ------
+//C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines types and helpers used when dealing with OpenMP declare
+/// simd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FRONTEND_OPENMP_OMPDECLARESIMD_H
+#define LLVM_FRONTEND_OPENMP_OMPDECLARESIMD_H
+
+#include "llvm/ADT/APSInt.h"
+#include "llvm/IR/Function.h"
+
+namespace llvm {
+namespace omp {
+
+/// Kind of parameter in a function with 'declare simd' directive.
+enum class DeclareSimdKindTy {
+ Linear,
+ LinearRef,
+ LinearUVal,
+ LinearVal,
+ Uniform,
+ Vector,
+};
+
+/// Attribute set of the `declare simd` parameter.
+struct DeclareSimdAttrTy {
+ DeclareSimdKindTy Kind = DeclareSimdKindTy::Vector;
+ llvm::APSInt StrideOrArg;
+ llvm::APSInt Alignment;
+ bool HasVarStride = false;
+};
+
+/// Type of branch clause of the `declare simd` directive.
+enum class DeclareSimdBranch {
+ Undefined,
+ Notinbranch,
+ Inbranch,
+};
+
+std::string
+mangleVectorParameters(llvm::ArrayRef<llvm::omp::DeclareSimdAttrTy> ParamAttrs);
+
+void emitDeclareSimdFunction(
+ llvm::Function *Fn, const llvm::APSInt &VLENVal,
+ llvm::ArrayRef<llvm::omp::DeclareSimdAttrTy> ParamAttrs,
+ DeclareSimdBranch Branch);
+
+} // end namespace omp
+
+} // end namespace llvm
+
+#endif // LLVM_FRONTEND_OPENMP_OMPDECLARESIMD_H
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
index 037fcaa863fe7..faded221d8131 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h
@@ -14,6 +14,7 @@
#ifndef LLVM_FRONTEND_OPENMP_OMPIRBUILDER_H
#define LLVM_FRONTEND_OPENMP_OMPIRBUILDER_H
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Frontend/Atomic/Atomic.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
diff --git a/llvm/lib/Frontend/OpenMP/CMakeLists.txt b/llvm/lib/Frontend/OpenMP/CMakeLists.txt
index e60b59c1203b9..68db83531a625 100644
--- a/llvm/lib/Frontend/OpenMP/CMakeLists.txt
+++ b/llvm/lib/Frontend/OpenMP/CMakeLists.txt
@@ -1,6 +1,7 @@
add_llvm_component_library(LLVMFrontendOpenMP
OMP.cpp
OMPContext.cpp
+ OMPDeclareSimd.cpp
OMPIRBuilder.cpp
DirectiveNameParser.cpp
diff --git a/llvm/lib/Frontend/OpenMP/OMPDeclareSimd.cpp b/llvm/lib/Frontend/OpenMP/OMPDeclareSimd.cpp
new file mode 100644
index 0000000000000..30c5af44a146b
--- /dev/null
+++ b/llvm/lib/Frontend/OpenMP/OMPDeclareSimd.cpp
@@ -0,0 +1,170 @@
+#include "llvm/Frontend/OpenMP/OMPDeclareSimd.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/IR/Module.h"
+#include "llvm/TargetParser/Triple.h"
+
+using namespace llvm;
+using namespace omp;
+
+std::string
+mangleVectorParameters(ArrayRef<llvm::omp::DeclareSimdAttrTy> ParamAttrs);
+
+/// Return type size in bits for `Ty` using DL.
+/// If scalable, return known-min as a conservative approximation.
+static unsigned getTypeSizeInBits(llvm::Type *Ty, const llvm::DataLayout &DL) {
+ if (!Ty)
+ return 0;
+ llvm::TypeSize TS = DL.getTypeSizeInBits(Ty);
+
+ if (TS.isScalable())
+ return (unsigned)TS.getKnownMinValue();
+ return (unsigned)TS.getFixedValue();
+}
+
+/// Returns size in *bits* of the Characteristic Data Type (CDT).
+static unsigned
+evaluateCDTSize(const llvm::Function *Fn,
+ ArrayRef<llvm::omp::DeclareSimdAttrTy> ParamAttrs) {
+ const llvm::DataLayout &DL = Fn->getParent()->getDataLayout();
+
+ llvm::Type *RetTy = Fn->getReturnType();
+ llvm::Type *CDT = nullptr;
+
+ // Non-void return => CDT = return type
+ if (RetTy && !RetTy->isVoidTy()) {
+ CDT = RetTy;
+ } else {
+ // First "Vector" param (ParamAttrs aligned with function params)
+ // If ParamAttrs is shorter than the parameter list, treat missing as Vector
+ // (matches the idea "default Kind is Vector").
+ unsigned NumParams = Fn->getFunctionType()->getNumParams();
+ for (unsigned I = 0; I < NumParams; ++I) {
+ bool IsVector =
+ (I < ParamAttrs.size())
+ ? ParamAttrs[I].Kind == llvm::omp::DeclareSimdKindTy::Vector
+ : true;
+ if (!IsVector)
+ continue;
+ CDT = Fn->getFunctionType()->getParamType(I);
+ break;
+ }
+ }
+
+ llvm::Type *IntTy = llvm::Type::getInt32Ty(Fn->getContext());
+ if (!CDT || CDT->isStructTy() || CDT->isArrayTy())
+ CDT = IntTy;
+
+ return getTypeSizeInBits(CDT, DL);
+}
+
+static void
+emitX86DeclareSimdFunction(llvm::Function *Fn, const llvm::APSInt &VLENVal,
+ ArrayRef<llvm::omp::DeclareSimdAttrTy> ParamAttrs,
+ DeclareSimdBranch Branch) {
+ struct ISADataTy {
+ char ISA;
+ unsigned VecRegSize;
+ };
+ ISADataTy ISAData[] = {
+ {'b', 128}, // SSE
+ {'c', 256}, // AVX
+ {'d', 256}, // AVX2
+ {'e', 512}, // AVX512
+ };
+ llvm::SmallVector<char, 2> Masked;
+ switch (Branch) {
+ case DeclareSimdBranch::Undefined:
+ Masked.push_back('N');
+ Masked.push_back('M');
+ break;
+ case DeclareSimdBranch::Notinbranch:
+ Masked.push_back('N');
+ break;
+ case DeclareSimdBranch::Inbranch:
+ Masked.push_back('M');
+ break;
+ }
+ for (char Mask : Masked) {
+ for (const ISADataTy &Data : ISAData) {
+ SmallString<256> Buffer;
+ llvm::raw_svector_ostream Out(Buffer);
+ Out << "_ZGV" << Data.ISA << Mask;
+ if (!VLENVal) {
+ unsigned NumElts = evaluateCDTSize(Fn, ParamAttrs);
+ assert(NumElts && "Non-zero simdlen/cdtsize expected");
+ Out << llvm::APSInt::getUnsigned(Data.VecRegSize / NumElts);
+ } else {
+ Out << VLENVal;
+ }
+ Out << llvm::omp::mangleVectorParameters(ParamAttrs);
+ Out << '_' << Fn->getName();
+ Fn->addFnAttr(Out.str());
+ }
+ }
+}
+
+namespace llvm {
+
+namespace omp {
+
+/// Mangle the parameter part of the vector function name according to
+/// their OpenMP classification. The mangling function is defined in
+/// section 4.5 of the AAVFABI(2021Q1).
+std::string
+mangleVectorParameters(ArrayRef<llvm::omp::DeclareSimdAttrTy> ParamAttrs) {
+ SmallString<256> Buffer;
+ llvm::raw_svector_ostream Out(Buffer);
+ for (const auto &ParamAttr : ParamAttrs) {
+ switch (ParamAttr.Kind) {
+ case llvm::omp::DeclareSimdKindTy::Linear:
+ Out << 'l';
+ break;
+ case llvm::omp::DeclareSimdKindTy::LinearRef:
+ Out << 'R';
+ break;
+ case llvm::omp::DeclareSimdKindTy::LinearUVal:
+ Out << 'U';
+ break;
+ case llvm::omp::DeclareSimdKindTy::LinearVal:
+ Out << 'L';
+ break;
+ case llvm::omp::DeclareSimdKindTy::Uniform:
+ Out << 'u';
+ break;
+ case llvm::omp::DeclareSimdKindTy::Vector:
+ Out << 'v';
+ break;
+ }
+ if (ParamAttr.HasVarStride)
+ Out << "s" << ParamAttr.StrideOrArg;
+ else if (ParamAttr.Kind == llvm::omp::DeclareSimdKindTy::Linear ||
+ ParamAttr.Kind == llvm::omp::DeclareSimdKindTy::LinearRef ||
+ ParamAttr.Kind == llvm::omp::DeclareSimdKindTy::LinearUVal ||
+ ParamAttr.Kind == llvm::omp::DeclareSimdKindTy::LinearVal) {
+ // Don't print the step value if it is not present or if it is
+ // equal to 1.
+ if (ParamAttr.StrideOrArg < 0)
+ Out << 'n' << -ParamAttr.StrideOrArg;
+ else if (ParamAttr.StrideOrArg != 1)
+ Out << ParamAttr.StrideOrArg;
+ }
+
+ if (!!ParamAttr.Alignment)
+ Out << 'a' << ParamAttr.Alignment;
+ }
+
+ return std::string(Out.str());
+}
+
+void emitDeclareSimdFunction(llvm::Function *Fn, const llvm::APSInt &VLENVal,
+ ArrayRef<DeclareSimdAttrTy> ParamAttrs,
+ DeclareSimdBranch Branch) {
+ Module *M = Fn->getParent();
+ const llvm::Triple &Triple = M->getTargetTriple();
+
+ if (Triple.isX86())
+ emitX86DeclareSimdFunction(Fn, VLENVal, ParamAttrs, Branch);
+}
+
+} // end namespace omp
+} // end namespace llvm
\ No newline at end of file
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index 5071fc53b43cc..3fea4edd52752 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -26,6 +26,7 @@
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Frontend/Offloading/Utility.h"
+#include "llvm/Frontend/OpenMP/OMPDeclareSimd.h"
#include "llvm/Frontend/OpenMP/OMPGridValues.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 781928e79db35..1cdce0bb3c99c 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -21,10 +21,12 @@
#include "mlir/Target/LLVMIR/Dialect/OpenMPCommon.h"
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
+#include "llvm/Frontend/OpenMP/OMPDeclareSimd.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfoMetadata.h"
@@ -6870,6 +6872,242 @@ convertTargetFreeMemOp(Operation &opInst, llvm::IRBuilderBase &builder,
return success();
}
+static llvm::StringRef paramKindToStr(llvm::omp::DeclareSimdKindTy param) {
+ switch (param) {
+ case llvm::omp::DeclareSimdKindTy::Linear:
+ return "Linear";
+ case llvm::omp::DeclareSimdKindTy::LinearRef:
+ return "LinearRef";
+ case llvm::omp::DeclareSimdKindTy::LinearUVal:
+ return "LinearUVal";
+ case llvm::omp::DeclareSimdKindTy::LinearVal:
+ return "LinearVal";
+ case llvm::omp::DeclareSimdKindTy::Uniform:
+ return "Uniform";
+ case llvm::omp::DeclareSimdKindTy::Vector:
+ return "Vector";
+ }
+ return "Unknown";
+}
+
+static void dumpParamAttrs(LLVM::LLVMFuncOp func,
+ llvm::ArrayRef<llvm::omp::DeclareSimdAttrTy> attrs) {
+ llvm::errs() << "=== DeclareSimd ParamAttrs for function " << func.getName()
+ << " ===\n";
+ for (auto it : llvm::enumerate(func.getArguments())) {
+ unsigned I = it.index();
+ mlir::Value Arg = it.value();
+ llvm::errs() << " [" << I << "] ";
+
+ // Try to show fir.bindc_name if present.
+ if (auto barg = mlir::dyn_cast<mlir::BlockArgument>(Arg)) {
+ if (auto *op = barg.getOwner()->getParentOp()) {
+ (void)op;
+ }
+ }
+ if (auto ptrTy =
+ mlir::dyn_cast<mlir::LLVM::LLVMPointerType>(Arg.getType())) {
+ (void)ptrTy;
+ }
+ if (auto attr =
+ func.getArgAttrOfType<mlir::StringAttr>(I, "fir.bindc_name"))
+ llvm::errs() << attr.getValue().str() << " ";
+ else
+ llvm::errs() << "(arg" << I << ") ";
+
+ const llvm::omp::DeclareSimdAttrTy &A = attrs[I];
+ llvm::errs() << "Kind=" << paramKindToStr(A.Kind);
+
+ if (A.Alignment != 0)
+ llvm::errs() << " Align=" << A.Alignment;
+
+ if (A.Kind == llvm::omp::DeclareSimdKindTy::Linear ||
+ A.Kind == llvm::omp::DeclareSimdKindTy::LinearRef ||
+ A.Kind == llvm::omp::DeclareSimdKindTy::LinearUVal ||
+ A.Kind == llvm::omp::DeclareSimdKindTy::LinearVal) {
+ if (A.HasVarStride)
+ llvm::errs() << " VarStrideArgIndex=" << A.StrideOrArg;
+ else
+ llvm::errs() << " Step=" << A.StrideOrArg;
+ }
+ llvm::errs() << "\n";
+ }
+ llvm::errs() << "=============================================\n";
+}
+
+// if `v` is a function block-arg, return its index.
+// If `v` is `llvm.load %argN`, return N as well (var-stride common case).
+static std::optional<unsigned> getFuncArgIndex(mlir::LLVM::LLVMFuncOp func,
+ mlir::Value v) {
+ if (!v)
+ return std::nullopt;
+
+ // Direct block argument case: %argN
+ if (auto barg = mlir::dyn_cast<mlir::BlockArgument>(v)) {
+ // Make sure this block arg belongs to this function.
+ // For LLVMFuncOp, the body is a Region; its entry block holds the args.
+ mlir::Block &entry = func.getBody().front();
+ if (barg.getOwner() == &entry)
+ return barg.getArgNumber();
+ return std::nullopt;
+ }
+
+ // Common LLVM dialect pattern: %v = llvm.load %argN
+ if (auto load = v.getDefiningOp<mlir::LLVM::LoadOp>()) {
+ mlir::Value addr = load.getAddr();
+ if (auto addrBArg = mlir::dyn_cast<mlir::BlockArgument>(addr)) {
+ mlir::Block &entry = func.getBody().front();
+ if (addrBArg.getOwner() == &entry)
+ return addrBArg.getArgNumber();
+ }
+ }
+
+ return std::nullopt;
+}
+
+static void
+applyUniform(LLVM::LLVMFuncOp funcOp, mlir::omp::DeclareSimdOp ds,
+ llvm::SmallVectorImpl<llvm::omp::DeclareSimdAttrTy> &attrs) {
+ for (mlir::Value u : ds.getUniformVars()) {
+ if (auto idx = getFuncArgIndex(funcOp, u))
+ attrs[*idx].Kind = llvm::omp::DeclareSimdKindTy::Uniform;
+ else
+ llvm::errs() << "NOTE: uniform var is not a function argument: " << u
+ << "\n";
+ }
+}
+
+static void
+applyAligned(LLVM::LLVMFuncOp funcOp, mlir::omp::DeclareSimdOp ds,
+ llvm::SmallVectorImpl<llvm::omp::DeclareSimdAttrTy> &attrs) {
+ auto alignedVars = ds.getAlignedVars();
+ std::optional<mlir::ArrayAttr> maybeAlignArr = ds.getAlignments();
+
+ if (alignedVars.empty() || !maybeAlignArr || !*maybeAlignArr)
+ return;
+
+ mlir::ArrayAttr alignArr = *maybeAlignArr;
+
+ unsigned n = std::min<unsigned>(alignedVars.size(), alignArr.size());
+ for (unsigned i = 0; i < n; ++i) {
+ mlir::Value v = alignedVars[i];
+ auto idx = getFuncArgIndex(funcOp, v);
+ if (!idx) {
+ llvm::errs() << "NOTE: aligned var is not a function argument: " << v
+ << "\n";
+ continue;
+ }
+
+ auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(alignArr[i]);
+ if (!intAttr) {
+ llvm::errs() << "NOTE: alignment entry is not an IntegerAttr\n";
+ continue;
+ }
+
+ llvm::APInt ap = intAttr.getValue();
+ attrs[*idx].Alignment = llvm::APSInt(ap, /*isUnsigned=*/true);
+ }
+}
+
+/// Helper: fill linear kind + step.
+/// linear(%arg2 = %2 : !llvm.ptr)
+/// - linear var: %arg2 (must be function arg)
+/// - step value: %2 (may be constant) or another function arg (var stride)
+static void
+applyLinear(LLVM::LLVMFuncOp func, mlir::omp::DeclareSimdOp ds,
+ llvm::SmallVectorImpl<llvm::omp::DeclareSimdAttrTy> &attrs) {
+ auto linearVars = ds.getLinearVars();
+ auto linearSteps = ds.getLinearStepVars(); // SSA values (may be empty)
+
+ // Default step=1 if no step provided / decode fails.
+ llvm::APSInt one(/*Bits=*/llvm::APInt(32, 1), /*isUnsigned=*/true);
+
+ for (unsigned i = 0; i < linearVars.size(); ++i) {
+ mlir::Value linearVar = linearVars[i];
+ auto idx = getFuncArgIndex(func, linearVar);
+ if (!idx) {
+ llvm::errs() << "NOTE: linear var is not a function argument: "
+ << linearVar << "\n";
+ continue;
+ }
+
+ llvm::omp::DeclareSimdAttrTy ¶mAttr = attrs[*idx];
+ paramAttr.Kind = llvm::omp::DeclareSimdKindTy::Linear;
+ paramAttr.HasVarStride = false;
+ paramAttr.StrideOrArg = one;
+
+ if (i >= linearSteps.size())
+ continue;
+
+ mlir::Value stepV = linearSteps[i];
+
+ // 1) Var-stride case: step is (or comes from) a function arg.
+ // In your failing example: StepV == %3, which is llvm.load %arg3
+ if (auto stepArgIdx = getFuncArgIndex(func, stepV)) {
+ paramAttr.HasVarStride = true;
+ paramAttr.StrideOrArg = llvm::APSInt(llvm::APInt(32, *stepArgIdx),
+ /*isUnsigned=*/true);
+ continue;
+ }
+
+ // 2) Constant step case.
+ if (auto cst = stepV.getDefiningOp<mlir::LLVM::ConstantOp>()) {
+ if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(cst.getValue())) {
+ paramAttr.HasVarStride = false;
+ // Signed vs unsigned doesn’t matter much for printing; use signed=false
+ // unless you want to preserve sign semantics for negative steps.
+ paramAttr.StrideOrArg =
+ llvm::APSInt(intAttr.getValue(), /*isUnsigned=*/false);
+ continue;
+ }
+ }
+
+ // 3) Otherwise: keep default step=1, but log.
+ llvm::errs() << "NOTE: could not decode linear step value: " << stepV
+ << " (keeping step=1)\n";
+ }
+}
+
+static llvm::omp::DeclareSimdBranch
+getDeclareSimdBranch(mlir::omp::DeclareSimdOp &op) {
+ if (op.getInbranch())
+ return llvm::omp::DeclareSimdBranch::Inbranch;
+ if (op.getNotinbranch())
+ return llvm::omp::DeclareSimdBranch::Notinbranch;
+ return llvm::omp::DeclareSimdBranch::Undefined;
+}
+
+static LogicalResult
+convertDeclareSimdOp(Operation &opInst, llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ auto funcOp = opInst.getParentOfType<LLVM::LLVMFuncOp>();
+ assert(funcOp && "declare_simd must be defined inside an LLVM function");
+
+ llvm::Function *fn = moduleTranslation.lookupFunction(funcOp.getName());
+ assert(fn && "Failed to find corresponding LLVM function for LLVMFuncOp");
+
+ funcOp.walk([&](mlir::omp::DeclareSimdOp ds) {
+ llvm::SmallVector<llvm::omp::DeclareSimdAttrTy, 8> paramAttrs(
+ funcOp.getNumArguments());
+
+ applyUniform(funcOp, ds, paramAttrs);
+ applyAligned(funcOp, ds, paramAttrs);
+ applyLinear(funcOp, ds, paramAttrs);
+
+ llvm::APSInt VLENVal;
+ if (std::optional<int64_t> simdlen = ds.getSimdlen()) {
+ VLENVal = llvm::APSInt(llvm::APInt(/*numBits=*/64, *simdlen),
+ /*isUnsigned=*/false);
+ }
+
+ // dumpParamAttrs(funcOp, paramAttrs);
+ llvm::omp::emitDeclareSimdFunction(fn, VLENVal, paramAttrs,
+ getDeclareSimdBranch(ds));
+ });
+
+ return success();
+}
+
/// Given an OpenMP MLIR operation, create the corresponding LLVM IR (including
/// OpenMP runtime calls).
static LogicalResult
@@ -6992,6 +7230,9 @@ convertHostOrTargetOperation(Operation *op, llvm::IRBuilderBase &builder,
.Case([&](omp::TaskwaitOp op) {
return convertOmpTaskwaitOp(op, builder, moduleTranslation);
})
+ .Case([&](omp::DeclareSimdOp op) {
+ return convertDeclareSimdOp(*op, builder, moduleTranslation);
+ })
.Case<omp::YieldOp, omp::TerminatorOp, omp::DeclareMapperOp,
omp::DeclareMapperInfoOp, omp::DeclareReductionOp,
omp::CriticalDeclareOp>([](auto op) {
diff --git a/mlir/test/Target/LLVMIR/openmp-declare-simd.mlir b/mlir/test/Target/LLVMIR/openmp-declare-simd.mlir
new file mode 100644
index 0000000000000..d4a909da0de48
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-declare-simd.mlir
@@ -0,0 +1,218 @@
+// RUN: mlir-translate --mlir-to-llvmir %s | FileCheck %s
+//
+// This test exercises translation of `omp.declare_simd` from MLIR LLVM dialect
+// to LLVM IR function attributes via llvm.
+//
+// For each `omp.declare_simd`, lowering computes:
+// 1) ParamAttrs: one entry per function argument, classifying it as
+// Vector / Uniform / Linear (+ step or var-stride) / Aligned.
+// 2) Branch kind: Undefined / Inbranch / Notinbranch.
+// 3) VLEN: either from `simdlen(...)` or derived from the CDT size.
+//
+// llvm then emits x86 declare-simd variants by attaching
+// mangled function attributes of the form:
+//
+// _ZGV <ISA> <Mask> <VLEN> <ParamAttrs> _ <FunctionName>
+//
+// where:
+// - ISA : b (SSE), c (AVX), d (AVX2), e (AVX-512)
+// - Mask : M (inbranch), N (notinbranch), or both if unspecified
+// - VLEN : explicit simdlen or computed from CDT size
+// - ParamAttrs encoding:
+// v = vector, u = uniform, l = linear
+// sN = var-stride using argument index N
+// aN = alignment N
+//
+
+module attributes {
+ llvm.target_triple = "x86_64-unknown-linux-gnu",
+ llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+} {
+
+ // - All parameters default to Vector
+ // - No branch clause => both masked (M) and unmasked (N) variants emitted
+ // - No simdlen => VLEN derived from CDT
+ // * CDT = return type i32 => 32 bits
+ // * VLEN = vector-register-size / 32
+ //
+ // CHECK-LABEL: define i32 @ds_minimal(ptr %0, ptr %1) #0
+ llvm.func @ds_minimal(%x: !llvm.ptr, %y: !llvm.ptr) -> i32 {
+ omp.declare_simd
+ %vx = llvm.load %x : !llvm.ptr -> i32
+ %vy = llvm.load %y : !llvm.ptr -> i32
+ %sum = llvm.add %vx, %vy : i32
+ llvm.return %sum : i32
+ }
+
+ // uniform + linear with variable stride + simdlen
+ //
+ // The linear step is produced by:
+ // %stepv = llvm.load %step
+ //
+ // This is recognized as a var-stride case:
+ // - Linear.HasVarStride = true
+ // - Linear.StrideOrArg = argument index of %step
+ //
+ // ParamAttrs:
+ // [0] Vector
+ // [1] Uniform
+ // [2] Linear(var-stride = arg3)
+ // [3] Vector
+ //
+ // No branch clause => both masked (M) and unmasked (N) variants emitted.
+ //
+ // CHECK-LABEL: define i32 @ds_uniform_linear_const_step_inbranch(ptr %0, ptr %1, ptr %2) #1 {
+ llvm.func @ds_uniform_linear_const_step_inbranch(
+ %x: !llvm.ptr, %y: !llvm.ptr, %i: !llvm.ptr) -> i32 {
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ omp.declare_simd simdlen(8) uniform(%y : !llvm.ptr) linear(%i = %c1 : !llvm.ptr) inbranch {linear_var_types = [i32]}
+ %vx = llvm.load %x : !llvm.ptr -> i32
+ %vy = llvm.load %y : !llvm.ptr -> i32
+ %sum = llvm.add %vx, %vy : i32
+ %vi = llvm.load %i : !llvm.ptr -> i32
+ %out = llvm.add %sum, %vi : i32
+ llvm.return %out : i32
+ }
+
+ // uniform + linear with variable stride + simdlen
+ //
+ // The linear step is produced by:
+ // %stepv = llvm.load %step
+ //
+ // This is recognized as a var-stride case:
+ // - Linear.HasVarStride = true
+ // - Linear.StrideOrArg = argument index of %step
+ //
+ // ParamAttrs:
+ // [0] Vector
+ // [1] Uniform
+ // [2] Linear(var-stride = arg3)
+ // [3] Vector
+ //
+ // No branch clause => both masked (M) and unmasked (N) variants emitted.
+ //
+ // CHECK-LABEL: define i32 @ds_uniform_linear_var_stride(ptr %0, ptr %1, ptr %2, ptr %3) #2 {
+ llvm.func @ds_uniform_linear_var_stride(
+ %x: !llvm.ptr, %y: !llvm.ptr, %i: !llvm.ptr, %step: !llvm.ptr) -> i32 {
+ %stepv = llvm.load %step : !llvm.ptr -> i32
+ omp.declare_simd simdlen(8) uniform(%y : !llvm.ptr) linear(%i = %stepv : !llvm.ptr) {linear_var_types = [i32]}
+ %vx = llvm.load %x : !llvm.ptr -> i32
+ %vy = llvm.load %y : !llvm.ptr -> i32
+ %sum = llvm.add %vx, %vy : i32
+ %vi = llvm.load %i : !llvm.ptr -> i32
+ %prod = llvm.mul %vi, %stepv : i32
+ %out = llvm.add %sum, %prod : i32
+ llvm.return %out : i32
+ }
+
+ // -------------------------------------------------------------------------
+ // aligned + uniform + notinbranch (no simdlen)
+ //
+ // ParamAttrs:
+ // [0] Vector, Alignment = 32
+ // [1] Uniform, Alignment = 128
+ // [2] Vector
+ //
+ // Branch:
+ // Notinbranch => only unmasked (N) variants emitted
+ //
+ // VLEN:
+ // No simdlen => derived from CDT (i32)
+ //
+ // CHECK-LABEL: define i32 @ds_aligned_uniform_notinbranch(ptr %0, ptr %1, ptr %2) #3 {
+ llvm.func @ds_aligned_uniform_notinbranch(
+ %p0: !llvm.ptr, %p1: !llvm.ptr, %i: !llvm.ptr) -> i32 {
+ omp.declare_simd aligned(%p0 : !llvm.ptr -> 32 : i64,
+ %p1 : !llvm.ptr -> 128 : i64)
+ uniform(%p1 : !llvm.ptr)
+ notinbranch
+ %v0 = llvm.load %p0 : !llvm.ptr -> i32
+ %v1 = llvm.load %p1 : !llvm.ptr -> i32
+ %sum = llvm.add %v0, %v1 : i32
+ %vi = llvm.load %i : !llvm.ptr -> i32
+ %out = llvm.add %sum, %vi : i32
+ llvm.return %out : i32
+ }
+
+ // Multiple declare_simd ops in the same function body
+ //
+ // Each omp.declare_simd independently contributes a set of
+ // vector-function attributes to the same LLVM function.
+ //
+ // CHECK-LABEL: define i32 @ds_multiple_ops_same_function(ptr %0, ptr %1, ptr %2) #4 {
+ llvm.func @ds_multiple_ops_same_function(%a: !llvm.ptr, %b: !llvm.ptr, %i: !llvm.ptr) -> i32 {
+ %c1 = llvm.mlir.constant(1 : i32) : i32
+ omp.declare_simd uniform(%b : !llvm.ptr) linear(%i = %c1 : !llvm.ptr) simdlen(4) {linear_var_types = [i32]}
+ omp.declare_simd uniform(%a : !llvm.ptr) simdlen(8)
+
+ %va = llvm.load %a : !llvm.ptr -> i32
+ %vb = llvm.load %b : !llvm.ptr -> i32
+ %sum = llvm.add %va, %vb : i32
+ %vi = llvm.load %i : !llvm.ptr -> i32
+ %out = llvm.add %sum, %vi : i32
+ llvm.return %out : i32
+ }
+}
+
+// no branch clause => both N and M, VLEN from CDT(i32)=32b
+//
+// CHECK: attributes #0 = {
+// CHECK-SAME: "_ZGVbM4vv_ds_minimal"
+// CHECK-SAME: "_ZGVbN4vv_ds_minimal"
+// CHECK-SAME: "_ZGVcN8vv_ds_minimal"
+// CHECK-SAME: "_ZGVdM8vv_ds_minimal"
+// CHECK-SAME: "_ZGVeM16vv_ds_minimal"
+// CHECK-SAME: "_ZGVeN16vv_ds_minimal"
+// CHECK-SAME: }
+//
+// uniform + linear with constant step + simdlen + inbranch
+//
+// CHECK: attributes #1 = {
+// CHECK-SAME: "_ZGVbM8vul_ds_uniform_linear_const_step_inbranch"
+// CHECK-SAME: "_ZGVcM8vul_ds_uniform_linear_const_step_inbranch"
+// CHECK-SAME: "_ZGVdM8vul_ds_uniform_linear_const_step_inbranch"
+// CHECK-SAME: "_ZGVeM8vul_ds_uniform_linear_const_step_inbranch"
+// CHECK-SAME: }
+//
+// uniform + linear with var-stride via `llvm.load %step` + simdlen
+//
+// CHECK: attributes #2 = {
+// CHECK-SAME: "_ZGVbM8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: "_ZGVbN8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: "_ZGVcM8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: "_ZGVcN8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: "_ZGVdM8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: "_ZGVdN8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: "_ZGVeM8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: "_ZGVeN8vuls3v_ds_uniform_linear_var_stride"
+// CHECK-SAME: }
+//
+// aligned + uniform + notinbranch
+//
+// CHECK: attributes #3 = {
+// CHECK-SAME: "_ZGVbN4va32ua128v_ds_aligned_uniform_notinbranch"
+// CHECK-SAME: "_ZGVcN8va32ua128v_ds_aligned_uniform_notinbranch"
+// CHECK-SAME: "_ZGVdN8va32ua128v_ds_aligned_uniform_notinbranch"
+// CHECK-SAME: "_ZGVeN16va32ua128v_ds_aligned_uniform_notinbranch"
+// CHECK-SAME: }
+//
+// multiple declare_simd ops in the same function body
+//
+// CHECK: attributes #4 = {
+// CHECK-SAME: "_ZGVbM4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVbM8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVbN4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVbN8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVcM4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVcM8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVcN4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVcN8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVdM4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVdM8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVdN4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVdN8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVeM4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVeM8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVeN4vul_ds_multiple_ops_same_function"
+// CHECK-SAME: "_ZGVeN8uvv_ds_multiple_ops_same_function"
+// CHECK-SAME: }
More information about the cfe-commits
mailing list