[llvm] [OpenMP][LLVM] Add pass to bridge declare simd to loop vectorization (PR #190384)

via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 3 11:52:13 PDT 2026


https://github.com/chichunchen created https://github.com/llvm/llvm-project/pull/190384

Add a OpenMP pass "InjectOpenMPVFABIMappings" to convert `_ZGV` function attributes emitted by Clang or Flang for `#pragma omp declare simd` into `vector-function-abi-variant` attribute and vector function declarations that LoopVectorize consumes via `VFDatabase`.

The end-to-end process:

1. Frontend (Clang[1] / Flang[2]):

The frontend sees `#pragma omp declare simd` and computes VFABI mangled names based on the target's natural vector lengths. These are emitted as boolean function attributes on the scalar function:

```
    declare double @_func(double) #0
    attributes #0 = { "_ZGVnM2v_compute" "_ZGVnN2v_compute" }
```

2. InjectOpenMPVFABIMappings:

Runs as a module pass early in the -O2 pipeline. For each function: (a) Scan attributes for keys starting with `_ZGV`. (b) Demangle each via VFABI::tryDemangleForVFABI() to obtain ISA, VF, mask, and parameter info. (c) Create a vector function type (e.g., double(double) at VF=2 becomes <2 x double>(<2 x double>); masked adds <2 x i1>). (d) Emit an external declaration with the appropriate calling convention (aarch64_vector_pcs for NEON, aarch64_sve_vector_pcs for SVE, default C for x86). (e) Add the declaration to @llvm.compiler.used. (f) Replace the raw boolean attrs with a consolidated vector-function-abi-variant attribute.

  Output:

  ```
    declare double @_func(double) #0
    declare aarch64_vector_pcs <2 x double> @_ZGVnN2v_compute(<2 x double>)
    declare aarch64_vector_pcs <2 x double> @_ZGVnM2v_compute(<2 x double>, <2 x i1>)
    attributes #0 = {
      "vector-function-abi-variant"=
        "_ZGVnM2v_compute(_ZGVnM2v_compute),_ZGVnN2v_compute(_ZGVnN2v_compute)"
    }
  ```

3. VFDatabase Lookup:
  
When LoopVectorize considers a call to @_func, `VFDatabase::getMappings()` reads the vector-function-abi-variant attribute (falling through from call-site to callee via CallBase::getFnAttr), demangles each name, and verifies the vector function declaration exists in the module. This is why `InjectOpenMPVFABIMappings` must create the declarations.

4. LoopVectorize Cost Model:
  
For each VFInfo candidate, the cost model checks VF compatibility, masking support, and parameter kinds, then compares vector call cost against scalar call cost. If vector is cheaper, it selects CM_VectorCall.

5. Code Generation:

VPWidenCallRecipe replaces the scalar call with the vector variant:

  ```
    %wide.load = load <2 x double>, ptr %ptr
    %vec.call = call aarch64_vector_pcs <2 x double>
        @_ZGVnN2v_compute(<2 x double> %wide.load)
  ```

- [1] Clang declare simd implementation
    - https://github.com/llvm/llvm-project/commit/c7a82b41a706728ce7c212b5bc40c74d1cce53c7
    - https://github.com/llvm/llvm-project/commit/a0a2264ef757f8383c6b283b7ad80b33d5d52f13
-  [2] Flang work is under review in #187767

Assisted with copilot and claude opus 4.6

>From 3a5b33870cdd290ce749d0e38aa321a53073e28d Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Thu, 2 Apr 2026 13:32:28 -0500
Subject: [PATCH] [OpenMP][LLVM] Add pass to bridge declare simd to loop
 vectorization
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a OpenMP pass "InjectOpenMPVFABIMappings" to convert `_ZGV` function
attributes emitted by Clang or Flang for `#pragma omp declare simd` into
`vector-function-abi-variant` attribute and vector function declarations
that LoopVectorize consumes via `VFDatabase`.

The end-to-end process:

- Frontend (Clang[1] / Flang[2]):
  The frontend sees `#pragma omp declare simd` and computes VFABI mangled
  names based on the target's natural vector lengths. These are emitted as
  boolean function attributes on the scalar function:

  ```
    declare double @_func(double) #0
    attributes #0 = { "_ZGVnM2v_compute" "_ZGVnN2v_compute" }
  ```

— InjectOpenMPVFABIMappings:
  Runs as a module pass early in the -O2 pipeline. For each function:
  (a) Scan attributes for keys starting with `_ZGV`.
  (b) Demangle each via VFABI::tryDemangleForVFABI() to obtain ISA, VF,
      mask, and parameter info.
  (c) Create a vector function type (e.g., double(double) at VF=2
      becomes <2 x double>(<2 x double>); masked adds <2 x i1>).
  (d) Emit an external declaration with the appropriate calling
      convention (aarch64_vector_pcs for NEON, aarch64_sve_vector_pcs
      for SVE, default C for x86).
  (e) Add the declaration to @llvm.compiler.used.
  (f) Replace the raw boolean attrs with a consolidated
      vector-function-abi-variant attribute.

  Output:

  ```
    declare double @_func(double) #0
    declare aarch64_vector_pcs <2 x double> @_ZGVnN2v_compute(<2 x double>)
    declare aarch64_vector_pcs <2 x double> @_ZGVnM2v_compute(<2 x double>, <2 x i1>)
    attributes #0 = {
      "vector-function-abi-variant"=
        "_ZGVnM2v_compute(_ZGVnM2v_compute),_ZGVnN2v_compute(_ZGVnN2v_compute)"
    }
  ```

— VFDatabase Lookup:
  When LoopVectorize considers a call to @_func, `VFDatabase::getMappings()`
  reads the vector-function-abi-variant attribute (falling through from
  call-site to callee via CallBase::getFnAttr), demangles each name, and
  verifies the vector function declaration exists in the module. This is
  why `InjectOpenMPVFABIMappings` must create the declarations.

— LoopVectorize Cost Model:
  For each VFInfo candidate, the cost model checks VF compatibility,
  masking support, and parameter kinds, then compares vector call cost
  against scalar call cost. If vector is cheaper, it selects CM_VectorCall.

— Code Generation:
  VPWidenCallRecipe replaces the scalar call with the vector variant:

  ```
    %wide.load = load <2 x double>, ptr %ptr
    %vec.call = call aarch64_vector_pcs <2 x double>
        @_ZGVnN2v_compute(<2 x double> %wide.load)
  ```

[1] Clang declare simd implementation
    - https://github.com/llvm/llvm-project/commit/c7a82b41a706728ce7c212b5bc40c74d1cce53c7
    - https://github.com/llvm/llvm-project/commit/a0a2264ef757f8383c6b283b7ad80b33d5d52f13
[2] Flang work is under review in #187767

Assisted with copilot and claude opus 4.6
---
 .../Utils/InjectOpenMPVFABIMappings.h         |  32 ++++
 llvm/lib/Passes/PassBuilder.cpp               |   1 +
 llvm/lib/Passes/PassBuilderPipelines.cpp      |   4 +
 llvm/lib/Passes/PassRegistry.def              |   1 +
 llvm/lib/Transforms/Utils/CMakeLists.txt      |   1 +
 .../Utils/InjectOpenMPVFABIMappings.cpp       | 158 ++++++++++++++++++
 .../Util/inject-openmp-vfabi-mappings.ll      |  87 ++++++++++
 7 files changed, 284 insertions(+)
 create mode 100644 llvm/include/llvm/Transforms/Utils/InjectOpenMPVFABIMappings.h
 create mode 100644 llvm/lib/Transforms/Utils/InjectOpenMPVFABIMappings.cpp
 create mode 100644 llvm/test/Transforms/Util/inject-openmp-vfabi-mappings.ll

diff --git a/llvm/include/llvm/Transforms/Utils/InjectOpenMPVFABIMappings.h b/llvm/include/llvm/Transforms/Utils/InjectOpenMPVFABIMappings.h
new file mode 100644
index 0000000000000..fa620c9eee663
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/InjectOpenMPVFABIMappings.h
@@ -0,0 +1,32 @@
+//===- InjectOpenMPVFABIMappings.h - OpenMP _ZGV to VFABI conversion ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Converts raw `_ZGV...` function attributes (emitted by OMPIRBuilder for
+// `declare simd` functions) into the structured `vector-function-abi-variant`
+// attribute that LoopVectorize / VFDatabase consumes, and emits external
+// declarations for the vector variants.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TRANSFORMS_UTILS_INJECTOPENMPVFABIMAPPINGS_H
+#define LLVM_TRANSFORMS_UTILS_INJECTOPENMPVFABIMAPPINGS_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class Module;
+
+class InjectOpenMPVFABIMappings
+    : public PassInfoMixin<InjectOpenMPVFABIMappings> {
+public:
+  LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_INJECTOPENMPVFABIMAPPINGS_H
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index a23d64b491a79..b4fda877a7678 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -358,6 +358,7 @@
 #include "llvm/Transforms/Utils/FixIrreducible.h"
 #include "llvm/Transforms/Utils/HelloWorld.h"
 #include "llvm/Transforms/Utils/IRNormalizer.h"
+#include "llvm/Transforms/Utils/InjectOpenMPVFABIMappings.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
 #include "llvm/Transforms/Utils/InstructionNamer.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 6f5b1c2e2fcc7..f7142401bfaa0 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -140,6 +140,7 @@
 #include "llvm/Transforms/Utils/CountVisits.h"
 #include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
 #include "llvm/Transforms/Utils/ExtraPassManager.h"
+#include "llvm/Transforms/Utils/InjectOpenMPVFABIMappings.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
 #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h"
 #include "llvm/Transforms/Utils/Mem2Reg.h"
@@ -1635,6 +1636,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
                           .speculateUnpredictables(true)
                           .hoistLoadsStoresWithCondFaulting(true)));
 
+  // Enable LoopVectorize for OpenMP declare simd.
+  MPM.addPass(InjectOpenMPVFABIMappings());
+
   // Add the core optimizing pipeline.
   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizePM),
                                                 PTO.EagerlyInvalidateAnalyses));
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index c92d93d7ae396..ee19fd00fdbef 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -97,6 +97,7 @@ MODULE_PASS("inliner-ml-advisor-release",
 MODULE_PASS("inliner-wrapper", ModuleInlinerWrapperPass())
 MODULE_PASS("inliner-wrapper-no-mandatory-first",
             ModuleInlinerWrapperPass(getInlineParams(), false))
+MODULE_PASS("inject-openmp-vfabi-mappings", InjectOpenMPVFABIMappings())
 MODULE_PASS("insert-gcov-profiling", GCOVProfilerPass())
 MODULE_PASS("instrprof", InstrProfilingLoweringPass())
 MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass())
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index 2b5f5cf344e60..ec6bcfeb78d56 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -36,6 +36,7 @@ add_llvm_component_library(LLVMTransformUtils
   HelloWorld.cpp
   InlineFunction.cpp
   InjectTLIMappings.cpp
+  InjectOpenMPVFABIMappings.cpp
   InstructionNamer.cpp
   Instrumentation.cpp
   IntegerDivision.cpp
diff --git a/llvm/lib/Transforms/Utils/InjectOpenMPVFABIMappings.cpp b/llvm/lib/Transforms/Utils/InjectOpenMPVFABIMappings.cpp
new file mode 100644
index 0000000000000..57460bb8d4447
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/InjectOpenMPVFABIMappings.cpp
@@ -0,0 +1,158 @@
+//===- InjectOpenMPVFABIMappings.cpp - OpenMP _ZGV to VFABI conversion ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Converts `_ZGV...` function attributes (emitted by OMPIRBuilder for OpenMP
+// `declare simd` functions) into `vector-function-abi-variant` attribute that
+// LoopVectorize / VFDatabase consumes.
+//
+// OMPIRBuilder emits one function attribute per vector variant:
+//
+//   attributes #0 = { "_ZGVnN4v_foo" "_ZGVnM4v_foo" }
+//
+// VFDatabase::getMappings() reads the `vector-function-abi-variant`
+// attribute (via CallBase::getFnAttr fallthrough to callee attrs):
+//
+//   attributes #0 = {
+//     "vector-function-abi-variant"=
+//       "_ZGVnN4v_foo(_ZGVnN4v_foo),_ZGVnM4v_foo(_ZGVnM4v_foo)" }
+//
+// This pass bridges the two by:
+//   1. Collecting all `_ZGV*` attrs from each function.
+//   2. Demangling each to a VFInfo via VFABI::tryDemangleForVFABI.
+//   3. Creating external declarations for the vector variants (so
+//      VFDatabase can verify they exist via Module::getFunction).
+//   4. Consolidating the names into a single `vector-function-abi-variant`
+//      attribute and removing the `_ZGV...` attrs.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/InjectOpenMPVFABIMappings.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/AttributeMask.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/CallingConv.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/VFABIDemangler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+#define DEBUG_TYPE "inject-openmp-vfabi-mappings"
+
+using namespace llvm;
+
+STATISTIC(NumFunctionsInjected,
+          "Number of functions with VFABI mappings injected.");
+STATISTIC(NumVFDeclAdded, "Number of vector function declarations added.");
+STATISTIC(NumCompUsedAdded,
+          "Number of `@llvm.compiler.used` operands that have been added.");
+
+// Convert `_ZGV` attrs into `vector-function-abi-variant` and emit
+// vector declarations.
+static void injectMappingsForFunction(Function &F) {
+  Module *M = F.getParent();
+  FunctionType *ScalarFTy = F.getFunctionType();
+
+  // Collect raw _ZGV attrs from the function.
+  SmallVector<std::string, 8> MangledNames;
+  for (const Attribute &A : F.getAttributes().getFnAttrs()) {
+    if (!A.isStringAttribute())
+      continue;
+    StringRef Key = A.getKindAsString();
+    if (!Key.starts_with("_ZGV"))
+      continue;
+    MangledNames.push_back(Key.str());
+  }
+  if (MangledNames.empty())
+    return;
+
+  // Build VFABI mapping strings and vector declarations.
+  SmallVector<std::string, 8> Mappings;
+  for (const std::string &Name : MangledNames) {
+    std::optional<VFInfo> Info = VFABI::tryDemangleForVFABI(Name, ScalarFTy);
+    if (!Info)
+      continue;
+
+    // Build the VFABI mapping: "_ZGVnN4v_foo(_ZGVnN4v_foo)"
+    Mappings.push_back(Name + "(" + Info->VectorName + ")");
+
+    // Add vector function declaration if it doesn't already exist.
+    if (!M->getFunction(Info->VectorName)) {
+      FunctionType *VecFTy = VFABI::createFunctionType(*Info, ScalarFTy);
+      Function *VecFunc = Function::Create(VecFTy, Function::ExternalLinkage,
+                                           Info->VectorName, M);
+
+      // Copy attributes from the scalar function and strip ones that are
+      // incompatible with the vectorized types (e.g., signext on vectors).
+      VecFunc->copyAttributesFrom(&F);
+      VecFunc->removeRetAttrs(AttributeFuncs::typeIncompatible(
+          VecFunc->getReturnType(), VecFunc->getAttributes().getRetAttrs()));
+      for (auto &Arg : VecFunc->args())
+        Arg.removeAttrs(AttributeFuncs::typeIncompatible(
+            Arg.getType(), Arg.getAttributes()));
+      // Remove _ZGV attrs that were copied from the scalar function.
+      for (const std::string &N : MangledNames)
+        VecFunc->removeFnAttr(N);
+      // The vector declaration does not need the mapping attribute itself.
+      VecFunc->removeFnAttr(VFABI::MappingsAttrName);
+
+      // Set the appropriate calling convention for the vector variant.
+      // The Clang/Flang declare simd codegen only targets AArch64 and x86.
+      // AArch64 NEON uses aarch64_vector_pcs and SVE uses
+      // aarch64_sve_vector_pcs. x86 ISAs (SSE, AVX, AVX2, AVX512) use the
+      // default C calling convention, consistent with VecFuncs.def.
+      switch (Info->ISA) {
+      case VFISAKind::AdvancedSIMD:
+        VecFunc->setCallingConv(CallingConv::AArch64_VectorCall);
+        break;
+      case VFISAKind::SVE:
+        VecFunc->setCallingConv(CallingConv::AArch64_SVE_VectorCall);
+        break;
+      default:
+        break;
+      }
+
+      LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Added to the module: `"
+                        << Info->VectorName << "` of type " << *VecFTy << "\n");
+      ++NumVFDeclAdded;
+
+      // Make function declaration (without a body) "sticky" in the IR by
+      // listing it in the @llvm.compiler.used intrinsic.
+      appendToCompilerUsed(*M, {VecFunc});
+      LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Adding `" << Info->VectorName
+                  << "` to `@llvm.compiler.used`.\n");
+      ++NumCompUsedAdded;
+    }
+
+    // Remove "_ZGV...".
+    F.removeFnAttr(Name);
+  }
+
+  // Set the consolidated vector-function-abi-variant attribute.
+  if (!Mappings.empty()) {
+    SmallString<256> Buffer;
+    raw_svector_ostream Out(Buffer);
+    interleave(Mappings, Out, [&](const std::string &S) { Out << S; }, ",");
+    F.addFnAttr(VFABI::MappingsAttrName, Buffer.str());
+    LLVM_DEBUG(dbgs() << DEBUG_TYPE << ": Set `" << VFABI::MappingsAttrName
+                      << "` for `" << F.getName() << "` to `" << Buffer
+                      << "`\n");
+    ++NumFunctionsInjected;
+  }
+}
+
+PreservedAnalyses InjectOpenMPVFABIMappings::run(Module &M,
+                                                 ModuleAnalysisManager &AM) {
+  for (Function &F : M)
+    injectMappingsForFunction(F);
+  // Only IR attributes are added; all analyses remain valid.
+  return PreservedAnalyses::all();
+}
diff --git a/llvm/test/Transforms/Util/inject-openmp-vfabi-mappings.ll b/llvm/test/Transforms/Util/inject-openmp-vfabi-mappings.ll
new file mode 100644
index 0000000000000..2f07b5c001bb0
--- /dev/null
+++ b/llvm/test/Transforms/Util/inject-openmp-vfabi-mappings.ll
@@ -0,0 +1,87 @@
+; RUN: opt -passes='inject-openmp-vfabi-mappings' -S < %s | FileCheck %s
+
+;; Verify @llvm.compiler.used contains the vector declarations.
+; CHECK-DAG: @llvm.compiler.used = appending global [{{[0-9]+}} x ptr] [{{.*}}ptr @_ZGVnN2v_neon_single{{.*}}], section "llvm.metadata"
+
+;; AArch64 Advanced SIMD (ISA "n"): single variant on a declaration.
+declare void @neon_single(double) #0
+; CHECK-DAG: declare aarch64_vector_pcs void @_ZGVnN2v_neon_single(<2 x double>)
+; CHECK-DAG: attributes #0 = {{{.*}}"vector-function-abi-variant"="_ZGVnN2v_neon_single(_ZGVnN2v_neon_single)"{{.*}}}
+
+;; AArch64 Advanced SIMD: multiple variants on the same function.
+declare float @neon_multi(float, float) #1
+; CHECK-DAG: declare aarch64_vector_pcs <4 x float> @_ZGVnN4vv_neon_multi(<4 x float>, <4 x float>)
+; CHECK-DAG: declare aarch64_vector_pcs <2 x float> @_ZGVnN2vv_neon_multi(<2 x float>, <2 x float>)
+; CHECK-DAG: attributes #1 = {{{.*}}"vector-function-abi-variant"="{{.*}}_ZGVnN2vv_neon_multi(_ZGVnN2vv_neon_multi){{.*}}_ZGVnN4vv_neon_multi(_ZGVnN4vv_neon_multi){{.*}}"{{.*}}}
+
+;; AArch64 Advanced SIMD: function definition (not just declaration).
+define void @neon_def(i32 %x) #2 {
+entry:
+  ret void
+}
+; CHECK-DAG: declare aarch64_vector_pcs void @_ZGVnN4v_neon_def(<4 x i32>)
+; CHECK-DAG: attributes #2 = {{{.*}}"vector-function-abi-variant"="_ZGVnN4v_neon_def(_ZGVnN4v_neon_def)"{{.*}}}
+
+;; AArch64 Advanced SIMD: linear parameter with stride.
+declare void @neon_linear(ptr, i32) #3
+; CHECK-DAG: declare aarch64_vector_pcs void @_ZGVnN4l2v_neon_linear(ptr, <4 x i32>)
+; CHECK-DAG: attributes #3 = {{{.*}}"vector-function-abi-variant"="_ZGVnN4l2v_neon_linear(_ZGVnN4l2v_neon_linear)"{{.*}}}
+
+;; AArch64 Advanced SIMD: masked variant (ISA "n", Mask "M").
+declare double @neon_masked(double) #4
+; CHECK-DAG: declare aarch64_vector_pcs <2 x double> @_ZGVnM2v_neon_masked(<2 x double>, <2 x i1>)
+; CHECK-DAG: attributes #4 = {{{.*}}"vector-function-abi-variant"="_ZGVnM2v_neon_masked(_ZGVnM2v_neon_masked)"{{.*}}}
+
+;; AArch64 SVE (ISA "s"): uses AArch64_SVE_VectorCall calling convention.
+declare double @sve_fn(double) #5
+; CHECK-DAG: declare aarch64_sve_vector_pcs <vscale x 2 x double> @_ZGVsMxv_sve_fn(<vscale x 2 x double>, <vscale x 2 x i1>)
+; CHECK-DAG: attributes #5 = {{{.*}}"vector-function-abi-variant"="_ZGVsMxv_sve_fn(_ZGVsMxv_sve_fn)"{{.*}}}
+
+;; x86 SSE (ISA "b"): uses default calling convention.
+declare double @sse_fn(double) #6
+; CHECK-DAG: declare <2 x double> @_ZGVbN2v_sse_fn(<2 x double>)
+; CHECK-DAG: attributes #6 = {{{.*}}"vector-function-abi-variant"="_ZGVbN2v_sse_fn(_ZGVbN2v_sse_fn)"{{.*}}}
+
+;; x86 AVX2 (ISA "d"): uses default calling convention.
+declare double @avx2_fn(double) #7
+; CHECK-DAG: declare <4 x double> @_ZGVdN4v_avx2_fn(<4 x double>)
+; CHECK-DAG: attributes #7 = {{{.*}}"vector-function-abi-variant"="_ZGVdN4v_avx2_fn(_ZGVdN4v_avx2_fn)"{{.*}}}
+
+;; x86 AVX512 (ISA "e"): uses default calling convention.
+declare double @avx512_fn(double) #8
+; CHECK-DAG: declare <8 x double> @_ZGVeN8v_avx512_fn(<8 x double>)
+; CHECK-DAG: attributes #8 = {{{.*}}"vector-function-abi-variant"="_ZGVeN8v_avx512_fn(_ZGVeN8v_avx512_fn)"{{.*}}}
+
+;; No _ZGV attrs: function is not modified.
+declare void @no_zgv(double) #9
+; CHECK-DAG: attributes #9 = { noinline }
+
+;; Pre-existing vector-function-abi-variant with additional _ZGV attr:
+;; the new _ZGV attr is processed and the existing mapping is overwritten.
+declare void @already_has(double) #10
+; CHECK-DAG: declare aarch64_vector_pcs void @_ZGVnN2v_already_has_extra(<2 x double>)
+; CHECK-DAG: attributes #10 = {{{.*}}"vector-function-abi-variant"="_ZGVnN2v_already_has_extra(_ZGVnN2v_already_has_extra)"{{.*}}}
+
+;; Malformed _ZGV attr: demangling fails, attr is kept; valid _ZGV is processed.
+declare void @malformed(double) #11
+; CHECK-DAG: declare aarch64_vector_pcs void @_ZGVnN2v_malformed(<2 x double>)
+; CHECK-DAG: attributes #11 = {{{.*}}"_ZGV_malformed_name"{{.*}}"vector-function-abi-variant"="_ZGVnN2v_malformed(_ZGVnN2v_malformed)"{{.*}}}
+
+;; Scalar function with nounwind — vector decl should inherit it.
+declare void @with_attrs(double) #12
+; CHECK-DAG: declare aarch64_vector_pcs void @_ZGVnN2v_with_attrs(<2 x double>) [[ATTRS:#[0-9]+]]
+; CHECK-DAG: attributes [[ATTRS]] = { nounwind }
+
+attributes #0 = { "_ZGVnN2v_neon_single" }
+attributes #1 = { "_ZGVnN4vv_neon_multi" "_ZGVnN2vv_neon_multi" }
+attributes #2 = { "_ZGVnN4v_neon_def" }
+attributes #3 = { "_ZGVnN4l2v_neon_linear" }
+attributes #4 = { "_ZGVnM2v_neon_masked" }
+attributes #5 = { "_ZGVsMxv_sve_fn" }
+attributes #6 = { "_ZGVbN2v_sse_fn" }
+attributes #7 = { "_ZGVdN4v_avx2_fn" }
+attributes #8 = { "_ZGVeN8v_avx512_fn" }
+attributes #9 = { noinline }
+attributes #10 = { "vector-function-abi-variant"="_ZGVnN2v_already_has(_ZGVnN2v_already_has)" "_ZGVnN2v_already_has_extra" }
+attributes #11 = { "_ZGV_malformed_name" "_ZGVnN2v_malformed" }
+attributes #12 = { nounwind "_ZGVnN2v_with_attrs" }



More information about the llvm-commits mailing list