[llvm] [SPIR-V] Add pass to remove spv_ptrcast intrinsics (PR #128896)

Vyacheslav Levytskyy via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 27 03:29:38 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) {
----------------
VyacheslavLevytskyy wrote:

The same thought here, it looks like keeping all type inference related staff in one place (currently it's going to be GlobalRegistry) instead of dispersing over different modules may help in future.

I've been thinking about middle to long-term plans to reorganize type inference to a separate pass, splitting emit-intrinsics, and making type inference slightly more universal, so this is probably not the last anticipated refactoring and having that in mind a bigger picture is that we should somehow try factoring out and grouping together type inference related functions.

https://github.com/llvm/llvm-project/pull/128896


More information about the llvm-commits mailing list