[llvm] [SPIR-V] Add pass to remove spv_ptrcast intrinsics (PR #128896)
Steven Perron via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 27 08:19:09 PST 2025
================
@@ -0,0 +1,265 @@
+//===-- SPIRVLegalizePointerLoad.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
+//
+//===----------------------------------------------------------------------===//
+//
+// The LLVM IR has multiple legal patterns we cannot lower to Logical SPIR-V.
+// This pass modifies such loads to have an IR we can directly lower to valid
+// logical SPIR-V.
+// OpenCL can avoid this because they rely on ptrcast, which is not supported
+// by logical SPIR-V.
+//
+// This pass relies on the assign_ptr_type intrinsic to deduce the type of the
+// pointed values, must replace all occurences of `ptrcast`. This is why
+// unhandled cases are reported as unreachable: we MUST cover all cases.
+//
+// 1. Loading the first element of an array
+//
+// %array = [10 x i32]
+// %value = load i32, ptr %array
+//
+// LLVM can skip the GEP instruction, and only request loading the first 4
+// bytes. In logical SPIR-V, we need an OpAccessChain to access the first
+// element. This pass will add a getelementptr instruction before the load.
+//
+//
+// 2. Implicit downcast from load
+//
+// %1 = getelementptr <4 x i32>, ptr %vec4, i64 0
+// %2 = load <3 x i32>, ptr %1
+//
+// The pointer in the GEP instruction is only used for offset computations,
+// but it doesn't NEED to match the pointed type. OpAccessChain however
+// requires this. Also, LLVM loads define the bitwidth of the load, not the
+// pointer. In this example, we can guess %vec4 is a vec4 thanks to the GEP
+// instruction basetype, but we only want to load the first 3 elements, hence
+// do a partial load. In logical SPIR-V, this is not legal. What we must do
+// is load the full vector (basetype), extract 3 elements, and recombine them
+// to form a 3-element vector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRV.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "llvm/CodeGen/IntrinsicLowering.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+#include "llvm/Transforms/Utils/Cloning.h"
+#include "llvm/Transforms/Utils/LowerMemIntrinsics.h"
+
+using namespace llvm;
+
+namespace llvm {
+void initializeSPIRVLegalizePointerLoadPass(PassRegistry &);
+}
+
+class SPIRVLegalizePointerLoad : public FunctionPass {
+
+ // Replace all uses of a |Old| with |New| updates the global registry type
+ // mappings.
+ void replaceAllUsesWith(Value *Old, Value *New) {
+ Old->replaceAllUsesWith(New);
+ GR->updateIfExistDeducedElementType(Old, New, /* deleteOld = */ true);
+ GR->updateIfExistAssignPtrTypeInstr(Old, New, /* deleteOld = */ true);
+ }
+
+ // Builds the `spv_assign_type` assigning |Ty| to |Value| at the current
+ // builder position.
+ void buildAssignType(IRBuilder<> &B, Type *Ty, Value *Arg) {
+ Value *OfType = PoisonValue::get(Ty);
+ CallInst *AssignCI = buildIntrWithMD(Intrinsic::spv_assign_type,
+ {Arg->getType()}, OfType, Arg, {}, B);
+ GR->addAssignPtrTypeInstr(Arg, AssignCI);
+ }
+
+ // Loads a single scalar of type |To| from the vector pointed by |Source| of
+ // the type |From|. Returns the loaded value.
+ Value *loadScalarFromVector(IRBuilder<> &B, Value *Source,
+ FixedVectorType *From) {
+
+ LoadInst *NewLoad = B.CreateLoad(From, Source);
+ buildAssignType(B, From, NewLoad);
+
+ SmallVector<Value *, 2> Args = {NewLoad, B.getInt64(0)};
+ SmallVector<Type *, 3> Types = {From->getElementType(), Args[0]->getType(),
+ Args[1]->getType()};
+ Value *Extracted =
+ B.CreateIntrinsic(Intrinsic::spv_extractelt, {Types}, {Args});
+ buildAssignType(B, Extracted->getType(), Extracted);
+ return Extracted;
+ }
+
+ // Loads parts of the vector of type |From| from the pointer |Source| and
+ // create a new vector of type |To|. |To| must be a vector type, and element
+ // types of |To| and |From| must match. Returns the loaded value.
+ Value *loadVectorFromVector(IRBuilder<> &B, FixedVectorType *From,
+ FixedVectorType *To, Value *Source) {
----------------
s-perron wrote:
The variable names could use more info. The different between `From` and `Source` is not immediately obvious. I have to go back and look at the defintions to see one is a type, and the other a value.
```suggestion
Value *loadVectorFromVector(IRBuilder<> &B, FixedVectorType *SourceType,
FixedVectorType *TargetType, Value *Source) {
```
https://github.com/llvm/llvm-project/pull/128896
More information about the llvm-commits
mailing list