[llvm] [SPIR-V] Add pass to fixup global variable AS (PR #124591)
Michal Paszkowski via llvm-commits
llvm-commits at lists.llvm.org
Fri Jan 31 03:20:03 PST 2025
================
@@ -0,0 +1,593 @@
+//===-- SPIRVFixAddressSpace.cpp ----------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+// This pass is Vulkan specific as Logical SPIR-V doesn't support pointer
+// cast.
+//
+// In LLVM IR, global and local variables are by default in the same address
+// space. In SPIR-V, global and local variables live in a different address
+// space (storage class in the SPIR-V parlance).
+//
+// This means the following function cannot be lowered to SPIR-V:
+// static int global = 0;
+// int& return_ref(int& ref, bool select) {
+// return select ? ref : global;
+// }
+//
+// A solution is to force inline, but this would prevent us from emitting SPIR-V
+// libraries. Another solution is to move all globals to local variables, but
+// this also blocks libraries. The last solution is to replace all local
+// variables with global variables. This is possible because Vulkan SPIR-V
+// completely forbids static recursion.
+//
+// This pass replace all alloca instruction with a new global variable.
+// In addition, it moves all such allocations into the `Private` address space.
+//
+// After this pass, no variable or pointer should reference the default address
+// space.
+//
+// Note:
+// LLVM IR has address spaces for functions, but SPIR-V doesn't. In addition,
+// Vulkan disallow function pointers and indirect jump, meaning we could never
+// have a pointer storing the function address. For this reason, functions are
+// left in the default address space, but all pointer operands to the default
+// AS are rewritten to point to the AS `Private`. This kind of blind rewrite
+// simplifies the code, but can only work with those assumptions.
+//===----------------------------------------------------------------------===//
+
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/CFG.h"
+#include "llvm/IR/Dominators.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/InstVisitor.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/NoFolder.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/PassRegistry.h"
+#include "llvm/Transforms/Utils.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LoopSimplify.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+#include <queue>
+#include <stack>
+#include <type_traits>
+#include <unordered_set>
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace llvm {
+void initializeSPIRVFixAddressSpacePass(PassRegistry &);
+} // namespace llvm
+
+namespace {
+
+constexpr unsigned GlobalAddressSpace =
+ storageClassToAddressSpace(SPIRV::StorageClass::Private);
+
+// Returns true if the given type or any subtype contains a pointer to the
+// default address space.
+bool typeRequiresConversion(Type *T) {
+ if (T->isPointerTy())
+ return T->getPointerAddressSpace() == 0;
+
+ if (T->isArrayTy())
+ return typeRequiresConversion(T->getArrayElementType());
+ if (T->isStructTy()) {
+ for (unsigned I = 0; I < T->getStructNumElements(); ++I)
+ if (typeRequiresConversion(T->getStructElementType(I)))
+ return true;
+ return false;
+ }
+ if (FunctionType *FT = dyn_cast<FunctionType>(T)) {
+ if (typeRequiresConversion(FT->getReturnType()))
+ return true;
+ for (Type *TP : FT->params())
+ if (typeRequiresConversion(TP))
+ return true;
+ return false;
+ }
+
+ return false;
+}
+
+class SPIRVFixAddressSpace : public InstVisitor<SPIRVFixAddressSpace>,
+ public ModulePass {
+
+ // Types are supposed to be unique. When using Type::get, there is a lookup to
+ // only create types on demand. StructType::get only allows creating literal
+ // structs, meaning we would lose the type name. This forces us to use
+ // StructType::create, which doesn't deduplicates. This means we must bring
+ // our own old-type/new-type map to prevent creating distinct types when we
+ // shouldn't.
+ std::unordered_map<Type *, Type *> ConvertedTypes;
----------------
michalpaszkowski wrote:
You could replace `std::unordered_map` with `llvm::DenseMap` especially for pointer to pointer lookups. I think using `std::unordered_map` [is generally discouraged](https://llvm.org/docs/ProgrammersManual.html#other-map-like-container-options).
https://github.com/llvm/llvm-project/pull/124591
More information about the llvm-commits
mailing list