[llvm] [LLVM] Add support for printing and parsing symbolic address spaces (PR #169422)
Rahul Joshi via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 1 10:50:46 PST 2025
https://github.com/jurahul updated https://github.com/llvm/llvm-project/pull/169422
>From 115a821c0a82a483afa9be33202447a0fa8fcb2a Mon Sep 17 00:00:00 2001
From: Rahul Joshi <rjoshi at nvidia.com>
Date: Mon, 24 Nov 2025 15:25:42 -0800
Subject: [PATCH] [LLVM] Add support for printing and parsing symbolic address
spaces
---
.../llvm/TargetParser/NVPTXAddressSpaces.h | 33 ++++++++++
llvm/include/llvm/TargetParser/Triple.h | 3 +
llvm/lib/AsmParser/LLParser.cpp | 3 +
llvm/lib/IR/AsmWriter.cpp | 62 +++++++++++--------
llvm/lib/Target/NVPTX/NVPTX.h | 15 +----
llvm/lib/TargetParser/Triple.cpp | 51 +++++++++++++++
.../Assembler/symbolic-addrspace-nvptx.ll | 54 ++++++++++++++++
7 files changed, 181 insertions(+), 40 deletions(-)
create mode 100644 llvm/include/llvm/TargetParser/NVPTXAddressSpaces.h
create mode 100644 llvm/test/Assembler/symbolic-addrspace-nvptx.ll
diff --git a/llvm/include/llvm/TargetParser/NVPTXAddressSpaces.h b/llvm/include/llvm/TargetParser/NVPTXAddressSpaces.h
new file mode 100644
index 0000000000000..163abb84a9543
--- /dev/null
+++ b/llvm/include/llvm/TargetParser/NVPTXAddressSpaces.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Address space definitions for NVPTX target.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TARGETPARSER_NVPTX_ADDRESS_SPACES_H
+#define LLVM_TARGETPARSER_NVPTX_ADDRESS_SPACES_H
+
+namespace llvm::NVPTX {
+
+using AddressSpaceUnderlyingType = unsigned int;
+enum AddressSpace : AddressSpaceUnderlyingType {
+ Generic = 0,
+ Global = 1,
+ Shared = 3,
+ Const = 4,
+ Local = 5,
+ SharedCluster = 7,
+
+ // NVPTX Backend Private:
+ Param = 101
+};
+
+} // namespace llvm::NVPTX
+
+#endif // LLVM_TARGETPARSER_NVPTX_ADDRESS_SPACES_H
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 11b76cd183108..9a747c32d08c9 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -1362,6 +1362,9 @@ class Triple {
/// Compute the LLVM IR data layout string based on the triple. Some targets
/// customize the layout based on the ABIName string.
LLVM_ABI std::string computeDataLayout(StringRef ABIName = "") const;
+
+ LLVM_ABI StringRef getAddressSpaceName(unsigned AS) const;
+ LLVM_ABI std::optional<unsigned> getAddressSpaceNumber(StringRef Name) const;
};
} // End llvm namespace
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index c3678d37607d5..350a4b858ee13 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1965,6 +1965,9 @@ bool LLParser::parseOptionalAddrSpace(unsigned &AddrSpace, unsigned DefaultAS) {
AddrSpace = M->getDataLayout().getDefaultGlobalsAddressSpace();
} else if (AddrSpaceStr == "P") {
AddrSpace = M->getDataLayout().getProgramAddressSpace();
+ } else if (std::optional<unsigned> AS =
+ M->getTargetTriple().getAddressSpaceNumber(AddrSpaceStr)) {
+ AddrSpace = *AS;
} else {
return tokError("invalid symbolic addrspace '" + AddrSpaceStr + "'");
}
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 7932765db8359..03b31f77f1d4e 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -543,7 +543,8 @@ namespace {
class TypePrinting {
public:
- TypePrinting(const Module *M = nullptr) : DeferredM(M) {}
+ TypePrinting(const Module *M = nullptr)
+ : M(M), TypesIncorporated(M == nullptr) {}
TypePrinting(const TypePrinting &) = delete;
TypePrinting &operator=(const TypePrinting &) = delete;
@@ -564,7 +565,8 @@ class TypePrinting {
void incorporateTypes();
/// A module to process lazily when needed. Set to nullptr as soon as used.
- const Module *DeferredM;
+ const Module *M;
+ bool TypesIncorporated;
TypeFinder NamedTypes;
@@ -605,11 +607,11 @@ bool TypePrinting::empty() {
}
void TypePrinting::incorporateTypes() {
- if (!DeferredM)
+ if (TypesIncorporated)
return;
- NamedTypes.run(*DeferredM, false);
- DeferredM = nullptr;
+ NamedTypes.run(*M, false);
+ TypesIncorporated = true;
// The list of struct types we got back includes all the struct types, split
// the unnamed ones out to a numbering and remove the anonymous structs.
@@ -630,6 +632,20 @@ void TypePrinting::incorporateTypes() {
NamedTypes.erase(NextToUse, NamedTypes.end());
}
+static void printAddressSpace(const Module *M, unsigned AS, raw_ostream &OS,
+ StringRef Prefix = " ", StringRef Suffix = "",
+ bool ForcePrint = false) {
+ if (AS == 0 && !ForcePrint)
+ return;
+ OS << Prefix << "addrspace(";
+ StringRef ASName = M ? M->getTargetTriple().getAddressSpaceName(AS) : "";
+ if (!ASName.empty())
+ OS << "\"" << ASName << "\"";
+ else
+ OS << AS;
+ OS << ")" << Suffix;
+}
+
/// Write the specified type to the specified raw_ostream, making use of type
/// names or up references to shorten the type name where possible.
void TypePrinting::print(Type *Ty, raw_ostream &OS) {
@@ -686,8 +702,7 @@ void TypePrinting::print(Type *Ty, raw_ostream &OS) {
case Type::PointerTyID: {
PointerType *PTy = cast<PointerType>(Ty);
OS << "ptr";
- if (unsigned AddressSpace = PTy->getAddressSpace())
- OS << " addrspace(" << AddressSpace << ')';
+ printAddressSpace(M, PTy->getAddressSpace(), OS);
return;
}
case Type::ArrayTyID: {
@@ -3896,10 +3911,9 @@ void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
printThreadLocalModel(GV->getThreadLocalMode(), Out);
StringRef UA = getUnnamedAddrEncoding(GV->getUnnamedAddr());
if (!UA.empty())
- Out << UA << ' ';
+ Out << UA << ' ';
- if (unsigned AddressSpace = GV->getType()->getAddressSpace())
- Out << "addrspace(" << AddressSpace << ") ";
+ printAddressSpace(GV->getParent(), GV->getType()->getAddressSpace(), Out, /*Prefix=*/"", /*Suffix=*/" ");
if (GV->isExternallyInitialized()) Out << "externally_initialized ";
Out << (GV->isConstant() ? "constant " : "global ");
TypePrinter.print(GV->getValueType(), Out);
@@ -4174,9 +4188,9 @@ void AssemblyWriter::printFunction(const Function *F) {
// a module with a non-zero program address space or if there is no valid
// Module* so that the file can be parsed without the datalayout string.
const Module *Mod = F->getParent();
- if (F->getAddressSpace() != 0 || !Mod ||
- Mod->getDataLayout().getProgramAddressSpace() != 0)
- Out << " addrspace(" << F->getAddressSpace() << ")";
+ bool ForcePrintAddressSpace =
+ !Mod || Mod->getDataLayout().getProgramAddressSpace() != 0;
+ printAddressSpace(Mod, F->getAddressSpace(), Out, /*Prefix=*/" ", /*Suffix=*/"", ForcePrintAddressSpace);
if (Attrs.hasFnAttrs())
Out << " #" << Machine.getAttributeGroupSlot(Attrs.getFnAttrs());
if (F->hasSection()) {
@@ -4358,17 +4372,13 @@ static void maybePrintCallAddrSpace(const Value *Operand, const Instruction *I,
return;
}
unsigned CallAddrSpace = Operand->getType()->getPointerAddressSpace();
- bool PrintAddrSpace = CallAddrSpace != 0;
- if (!PrintAddrSpace) {
- const Module *Mod = getModuleFromVal(I);
- // We also print it if it is zero but not equal to the program address space
- // or if we can't find a valid Module* to make it possible to parse
- // the resulting file even without a datalayout string.
- if (!Mod || Mod->getDataLayout().getProgramAddressSpace() != 0)
- PrintAddrSpace = true;
- }
- if (PrintAddrSpace)
- Out << " addrspace(" << CallAddrSpace << ")";
+ const Module *Mod = getModuleFromVal(I);
+ // We also print it if it is zero but not equal to the program address space
+ // or if we can't find a valid Module* to make it possible to parse
+ // the resulting file even without a datalayout string.
+ bool ForcePrintAddrSpace =
+ !Mod || Mod->getDataLayout().getProgramAddressSpace() != 0;
+ printAddressSpace(Mod, CallAddrSpace, Out, /*Prefix=*/" ", /*Suffix=*/"", ForcePrintAddrSpace);
}
// This member is called for each Instruction in a function..
@@ -4735,9 +4745,7 @@ void AssemblyWriter::printInstruction(const Instruction &I) {
Out << ", align " << A->value();
}
- unsigned AddrSpace = AI->getAddressSpace();
- if (AddrSpace != 0)
- Out << ", addrspace(" << AddrSpace << ')';
+ printAddressSpace(AI->getModule(), AI->getAddressSpace(), Out, /*Prefix=*/", ");
} else if (isa<CastInst>(I)) {
if (Operand) {
Out << ' ';
diff --git a/llvm/lib/Target/NVPTX/NVPTX.h b/llvm/lib/Target/NVPTX/NVPTX.h
index 95fd05f2a926f..1379bb8cd99c1 100644
--- a/llvm/lib/Target/NVPTX/NVPTX.h
+++ b/llvm/lib/Target/NVPTX/NVPTX.h
@@ -20,6 +20,8 @@
#include "llvm/Support/AtomicOrdering.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/NVPTXAddressSpaces.h"
+
namespace llvm {
class FunctionPass;
class MachineFunctionPass;
@@ -176,19 +178,6 @@ enum Scope : ScopeUnderlyingType {
LASTSCOPE = DefaultDevice
};
-using AddressSpaceUnderlyingType = unsigned int;
-enum AddressSpace : AddressSpaceUnderlyingType {
- Generic = 0,
- Global = 1,
- Shared = 3,
- Const = 4,
- Local = 5,
- SharedCluster = 7,
-
- // NVPTX Backend Private:
- Param = 101
-};
-
namespace PTXLdStInstCode {
enum FromType { Unsigned = 0, Signed, Float, Untyped };
} // namespace PTXLdStInstCode
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 11ba9ee32f66a..2df455b729b2a 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -17,6 +17,7 @@
#include "llvm/TargetParser/ARMTargetParser.h"
#include "llvm/TargetParser/ARMTargetParserCommon.h"
#include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/NVPTXAddressSpaces.h"
#include <cassert>
#include <cstring>
using namespace llvm;
@@ -2361,6 +2362,56 @@ ExceptionHandling Triple::getDefaultExceptionHandling() const {
return ExceptionHandling::None;
}
+static StringRef getNVPTXAddressSpaceName(unsigned AS) {
+ switch (AS) {
+ case NVPTX::Generic:
+ return "generic";
+ case NVPTX::Global:
+ return "global";
+ case NVPTX::Shared:
+ return "shared";
+ case NVPTX::Const:
+ return "const";
+ case NVPTX::Local:
+ return "local";
+ case NVPTX::SharedCluster:
+ return "shared_cluster";
+ default:
+ return "";
+ }
+}
+
+static std::optional<unsigned> getNVPTXAddressSpaceNumber(StringRef Name) {
+ return StringSwitch<std::optional<unsigned>>(Name)
+ .Case("generic", NVPTX::Generic)
+ .Case("global", NVPTX::Global)
+ .Case("shared", NVPTX::Shared)
+ .Case("const", NVPTX::Const)
+ .Case("local", NVPTX::Local)
+ .Case("shared_cluster", NVPTX::SharedCluster)
+ .Default(std::nullopt);
+}
+
+StringRef Triple::getAddressSpaceName(unsigned AS) const {
+ switch (getArch()) {
+ case Triple::nvptx:
+ case Triple::nvptx64:
+ return getNVPTXAddressSpaceName(AS);
+ default:
+ return "";
+ }
+}
+
+std::optional<unsigned> Triple::getAddressSpaceNumber(StringRef Name) const {
+ switch (getArch()) {
+ case Triple::nvptx:
+ case Triple::nvptx64:
+ return getNVPTXAddressSpaceNumber(Name);
+ default:
+ return std::nullopt;
+ }
+}
+
// HLSL triple environment orders are relied on in the front end
static_assert(Triple::Vertex - Triple::Pixel == 1,
"incorrect HLSL stage order");
diff --git a/llvm/test/Assembler/symbolic-addrspace-nvptx.ll b/llvm/test/Assembler/symbolic-addrspace-nvptx.ll
new file mode 100644
index 0000000000000..0d447c8cc658c
--- /dev/null
+++ b/llvm/test/Assembler/symbolic-addrspace-nvptx.ll
@@ -0,0 +1,54 @@
+;; Check that we can parse/print symbolic addres space for NVPTX triple.
+; RUN: split-file %s %t --leading-lines
+; RUN: llvm-as < %t/num-to-sym.ll | llvm-dis | FileCheck %t/num-to-sym.ll
+; RUN: llvm-as < %t/sym-to-sym.ll | llvm-dis | FileCheck %t/sym-to-sym.ll
+; RUN: not llvm-as < %t/invalid-sym.ll 2>&1 | FileCheck %t/invalid-sym.ll
+
+;--- num-to-sym.ll
+target triple = "nvptx64-nvidia-cuda"
+
+; CHECK: @str0 = private addrspace("global") constant
+ at str0 = private addrspace(1) constant [4 x i8] c"str\00"
+
+; CHECK: @gvs = external addrspace("shared") global i32
+ at gvs = external addrspace(3) global i32
+
+; CHECK: @gv_unknown_as = external addrspace(100) global i32
+ at gv_unknown_as = external addrspace(100) global i32
+
+define void @foo() {
+ ; CHECK: %alloca = alloca i32, align 4, addrspace("local")
+ %alloca = alloca i32, addrspace(5)
+ ; CHECK: store i32 0, ptr addrspace("local") %alloca
+ store i32 0, ptr addrspace(5) %alloca
+ ; CHECK: store i32 3, ptr addrspace("shared") @gvs
+ store i32 3, ptr addrspace(3) @gvs
+ ret void
+}
+
+;--- sym-to-sym.ll
+target triple = "nvptx64-nvidia-cuda"
+
+; CHECK: @str0 = private addrspace("global") constant
+ at str0 = private addrspace("global") constant [4 x i8] c"str\00"
+
+; CHECK: @gvs = external addrspace("shared") global i32
+ at gvs = external addrspace("shared") global i32
+
+; CHECK: @gv_unknown_as = external addrspace(100) global i32
+ at gv_unknown_as = external addrspace(100) global i32
+
+define void @foo() {
+ ; CHECK: %alloca = alloca i32, align 4, addrspace("local")
+ %alloca = alloca i32, addrspace("local")
+ ; CHECK: store i32 0, ptr addrspace("local") %alloca
+ store i32 0, ptr addrspace("local") %alloca
+ ; CHECK: store i32 3, ptr addrspace("shared") @gvs
+ store i32 3, ptr addrspace("shared") @gvs
+ ret void
+}
+
+;--- invalid-sym.ll
+target triple = "nvptx64-nvidia-cuda"
+; CHECK: error: invalid symbolic addrspace 'ram'
+ at str0 = private addrspace("ram") constant [4 x i8] c"str\00"
More information about the llvm-commits
mailing list