[llvm] [Hexagon] Add GlobalArrayAlignment pass (PR #217850)

Akash Agrawal via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 27 21:07:48 PDT 2026


https://github.com/akashagrwl updated https://github.com/llvm/llvm-project/pull/217850

>From d5f2fc61f13cea4cb25a446c9e2b1f17265b6217 Mon Sep 17 00:00:00 2001
From: Akash Agrawal <akashag at qti.qualcomm.com>
Date: Fri, 21 Aug 2026 01:46:55 -0700
Subject: [PATCH 1/2] [Hexagon] Add GlobalArrayAlignment pass

Add a module pass that raises the alignment of global integer arrays
(char, short, int), including multi-dimensional arrays, to an 8-byte
boundary. This gives their base address a wider alignment, which is
beneficial for the wide (double-word) loads and stores available on
Hexagon.

At -O1/-O2 the pass keeps byte and half-word arrays at their natural
alignment to reduce .rodata size; full 8-byte alignment is applied at
-O3. This size-reduction behavior can be disabled with
-hexagon-disable-align-opt-byte-half.

The pass is enabled by default and can be disabled with
-hexagon-disable-global-array-align.
---
 llvm/lib/Target/Hexagon/CMakeLists.txt        |   1 +
 llvm/lib/Target/Hexagon/Hexagon.h             |   4 +
 .../Hexagon/HexagonAlignGlobalArrays.cpp      | 125 ++++++++++++++++++
 .../Target/Hexagon/HexagonTargetMachine.cpp   |   6 +
 .../CodeGen/Hexagon/align-global-arrays.ll    |  72 ++++++++++
 5 files changed, 208 insertions(+)
 create mode 100644 llvm/lib/Target/Hexagon/HexagonAlignGlobalArrays.cpp
 create mode 100644 llvm/test/CodeGen/Hexagon/align-global-arrays.ll

diff --git a/llvm/lib/Target/Hexagon/CMakeLists.txt b/llvm/lib/Target/Hexagon/CMakeLists.txt
index 0f871c1f65cd4..bdd826e7950fe 100644
--- a/llvm/lib/Target/Hexagon/CMakeLists.txt
+++ b/llvm/lib/Target/Hexagon/CMakeLists.txt
@@ -18,6 +18,7 @@ add_public_tablegen_target(HexagonCommonTableGen)
 
 add_llvm_target(HexagonCodeGen
   BitTracker.cpp
+  HexagonAlignGlobalArrays.cpp
   HexagonAsmPrinter.cpp
   HexagonBitSimplify.cpp
   HexagonBitTracker.cpp
diff --git a/llvm/lib/Target/Hexagon/Hexagon.h b/llvm/lib/Target/Hexagon/Hexagon.h
index f7242d0d843ed..b0e5041fb850a 100644
--- a/llvm/lib/Target/Hexagon/Hexagon.h
+++ b/llvm/lib/Target/Hexagon/Hexagon.h
@@ -19,6 +19,7 @@
 namespace llvm {
 class HexagonTargetMachine;
 class ImmutablePass;
+class ModulePass;
 class PassRegistry;
 class FunctionPass;
 class Pass;
@@ -27,6 +28,7 @@ extern char &HexagonCopyHoistingID;
 extern char &HexagonExpandCondsetsID;
 extern char &HexagonTfrCleanupID;
 extern char &HexagonLiveVariablesID;
+void initializeHexagonAlignGlobalArraysPass(PassRegistry &);
 void initializeHexagonAsmPrinterPass(PassRegistry &);
 void initializeHexagonBitSimplifyPass(PassRegistry &);
 void initializeHexagonBranchRelaxationPass(PassRegistry &);
@@ -81,6 +83,8 @@ Pass *createHexagonVectorLoopCarriedReuseLegacyPass();
 ImmutablePass *
 createHexagonTargetTransformInfoPass(const HexagonTargetMachine *TM);
 
+ModulePass *createHexagonAlignGlobalArrays(bool ReduceRodataSize);
+
 FunctionPass *createHexagonBitSimplify();
 FunctionPass *createHexagonBranchRelaxation();
 FunctionPass *createHexagonCallFrameInformation();
diff --git a/llvm/lib/Target/Hexagon/HexagonAlignGlobalArrays.cpp b/llvm/lib/Target/Hexagon/HexagonAlignGlobalArrays.cpp
new file mode 100644
index 0000000000000..42f5c6ca1df1f
--- /dev/null
+++ b/llvm/lib/Target/Hexagon/HexagonAlignGlobalArrays.cpp
@@ -0,0 +1,125 @@
+//===- HexagonAlignGlobalArrays.cpp - Align Global Arrays -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass increases the alignment of global integer arrays (char, short,
+// int), including multi-dimensional arrays, to an 8-byte boundary. This gives
+// their base address a wider alignment, which is beneficial for the wide
+// (double-word) loads and stores available on Hexagon.
+//
+// When optimizing to reduce .rodata size, byte and half-word arrays already at
+// an alignment of two bytes or less are left at their natural alignment; only
+// word arrays are promoted. This size behavior can be turned off with
+// -hexagon-disable-align-opt-byte-half.
+//
+// The pass is enabled by default and can be disabled with
+// -hexagon-disable-global-array-align.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Hexagon.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "hexagon-global-array-alignment"
+
+static cl::opt<bool> DisableGlobalArrayAlignment(
+    "hexagon-disable-global-array-align",
+    cl::desc("Disable aligning global integer arrays to an 8-byte boundary"),
+    cl::init(false), cl::Hidden);
+
+static cl::opt<bool> DisableHexAlignOptByteHalf(
+    "hexagon-disable-align-opt-byte-half",
+    cl::desc("Disable keeping byte and half-word arrays at their natural "
+             "alignment when reducing .rodata size"),
+    cl::Hidden);
+
+namespace {
+
+class HexagonAlignGlobalArrays : public ModulePass {
+  bool ReduceRodataSize;
+
+public:
+  static char ID;
+
+  explicit HexagonAlignGlobalArrays(bool ReduceRodataSize = false)
+      : ModulePass(ID), ReduceRodataSize(ReduceRodataSize) {}
+
+  StringRef getPassName() const override {
+    return "Hexagon Global Array Alignment";
+  }
+
+  bool runOnModule(Module &M) override;
+};
+
+} // end anonymous namespace
+
+char HexagonAlignGlobalArrays::ID = 0;
+
+INITIALIZE_PASS(HexagonAlignGlobalArrays, "hexagon-global-array-alignment",
+                "Align Global Arrays to 8-byte", false, false)
+
+ModulePass *llvm::createHexagonAlignGlobalArrays(bool ReduceRodataSize) {
+  return new HexagonAlignGlobalArrays(ReduceRodataSize);
+}
+
+// Get the underlying element type of an array. This is useful if the array is
+// multi-dimensional.
+static Type *getUnderlyingArrayElmTy(Type *Ty) {
+  // Ty is guaranteed to be an array type.
+  Type *ElTy = cast<ArrayType>(Ty)->getElementType();
+  while (ElTy->isArrayTy())
+    ElTy = cast<ArrayType>(ElTy)->getElementType();
+  return ElTy;
+}
+
+bool HexagonAlignGlobalArrays::runOnModule(Module &M) {
+  if (DisableGlobalArrayAlignment)
+    return false;
+
+  bool Changed = false;
+  const DataLayout &DL = M.getDataLayout();
+
+  for (GlobalVariable &GV : M.globals()) {
+    Type *VT = GV.getValueType();
+    if (!VT->isArrayTy())
+      continue;
+
+    Type *ElTy = getUnderlyingArrayElmTy(VT);
+    if (!ElTy->isIntegerTy())
+      continue;
+
+    // Skip globals whose alignment cannot be safely raised, e.g. declarations,
+    // weak/interposable definitions, and section-pinned globals.
+    if (!GV.canIncreaseAlignment())
+      continue;
+
+    // Compute the current alignment, falling back to the ABI alignment.
+    MaybeAlign GVAlign = GV.getAlign();
+    if (!GVAlign && VT->isSized())
+      GVAlign = DL.getABITypeAlign(VT);
+
+    // Align integer arrays to an 8-byte boundary. When reducing .rodata size,
+    // leave byte and half-word arrays that are already at an alignment of two
+    // bytes or less at their natural alignment; word arrays are still promoted.
+    if (!ReduceRodataSize || !GVAlign || *GVAlign > Align(2) ||
+        DisableHexAlignOptByteHalf) {
+      MaybeAlign NewAlign = std::max(GVAlign.valueOrOne(), Align(8));
+      if (NewAlign != GVAlign) {
+        GV.setAlignment(NewAlign);
+        Changed = true;
+        LLVM_DEBUG(dbgs() << GV << '\n');
+      }
+    }
+  }
+
+  return Changed;
+}
diff --git a/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp b/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp
index 8728bd996551b..70f6bdaba0511 100644
--- a/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp
+++ b/llvm/lib/Target/Hexagon/HexagonTargetMachine.cpp
@@ -206,6 +206,7 @@ LLVMInitializeHexagonTarget() {
   RegisterTargetMachine<HexagonTargetMachine> X(getTheHexagonTarget());
 
   PassRegistry &PR = *PassRegistry::getPassRegistry();
+  initializeHexagonAlignGlobalArraysPass(PR);
   initializeHexagonAsmPrinterPass(PR);
   initializeHexagonBitSimplifyPass(PR);
   initializeHexagonConstExtendersPass(PR);
@@ -389,6 +390,11 @@ void HexagonPassConfig::addIRPasses() {
   bool NoOpt = (getOptLevel() == CodeGenOptLevel::None);
 
   if (!NoOpt) {
+    // Raise the alignment of global integer arrays to 8 bytes. At -O1/-O2,
+    // reduce .rodata size by keeping byte/half-word arrays at their natural
+    // alignment; apply full 8-byte alignment at -O3.
+    addPass(createHexagonAlignGlobalArrays(getOptLevel() !=
+                                           CodeGenOptLevel::Aggressive));
     if (EnableInstSimplify)
       addPass(createInstSimplifyLegacyPass());
     addPass(createDeadCodeEliminationPass());
diff --git a/llvm/test/CodeGen/Hexagon/align-global-arrays.ll b/llvm/test/CodeGen/Hexagon/align-global-arrays.ll
new file mode 100644
index 0000000000000..d1f46c9715f4b
--- /dev/null
+++ b/llvm/test/CodeGen/Hexagon/align-global-arrays.ll
@@ -0,0 +1,72 @@
+; The pass is enabled by default. At -O3 it does not reduce .rodata size, so
+; every locally-defined global integer array (including multi-dimensional ones)
+; is aligned to at least 8 bytes.
+; RUN: llc -mtriple=hexagon -O3 \
+; RUN:   -stop-after=hexagon-global-array-alignment < %s | FileCheck %s
+;
+; At -O1 and -O2 the pass reduces .rodata size, so byte and half-word arrays
+; with alignment <= 2 keep their natural alignment, while word arrays are
+; still aligned to 8 bytes.
+; RUN: llc -mtriple=hexagon -O1 \
+; RUN:   -stop-after=hexagon-global-array-alignment < %s \
+; RUN:   | FileCheck %s --check-prefix=RODATA
+; RUN: llc -mtriple=hexagon -O2 \
+; RUN:   -stop-after=hexagon-global-array-alignment < %s \
+; RUN:   | FileCheck %s --check-prefix=RODATA
+;
+; The pass can be disabled with -hexagon-disable-global-array-align.
+; RUN: llc -mtriple=hexagon -O3 -hexagon-disable-global-array-align \
+; RUN:   -stop-after=hexagon-global-array-alignment < %s \
+; RUN:   | FileCheck %s --check-prefix=DISABLED
+
+; Locally-defined integer arrays are promoted to at least 8 bytes.
+; CHECK: @int_array = dso_local global [4 x i32] zeroinitializer, align 8
+; CHECK: @char_array = dso_local global [8 x i8] zeroinitializer, align 8
+; CHECK: @short_array = dso_local global [4 x i16] zeroinitializer, align 8
+; CHECK: @multidim = dso_local global [3 x [5 x i32]] zeroinitializer, align 8
+; CHECK: @explicit = dso_local global [4 x i32] zeroinitializer, align 16
+; CHECK: @scalar = dso_local global i32 0, align 4
+
+; When reducing .rodata size, byte/half arrays at align <= 2 are left alone, but
+; word arrays are still promoted to 8 bytes.
+; RODATA: @int_array = dso_local global [4 x i32] zeroinitializer, align 8
+; RODATA: @char_array = dso_local global [8 x i8] zeroinitializer, align 1
+; RODATA: @short_array = dso_local global [4 x i16] zeroinitializer, align 2
+; RODATA: @multidim = dso_local global [3 x [5 x i32]] zeroinitializer, align 8
+; RODATA: @explicit = dso_local global [4 x i32] zeroinitializer, align 16
+; RODATA: @scalar = dso_local global i32 0, align 4
+
+; DISABLED: @int_array = dso_local global [4 x i32] zeroinitializer, align 4
+; DISABLED: @char_array = dso_local global [8 x i8] zeroinitializer, align 1
+; DISABLED: @short_array = dso_local global [4 x i16] zeroinitializer, align 2
+; DISABLED: @multidim = dso_local global [3 x [5 x i32]] zeroinitializer, align 4
+; DISABLED: @explicit = dso_local global [4 x i32] zeroinitializer, align 16
+; DISABLED: @scalar = dso_local global i32 0, align 4
+
+; The alignment of globals whose definition the linker may override or relocate
+; is left untouched (see GlobalObject::canIncreaseAlignment). These must keep
+; their original alignment for both the -O3 and -O2 runs.
+; CHECK: @extern_decl = external dso_local global [4 x i32], align 4
+; CHECK: @weak_arr = weak dso_local global [4 x i32] zeroinitializer, align 4
+; CHECK: @interposable = global [4 x i32] zeroinitializer, align 4
+; CHECK: @sectioned = dso_local global [4 x i32] zeroinitializer, section ".mysec", align 4
+; RODATA: @extern_decl = external dso_local global [4 x i32], align 4
+; RODATA: @weak_arr = weak dso_local global [4 x i32] zeroinitializer, align 4
+; RODATA: @interposable = global [4 x i32] zeroinitializer, align 4
+; RODATA: @sectioned = dso_local global [4 x i32] zeroinitializer, section ".mysec", align 4
+
+ at int_array = dso_local global [4 x i32] zeroinitializer, align 4
+ at char_array = dso_local global [8 x i8] zeroinitializer, align 1
+ at short_array = dso_local global [4 x i16] zeroinitializer, align 2
+ at multidim = dso_local global [3 x [5 x i32]] zeroinitializer, align 4
+ at explicit = dso_local global [4 x i32] zeroinitializer, align 16
+ at scalar = dso_local global i32 0, align 4
+
+; A declaration: the definition (and its alignment) lives in another module.
+ at extern_decl = external dso_local global [4 x i32], align 4
+; A weak definition: the linker may choose a different definition.
+ at weak_arr = weak dso_local global [4 x i32] zeroinitializer, align 4
+; An interposable (non-dso_local) definition subject to COPY relocations.
+ at interposable = global [4 x i32] zeroinitializer, align 4
+; A section-pinned global: raising the alignment could introduce padding.
+ at sectioned = dso_local global [4 x i32] zeroinitializer, section ".mysec", align 4

>From b90a15304626f873b288bc29f8f08876dd88e198 Mon Sep 17 00:00:00 2001
From: Akash Agrawal <akashag at qti.qualcomm.com>
Date: Wed, 26 Aug 2026 23:11:20 -0700
Subject: [PATCH 2/2] address comment - covered the flag with the test case

---
 llvm/test/CodeGen/Hexagon/align-global-arrays.ll | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/llvm/test/CodeGen/Hexagon/align-global-arrays.ll b/llvm/test/CodeGen/Hexagon/align-global-arrays.ll
index d1f46c9715f4b..de4be5f7a7966 100644
--- a/llvm/test/CodeGen/Hexagon/align-global-arrays.ll
+++ b/llvm/test/CodeGen/Hexagon/align-global-arrays.ll
@@ -14,6 +14,11 @@
 ; RUN:   -stop-after=hexagon-global-array-alignment < %s \
 ; RUN:   | FileCheck %s --check-prefix=RODATA
 ;
+; With -hexagon-disable-align-opt-byte-half the .rodata size reduction is off,
+; so byte and half-word arrays are promoted to 8 bytes even at -O2, as at -O3.
+; RUN: llc -mtriple=hexagon -O2 -hexagon-disable-align-opt-byte-half \
+; RUN:   -stop-after=hexagon-global-array-alignment < %s | FileCheck %s
+;
 ; The pass can be disabled with -hexagon-disable-global-array-align.
 ; RUN: llc -mtriple=hexagon -O3 -hexagon-disable-global-array-align \
 ; RUN:   -stop-after=hexagon-global-array-alignment < %s \



More information about the llvm-commits mailing list