https://github.com/skc7 updated https://github.com/llvm/llvm-project/pull/220197

>From 0bc4aded0fe8ddac78acd15a9657d14dbdfb9be5 Mon Sep 17 00:00:00 2001
From: skc7 <[email protected]>
Date: Tue, 1 Sep 2026 14:31:15 +0530
Subject: [PATCH] [CIR] Wire AMDGPU into the call-convention lowering pass

---
 clang/include/clang/CIR/Dialect/Passes.h      |  2 +-
 clang/include/clang/CIR/Dialect/Passes.td     | 14 ++--
 .../Dialect/Transforms/CIRABIClassifier.cpp   | 21 ++++--
 .../CIR/Dialect/Transforms/CIRABIClassifier.h | 15 +++-
 .../Transforms/CIRABIClassifierAMDGPU.cpp     | 61 +++++++++++++++
 .../lib/CIR/Dialect/Transforms/CMakeLists.txt |  1 +
 .../Transforms/CallConvLoweringPass.cpp       | 74 ++++++++++++++-----
 .../TargetLowering/CIRABIRewriteContext.cpp   | 37 +++++++++-
 .../abi-lowering/amdgpu-calling-conv.cir      | 59 +++++++++++++++
 .../abi-lowering/amdgpu-indirect-nyi.cir      | 37 ++++++++++
 .../abi-lowering/amdgpu-kernel-hip-ptr.cir    | 53 +++++++++++++
 11 files changed, 338 insertions(+), 36 deletions(-)
 create mode 100644 clang/lib/CIR/Dialect/Transforms/CIRABIClassifierAMDGPU.cpp
 create mode 100644 
clang/test/CIR/Transforms/abi-lowering/amdgpu-calling-conv.cir
 create mode 100644 
clang/test/CIR/Transforms/abi-lowering/amdgpu-indirect-nyi.cir
 create mode 100644 
clang/test/CIR/Transforms/abi-lowering/amdgpu-kernel-hip-ptr.cir

diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index dedfc9fa351796..117822486cc3a2 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -20,7 +20,7 @@ namespace cir {
 /// The ABI target whose calling-convention rules drive CallConvLowering.
 /// None is the unset state used when the pass runs in classification-attr
 /// mode instead of selecting a target.
-enum class CallConvTarget { None, Test, X86_64 };
+enum class CallConvTarget { None, Test, X86_64, AMDGPU };
 } // namespace cir
 
 namespace mlir {
diff --git a/clang/include/clang/CIR/Dialect/Passes.td 
b/clang/include/clang/CIR/Dialect/Passes.td
index 9b260af789384c..a5335ab3606dd6 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -225,10 +225,12 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", 
"mlir::ModuleOp"> {
 
     Two driver modes select how each function's classification is computed:
 
-    - `target=<name>` selects an ABI target.  Currently only `"test"` (the
-      MLIR test target in `mlir/lib/ABI/Targets/Test/`) is supported.  Real
-      targets (x86_64, AArch64, ...) will be added once the LLVM ABI library
-      ships them.
+    - `target=<name>` selects an ABI target.  The clang pipeline picks the
+      target from the module triple.  Supported targets:
+      - `"x86_64"`: System V.
+      - `"amdgpu"`: AMDGPU.
+      - `"test"`: the MLIR test target in `mlir/lib/ABI/Targets/Test/`.
+      Other targets will be added as the LLVM ABI library ships them.
     - `classification-attr=<name>` reads a `DictionaryAttr` named `<name>`
       from each `cir.func` and parses it via the test-target injection
       helper.  Used by tests to inject arbitrary classifications without
@@ -246,7 +248,9 @@ def CallConvLowering : Pass<"cir-call-conv-lowering", 
"mlir::ModuleOp"> {
              clEnumValN(cir::CallConvTarget::Test, "test",
                         "MLIR test ABI target"),
              clEnumValN(cir::CallConvTarget::X86_64, "x86_64",
-                        "x86_64 System V")
+                        "x86_64 System V"),
+             clEnumValN(cir::CallConvTarget::AMDGPU, "amdgpu",
+                        "AMDGPU")
            )}]>,
     Option<"classificationAttr", "classification-attr", "std::string",
            /*default=*/"\"\"",
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.cpp 
b/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.cpp
index bae1bafd5580c8..c58bff7e3d9cbb 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.cpp
@@ -22,11 +22,20 @@ using namespace mlir;
 using namespace mlir::abi;
 using namespace cir;
 
+/// FIXME: Only the AMDGPU kernel convention is mapped. Every other convention
+/// is classified as C.
+llvm::CallingConv::ID cir::convertCallingConv(cir::CallingConv cc) {
+  if (cc == cir::CallingConv::AMDGPUKernel)
+    return llvm::CallingConv::AMDGPU_KERNEL;
+  return llvm::CallingConv::C;
+}
+
 std::optional<FunctionClassification>
 CIRABIClassifierCommon::classifyVariadicCall(cir::CIRCallOpInterface call,
                                              cir::FuncOp callee) {
   return classifyVariadicCall(call, callee.getFunctionType(),
-                              getTargetInfo(callee));
+                              getTargetInfo(callee),
+                              convertCallingConv(callee.getCallingConv()));
 }
 
 std::optional<FunctionClassification>
@@ -34,11 +43,13 @@ 
CIRABIClassifierCommon::classifyIndirectCall(cir::CIRCallOpInterface call) {
   cir::FuncType funcTy = getIndirectCalleeType(call);
   const llvm::abi::TargetInfo &targetInfo =
       getTargetInfo(call->getParentOfType<cir::FuncOp>());
+  // An indirect callee is an ordinary function pointer, so it uses the C
+  // convention.
   if (call.getNumArgOperands() > funcTy.getNumInputs())
-    return classifyVariadicCall(call, funcTy, targetInfo);
-  return classifySignature(funcTy.getReturnType(), funcTy.getInputs(),
-                           requiredArgs(funcTy), targetInfo,
-                           [&]() { return call->emitOpError(); });
+    return classifyVariadicCall(call, funcTy, targetInfo, 
llvm::CallingConv::C);
+  return classifySignature(
+      funcTy.getReturnType(), funcTy.getInputs(), requiredArgs(funcTy),
+      targetInfo, llvm::CallingConv::C, [&]() { return call->emitOpError(); });
 }
 
 namespace {
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.h 
b/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.h
index d1799eccb37be5..36a4bee8390fa2 100644
--- a/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.h
+++ b/clang/lib/CIR/Dialect/Transforms/CIRABIClassifier.h
@@ -29,6 +29,7 @@
 #include "llvm/ABI/TargetInfo.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/IR/CallingConv.h"
 
 #include <memory>
 #include <optional>
@@ -135,18 +136,20 @@ class CIRABIClassifierCommon : public CIRABIClassifier {
 
 private:
   /// Classify a signature returning \p retCIR and taking \p inputs, whose
-  /// first \p required entries are declared parameters.
+  /// first \p required entries are declared parameters, under \p callConv.
   std::optional<mlir::abi::FunctionClassification>
   classifySignature(mlir::Type retCIR, mlir::TypeRange inputs,
                     llvm::abi::RequiredArgs required,
                     const llvm::abi::TargetInfo &targetInfo,
+                    llvm::CallingConv::ID callConv,
                     llvm::function_ref<mlir::InFlightDiagnostic()> emitError);
 
   /// Classify \p call, which passes more operands than \p calleeTy declares,
   /// from the call's operands.
   std::optional<mlir::abi::FunctionClassification>
   classifyVariadicCall(cir::CIRCallOpInterface call, cir::FuncType calleeTy,
-                       const llvm::abi::TargetInfo &targetInfo);
+                       const llvm::abi::TargetInfo &targetInfo,
+                       llvm::CallingConv::ID callConv);
 };
 
 /// The MLIR test ABI target's classifier, whose predictable rules approximate
@@ -165,6 +168,14 @@ std::unique_ptr<CIRABIClassifier>
 createX86_64ABIClassifier(const mlir::DataLayout &dl, mlir::ModuleOp modOp,
                           const ABIClassifierOptions &options);
 
+/// The AMDGPU classifier.
+std::unique_ptr<CIRABIClassifier>
+createAMDGPUABIClassifier(const mlir::DataLayout &dl, mlir::ModuleOp modOp);
+
+/// Map a CIR calling convention to the llvm::CallingConv the ABI classifier
+/// uses.  Conventions the classifier does not distinguish fall back to C.
+llvm::CallingConv::ID convertCallingConv(cir::CallingConv cc);
+
 
//===----------------------------------------------------------------------===//
 // Classification helpers defined in CallConvLoweringPass.cpp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/CIRABIClassifierAMDGPU.cpp 
b/clang/lib/CIR/Dialect/Transforms/CIRABIClassifierAMDGPU.cpp
new file mode 100644
index 00000000000000..b7683a81fa6a55
--- /dev/null
+++ b/clang/lib/CIR/Dialect/Transforms/CIRABIClassifierAMDGPU.cpp
@@ -0,0 +1,61 @@
+//===- CIRABIClassifierAMDGPU.cpp - AMDGPU classification 
-----------------===//
+//
+// 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 file implements the AMDGPU classifier, which classifies with the LLVM
+// ABI Lowering Library's AMDGPU target.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRABIClassifier.h"
+
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+
+using namespace mlir;
+using namespace mlir::abi;
+using namespace cir;
+
+namespace {
+
+class AMDGPUABIClassifier final : public CIRABIClassifierCommon {
+public:
+  AMDGPUABIClassifier(const mlir::DataLayout &dl, mlir::ModuleOp modOp)
+      : CIRABIClassifierCommon(dl, modOp) {}
+
+  std::optional<ArgClassification> classifyVAArg(cir::VAArgOp vaArg) override {
+    vaArg->emitOpError()
+        << "not yet implemented in CallConvLowering for AMDGPU";
+    return std::nullopt;
+  }
+
+protected:
+  /// AMDGPU has no per-function feature that changes its ABI rules.
+  const llvm::abi::TargetInfo &getTargetInfo(cir::FuncOp) override;
+
+private:
+  std::unique_ptr<llvm::abi::TargetInfo> target;
+};
+
+const llvm::abi::TargetInfo &AMDGPUABIClassifier::getTargetInfo(cir::FuncOp) {
+  if (!target) {
+    // HIP passes a generic pointer kernel argument as a global one.
+    auto langOpts = modOp->getAttrOfType<cir::LoweringLangOptionsAttr>(
+        cir::CIRDialect::getLoweringLangOptionsAttrName());
+    target = llvm::abi::createAMDGPUTargetInfo(
+        typeMapper.getTypeBuilder(),
+        /*CoerceGenericPtrArgToGlobal=*/langOpts && langOpts.getHip());
+  }
+  return *target;
+}
+
+} // namespace
+
+std::unique_ptr<CIRABIClassifier>
+cir::createAMDGPUABIClassifier(const mlir::DataLayout &dl,
+                               mlir::ModuleOp modOp) {
+  return std::make_unique<AMDGPUABIClassifier>(dl, modOp);
+}
diff --git a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt 
b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
index 47c4f180196d32..42fceb0590efda 100644
--- a/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/Transforms/CMakeLists.txt
@@ -16,6 +16,7 @@ add_clang_library(MLIRCIRTransforms
   LibOpt.cpp
   RecordTypeConverter.cpp
   CIRABIClassifier.cpp
+  CIRABIClassifierAMDGPU.cpp
   CIRABIClassifierX86_64.cpp
 
   DEPENDS
diff --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index e0ae978b0db8f4..a978d3a899c2a8 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -308,8 +308,12 @@ static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, 
MLIRContext *ctx) {
       .Case([&](const llvm::abi::FloatType *fltTy) {
         return cir::getFloatingPointType(*fltTy->getSemantics(), ctx);
       })
-      .Case([&](const llvm::abi::PointerType *) {
-        return cir::PointerType::get(cir::VoidType::get(ctx));
+      .Case([&](const llvm::abi::PointerType *ptrTy) {
+        auto voidTy = cir::VoidType::get(ctx);
+        if (unsigned as = ptrTy->getAddrSpace())
+          return cir::PointerType::get(
+              voidTy, cir::TargetAddressSpaceAttr::get(ctx, as));
+        return cir::PointerType::get(voidTy);
       })
       .Case([&](const llvm::abi::VectorType *vecTy) -> mlir::Type {
         mlir::Type elemCIR = abiTypeToCIR(vecTy->getElementType(), ctx);
@@ -554,11 +558,12 @@ static const llvm::abi::Type *mapCIRType(mlir::Type type,
 /// CIRABIRewriteContext.
 ///
 /// Direct: the value passes in register(s).  A coercion is forwarded in the
-/// four cases where the value has to be rebuilt on the wire: an aggregate
-/// unpacked into the register(s) holding it, a scalar too wide for one 
register
-/// split into a tuple of them, a scalar the classifier widens to fill its
-/// eightbyte, and a value whose live bytes start partway into its storage
-/// because a leading eightbyte holds no field (getDirectOffset).  canFlatten
+/// cases where the value has to be rebuilt on the wire: an aggregate unpacked
+/// into the register(s) holding it, a scalar too wide for one register split
+/// into a tuple of them, a scalar the classifier widens to fill its
+/// eightbyte, a pointer moved into another address space, and a value whose
+/// live bytes start partway into its storage because a leading eightbyte
+/// holds no field (getDirectOffset).  canFlatten
 /// follows the classifier's CanBeFlattened, so the rewriter splits a
 /// multi-field coerced struct into individual wire arguments unless the
 /// classifier asked to keep it intact.  Any other scalar passes in its
@@ -595,6 +600,26 @@ convertABIArgInfo(const llvm::abi::ArgInfo &info, 
MLIRContext *ctx,
     // than emit a read of the wrong bytes.
     if (offset && coerceIsRegisterTuple)
       return std::nullopt;
+    // A pointer moved to another address space keeps its pointee, which
+    // abiTypeToCIR cannot recover.
+    auto origPtr = dyn_cast_if_present<cir::PointerType>(origTy);
+    const auto *coercePtr =
+        dyn_cast_if_present<llvm::abi::PointerType>(coerceAbi);
+    if (origPtr && coercePtr && !offset) {
+      // Compare numbers, since an explicit address_space(0) keeps a
+      // target_address_space(0) attribute that a plain pointer lacks.
+      auto origAS = dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
+          origPtr.getAddrSpace());
+      if (coercePtr->getAddrSpace() != (origAS ? origAS.getValue() : 0)) {
+        auto coercedPtr = cast<cir::PointerType>(abiTypeToCIR(coerceAbi, ctx));
+        ArgClassification classified = ArgClassification::getDirect(
+            cir::PointerType::get(origPtr.getPointee(),
+                                  coercedPtr.getAddrSpace()),
+            /*offset=*/0);
+        classified.canFlatten = info.getCanBeFlattened();
+        return classified;
+      }
+    }
     // Compare widths rather than identity: a coerce no wider than the natural
     // type carries the same value and needs no rewrite.
     auto origInt = dyn_cast_if_present<cir::IntType>(origTy);
@@ -638,6 +663,10 @@ convertABIArgInfo(const llvm::abi::ArgInfo &info, 
MLIRContext *ctx,
     mlir::Type extendedTy = abiTypeToCIR(info.getCoerceToType(), ctx);
     return ArgClassification::getExtend(extendedTy, info.isSignExt());
   }
+  // ArgClassification has no address space for the indirect pointer yet.
+  if (info.isIndirectAliased() ||
+      (info.isIndirect() && info.getIndirectAddrSpace() != 0))
+    return std::nullopt;
   if (info.isIndirect())
     return ArgClassification::getIndirect(info.getIndirectAlign(),
                                           info.getIndirectByVal());
@@ -664,12 +693,13 @@ llvm::abi::RequiredArgs cir::requiredArgs(cir::FuncType 
fnTy) {
 /// indirect-call path (the latter classifies from the callee function
 /// pointer's pointee FuncType).  \p required marks where the declared
 /// parameters in \p inputs end.  The classifier treats every argument past 
that
-/// point as passed through an ellipsis.  Returns std::nullopt and emits an NYI
+/// point as passed through an ellipsis.  \p callConv is the calling convention
+/// the signature is classified under.  Returns std::nullopt and emits an NYI
 /// error via \p emitError if the signature uses a type the bridge does not
 /// handle yet.
 std::optional<FunctionClassification> 
CIRABIClassifierCommon::classifySignature(
     mlir::Type retCIR, mlir::TypeRange inputs, llvm::abi::RequiredArgs 
required,
-    const llvm::abi::TargetInfo &targetInfo,
+    const llvm::abi::TargetInfo &targetInfo, llvm::CallingConv::ID callConv,
     llvm::function_ref<mlir::InFlightDiagnostic()> emitError) {
   assert(retCIR && "signature return type must be non-null");
   assert((!required.allowsOptionalArgs() ||
@@ -698,8 +728,8 @@ std::optional<FunctionClassification> 
CIRABIClassifierCommon::classifySignature(
   for (mlir::Type a : inputs)
     argAbi.push_back(mapCIRType(a, typeMapper, dl, modOp));
 
-  std::unique_ptr<llvm::abi::FunctionInfo> fi = 
llvm::abi::FunctionInfo::create(
-      llvm::CallingConv::C, retAbi, argAbi, required);
+  std::unique_ptr<llvm::abi::FunctionInfo> fi =
+      llvm::abi::FunctionInfo::create(callConv, retAbi, argAbi, required);
   targetInfo.computeInfo(*fi);
 
   // convertABIArgInfo returns nullopt when the classifier picks a coercion 
this
@@ -769,6 +799,7 @@ CIRABIClassifierCommon::classifyFunction(cir::FuncOp func) {
   cir::FuncType fnTy = func.getFunctionType();
   return classifySignature(fnTy.getReturnType(), fnTy.getInputs(),
                            requiredArgs(fnTy), getTargetInfo(func),
+                           convertCallingConv(func.getCallingConv()),
                            [&]() { return func.emitOpError(); });
 }
 
@@ -779,9 +810,9 @@ std::optional<ArgClassification> 
CIRABIClassifierCommon::classifyVarArgType(
     mlir::Type ty, const llvm::abi::TargetInfo &targetInfo,
     llvm::function_ref<mlir::InFlightDiagnostic()> emitError) {
   MLIRContext *ctx = modOp->getContext();
-  std::optional<FunctionClassification> fc =
-      classifySignature(cir::VoidType::get(ctx), mlir::TypeRange(ty),
-                        llvm::abi::RequiredArgs(0), targetInfo, emitError);
+  std::optional<FunctionClassification> fc = classifySignature(
+      cir::VoidType::get(ctx), mlir::TypeRange(ty), llvm::abi::RequiredArgs(0),
+      targetInfo, llvm::CallingConv::C, emitError);
   if (!fc)
     return std::nullopt;
   return fc->argInfos[0];
@@ -797,13 +828,14 @@ std::optional<ArgClassification> 
CIRABIClassifierCommon::classifyVarArgType(
 std::optional<FunctionClassification>
 CIRABIClassifierCommon::classifyVariadicCall(
     cir::CIRCallOpInterface call, cir::FuncType calleeTy,
-    const llvm::abi::TargetInfo &targetInfo) {
+    const llvm::abi::TargetInfo &targetInfo, llvm::CallingConv::ID callConv) {
   assert(calleeTy.isVarArg() &&
          "only a variadic callee can take more operands than it declares");
   Operation *op = call.getOperation();
-  return classifySignature(
-      calleeTy.getReturnType(), call.getArgOperands().getTypes(),
-      requiredArgs(calleeTy), targetInfo, [&]() { return op->emitOpError(); });
+  return classifySignature(calleeTy.getReturnType(),
+                           call.getArgOperands().getTypes(),
+                           requiredArgs(calleeTy), targetInfo, callConv,
+                           [&]() { return op->emitOpError(); });
 }
 
 /// Whether \p fc gives the callee access to memory through a pointer the ABI
@@ -905,8 +937,8 @@ cir::classifyFunction(cir::FuncOp func, const DataLayout 
&dl,
                               returnType);
   }
 
-  // The x86_64 target classifies through CIRABIClassifierCommon, so only the
-  // test target reaches here.
+  // The x86_64 and AMDGPU targets classify through CIRABIClassifierCommon, so
+  // only the test target reaches here.
   assert(target == cir::CallConvTarget::Test &&
          "classifyFunction only handles the test target");
   return withReturnVoidness(mlir::abi::test::classify(argTypes, returnType, 
dl),
@@ -928,6 +960,8 @@ createABIClassifier(cir::CallConvTarget target,
     return createTestTargetABIClassifier(dl);
   case cir::CallConvTarget::X86_64:
     return createX86_64ABIClassifier(dl, modOp, options);
+  case cir::CallConvTarget::AMDGPU:
+    return createAMDGPUABIClassifier(dl, modOp);
   case cir::CallConvTarget::None:
     break;
   }
diff --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp
index c7ca6151029eb4..46e06b814776f3 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/CIRABIRewriteContext.cpp
@@ -484,14 +484,39 @@ mlir::Value emitCoercionToMemory(mlir::OpBuilder 
&builder, mlir::Location loc,
                           dstTy == slotTy ? 0 : offset);
 }
 
+/// Cast \p src to \p dstTy when both are pointers to the same pointee, so only
+/// the address space changes.  Returns null when a real coercion is needed.
+static mlir::Value
+emitAddrSpaceCoercion(mlir::OpBuilder &builder, mlir::Location loc,
+                      mlir::Type dstTy, mlir::Value src,
+                      SmallPtrSetImpl<mlir::Operation *> &createdOps,
+                      unsigned offset) {
+  auto srcPtrTy = dyn_cast<cir::PointerType>(src.getType());
+  auto dstPtrTy = dyn_cast<cir::PointerType>(dstTy);
+  if (offset || !srcPtrTy || !dstPtrTy ||
+      srcPtrTy.getPointee() != dstPtrTy.getPointee())
+    return {};
+  assert(srcPtrTy.getAddrSpace() != dstPtrTy.getAddrSpace() &&
+         "pointers to the same pointee must differ in address space");
+  auto cast = cir::CastOp::create(builder, loc, dstTy,
+                                  cir::CastKind::address_space, src);
+  createdOps.insert(cast);
+  return cast;
+}
+
 /// Coerce \p src to type \p dstTy by going through memory and load the whole
 /// coerced value back out.  Builds on emitCoercionToMemory, adding the final
-/// load of the destination-typed view.
+/// load of the destination-typed view.  A pointer that only changes address
+/// space is cast instead.
 mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
                          mlir::Type dstTy, mlir::Value src,
                          mlir::Block *slotBlock, const mlir::DataLayout &dl,
                          SmallPtrSetImpl<mlir::Operation *> &createdOps,
                          unsigned offset) {
+  if (mlir::Value cast =
+          emitAddrSpaceCoercion(builder, loc, dstTy, src, createdOps, offset))
+    return cast;
+
   mlir::Value dstSlot = emitCoercionToMemory(builder, loc, dstTy, src,
                                              slotBlock, dl, createdOps, 
offset);
   auto load = cir::LoadOp::create(builder, loc, dstSlot);
@@ -794,14 +819,20 @@ replaceUsesFromSlot(mlir::OpBuilder &builder, 
mlir::Location loc,
 /// \p origTy through a coercion slot at the start of \p slotBlock, and point
 /// the uses of \p val outside \p createdOps at the result with
 /// replaceUsesFromSlot.  A direct \p offset lands on the coerced side, so the
-/// view of \p origTy starts at the slot.  The coercion's operations are added
-/// to \p createdOps.
+/// view of \p origTy starts at the slot.  A pointer that only changes address
+/// space is cast instead.  The coercion's operations are added to
+/// \p createdOps.
 static mlir::LogicalResult
 emitCoercionAndReplaceUses(mlir::OpBuilder &builder, mlir::Location loc,
                            mlir::Type origTy, mlir::Value src, mlir::Value val,
                            mlir::Block *slotBlock, const mlir::DataLayout &dl,
                            SmallPtrSetImpl<mlir::Operation *> &createdOps,
                            unsigned offset) {
+  if (mlir::Value cast = emitAddrSpaceCoercion(builder, loc, origTy, src,
+                                               createdOps, offset)) {
+    val.replaceAllUsesExcept(cast, createdOps);
+    return mlir::success();
+  }
   mlir::Value view = emitCoercionToMemory(builder, loc, origTy, src, slotBlock,
                                           dl, createdOps, offset);
   return replaceUsesFromSlot(builder, loc, val, view, dl, createdOps);
diff --git a/clang/test/CIR/Transforms/abi-lowering/amdgpu-calling-conv.cir 
b/clang/test/CIR/Transforms/abi-lowering/amdgpu-calling-conv.cir
new file mode 100644
index 00000000000000..4043621d054bca
--- /dev/null
+++ b/clang/test/CIR/Transforms/abi-lowering/amdgpu-calling-conv.cir
@@ -0,0 +1,59 @@
+// RUN: cir-opt %s -cir-call-conv-lowering=target=amdgpu | FileCheck %s
+
+// Checks that CallConvLowering under target=amdgpu classifies a function by
+// its calling convention. Default functions get extension and Ignore rewrites
+// applied to definitions and call sites. Kernels go to the kernel classifier.
+
+!s8i = !cir.int<s, 8>
+!s32i = !cir.int<s, 32>
+
+!rec_E0 = !cir.struct<"E0" {}>
+
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<
+    #dlti.dl_entry<i1, dense<8>: vector<2xi64>>,
+    #dlti.dl_entry<i8, dense<8>: vector<2xi64>>,
+    #dlti.dl_entry<i16, dense<16>: vector<2xi64>>,
+    #dlti.dl_entry<i32, dense<32>: vector<2xi64>>,
+    #dlti.dl_entry<i64, dense<64>: vector<2xi64>>>
+} {
+
+  // Signed sub-register integer is sign-extended.
+  cir.func @take_s8(%arg0: !s8i) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @take_s8(%arg0: !s8i {llvm.signext})
+
+  // Call site picks up the same extension attribute on the operand.
+  cir.func @call_s8(%arg0: !s8i) {
+    cir.call @take_s8(%arg0) : (!s8i) -> ()
+    cir.return
+  }
+
+  // CHECK: cir.call @take_s8(%arg0) : (!s8i {llvm.signext}) -> ()
+
+  // A zero-field record classifies as Ignore: the empty argument is dropped
+  // and the real argument shifts down to the first slot.
+  cir.func @take_empty(%arg0: !rec_E0, %arg1: !s32i) -> !s32i {
+    cir.return %arg1 : !s32i
+  }
+
+  // CHECK: cir.func{{.*}} @take_empty(%arg0: !s32i) -> !s32i
+  // CHECK-NEXT: cir.return %arg0 : !s32i
+
+  // A kernel scalar argument stays Direct: the AMDGPU_KERNEL convention is
+  // routed to the kernel classifier and the signature is unchanged.
+  cir.func @kernel_scalar(%arg0: !s32i) cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @kernel_scalar(%arg0: !s32i){{.*}}cc(amdgpu_kernel)
+
+  // Without HIP in the module's lang options, a kernel pointer stays generic.
+  cir.func @kernel_ptr(%arg0: !cir.ptr<!s32i>) cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @kernel_ptr(%arg0: 
!cir.ptr<!s32i>){{.*}}cc(amdgpu_kernel)
+}
diff --git a/clang/test/CIR/Transforms/abi-lowering/amdgpu-indirect-nyi.cir 
b/clang/test/CIR/Transforms/abi-lowering/amdgpu-indirect-nyi.cir
new file mode 100644
index 00000000000000..c925f21bc5189e
--- /dev/null
+++ b/clang/test/CIR/Transforms/abi-lowering/amdgpu-indirect-nyi.cir
@@ -0,0 +1,37 @@
+// RUN: not cir-opt %s -cir-call-conv-lowering=target=amdgpu 2>&1 | FileCheck 
%s
+
+// AMDGPU classifications whose indirect pointer is not in the default address
+// space are not lowered yet, so they are reported rather than misclassified.
+
+!s32i = !cir.int<s, 32>
+!rec_Pair = !cir.struct<"Pair" {data !s32i, data !s32i}>
+!rec_Big = !cir.struct<"Big" {data !cir.array<!s32i x 17>}>
+
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<
+    #dlti.dl_entry<i32, dense<32>: vector<2xi64>>,
+    #dlti.dl_entry<i64, dense<64>: vector<2xi64>>>
+} {
+
+  // A multi-field kernel argument is passed byref in addrspace(4).
+  cir.func @kern_pair(%arg0: !rec_Pair) cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for the ABI coercion of type 
'!cir.struct<"Pair"
+
+  // An argument past the 16-register budget is passed byref in addrspace(5).
+  cir.func @take_big(%arg0: !rec_Big) {
+    cir.return
+  }
+
+  // CHECK: not yet implemented for the ABI coercion of type '!cir.struct<"Big"
+
+  // A return past the register budget goes through an sret in addrspace(5).
+  cir.func @ret_big(%arg0: !cir.ptr<!rec_Big>) -> !rec_Big {
+    %0 = cir.load %arg0 : !cir.ptr<!rec_Big>, !rec_Big
+    cir.return %0 : !rec_Big
+  }
+
+  // CHECK: not yet implemented for the ABI coercion of type '!cir.struct<"Big"
+}
diff --git a/clang/test/CIR/Transforms/abi-lowering/amdgpu-kernel-hip-ptr.cir 
b/clang/test/CIR/Transforms/abi-lowering/amdgpu-kernel-hip-ptr.cir
new file mode 100644
index 00000000000000..ba35c4bc74b475
--- /dev/null
+++ b/clang/test/CIR/Transforms/abi-lowering/amdgpu-kernel-hip-ptr.cir
@@ -0,0 +1,53 @@
+// RUN: cir-opt %s -cir-call-conv-lowering=target=amdgpu | FileCheck %s
+
+// Under HIP a generic pointer kernel argument is passed as a global pointer.
+// The HIP flag comes from the module's lowering_lang_options.
+
+!s32i = !cir.int<s, 32>
+!rec_PtrWrap = !cir.struct<"PtrWrap" {data !cir.ptr<!s32i>}>
+
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<
+    #dlti.dl_entry<i32, dense<32>: vector<2xi64>>,
+    #dlti.dl_entry<i64, dense<64>: vector<2xi64>>>,
+  cir.lowering_lang_options = #cir.lowering_lang_options<
+    exceptions = false, threadsafe_statics = true, cuda = true,
+    cuda_is_device = true, hip = true, hlsl = false, gpu_rdc = false,
+    openmp = false, openmp_is_target_device = false, clang_abi_compat = 17>
+} {
+
+  // The kernel casts the incoming global pointer back to generic before the
+  // body's spill reads it.
+  cir.func @kernel_ptr(%arg0: !cir.ptr<!s32i>) cc(amdgpu_kernel) {
+    %0 = cir.alloca "p" align(8) init : !cir.ptr<!cir.ptr<!s32i>>
+    cir.store %arg0, %0 : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
+    cir.return
+  }
+
+  // CHECK:      cir.func{{.*}} @kernel_ptr(%[[ARG:[^:]+]]: !cir.ptr<!s32i, 
target_address_space(1)>){{.*}}cc(amdgpu_kernel)
+  // CHECK-NEXT:   %[[GEN:.*]] = cir.cast address_space %[[ARG]] : 
!cir.ptr<!s32i, target_address_space(1)> -> !cir.ptr<!s32i>
+  // CHECK:        cir.store %[[GEN]], %{{.*}} : !cir.ptr<!s32i>, 
!cir.ptr<!cir.ptr<!s32i>>
+
+  // A pointer already outside the generic address space is left alone.
+  cir.func @kernel_global_ptr(%arg0: !cir.ptr<!s32i, target_address_space(1)>)
+      cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // CHECK:      cir.func{{.*}} @kernel_global_ptr(%arg0: !cir.ptr<!s32i, 
target_address_space(1)>){{.*}}cc(amdgpu_kernel)
+  // CHECK-NOT:    cir.cast
+
+  // A struct holding only a pointer is unwrapped to it, then coerced.
+  cir.func @kernel_ptr_wrap(%arg0: !rec_PtrWrap) cc(amdgpu_kernel) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @kernel_ptr_wrap(%{{.*}}: !cir.ptr<{{.*}}, 
target_address_space(1)>){{.*}}cc(amdgpu_kernel)
+
+  // Only kernels are coerced.
+  cir.func @device_ptr(%arg0: !cir.ptr<!s32i>) {
+    cir.return
+  }
+
+  // CHECK: cir.func{{.*}} @device_ptr(%arg0: !cir.ptr<!s32i>)
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to