[Mesa-dev] [PATCH 1/2] clover: Implement image attribute getters

Zoltan Gilian zoltan.gilian at gmail.com
Mon Jun 8 04:20:24 PDT 2015


Image attributes are passed to the kernel as hidden parameters after the
image attribute itself. An llvm pass replaces the getter builtins to
the appropriate parameters.
---
 src/gallium/state_trackers/clover/core/kernel.cpp  |  13 ++
 src/gallium/state_trackers/clover/core/memory.cpp  |   2 +-
 .../state_trackers/clover/llvm/invocation.cpp      | 158 ++++++++++++++++++++-
 3 files changed, 170 insertions(+), 3 deletions(-)

diff --git a/src/gallium/state_trackers/clover/core/kernel.cpp b/src/gallium/state_trackers/clover/core/kernel.cpp
index 0756f06..4703899 100644
--- a/src/gallium/state_trackers/clover/core/kernel.cpp
+++ b/src/gallium/state_trackers/clover/core/kernel.cpp
@@ -483,6 +483,19 @@ kernel::image_rd_argument::bind(exec_context &ctx,
    align(ctx.input, marg.target_align);
    insert(ctx.input, v);
 
+   cl_image_format fmt = img->format();
+   cl_uint image_attribs[] = {img->width(), img->height(), img->depth(),
+                              fmt.image_channel_data_type,
+                              fmt.image_channel_order};
+   for (int i = 0; i < 5; i++) {
+      auto v = bytes(image_attribs[i]);
+
+      extend(v, module::argument::zero_ext, marg.target_size);
+      byteswap(v, ctx.q->device().endianness());
+      align(ctx.input, marg.target_align);
+      insert(ctx.input, v);
+   }
+
    st = img->resource(*ctx.q).bind_sampler_view(*ctx.q);
    ctx.sviews.push_back(st);
 }
diff --git a/src/gallium/state_trackers/clover/core/memory.cpp b/src/gallium/state_trackers/clover/core/memory.cpp
index 055336a..b852e68 100644
--- a/src/gallium/state_trackers/clover/core/memory.cpp
+++ b/src/gallium/state_trackers/clover/core/memory.cpp
@@ -189,7 +189,7 @@ image2d::image2d(clover::context &ctx, cl_mem_flags flags,
                  const cl_image_format *format, size_t width,
                  size_t height, size_t row_pitch,
                  void *host_ptr) :
-   image(ctx, flags, format, width, height, 0,
+   image(ctx, flags, format, width, height, 1,
          row_pitch, 0, height * row_pitch, host_ptr) {
 }
 
diff --git a/src/gallium/state_trackers/clover/llvm/invocation.cpp b/src/gallium/state_trackers/clover/llvm/invocation.cpp
index 9b91fee..5d5e619 100644
--- a/src/gallium/state_trackers/clover/llvm/invocation.cpp
+++ b/src/gallium/state_trackers/clover/llvm/invocation.cpp
@@ -33,6 +33,8 @@
 #include <llvm/IR/DiagnosticInfo.h>
 #include <llvm/IR/DiagnosticPrinter.h>
 #include <llvm/IR/DerivedTypes.h>
+#include <llvm/IR/InstIterator.h>
+#include <llvm/IR/Instructions.h>
 #include <llvm/IR/LLVMContext.h>
 #include <llvm/IR/Module.h>
 #include <llvm/Support/SourceMgr.h>
@@ -80,6 +82,130 @@
 using namespace clover;
 
 namespace {
+
+   /* LLVM pass to resolve get_image_* OpenCL builtins.
+    * The image attributes (e.g. width or channel order) are passed as hidden
+    * arguments to the kernel.
+    * This pass replaces specific function calls with the appropriate hidden
+    * arguments. The libclc library needs to implements the get_image_*
+    * builtins as these specific functions to avoid dealing with name mangling
+    * here.
+    */
+   struct OpenCLImageBuiltinPass : public llvm::FunctionPass {
+      static char ID;
+
+      OpenCLImageBuiltinPass(): llvm::FunctionPass(ID) {}
+      bool runOnFunction(llvm::Function &F) override;
+
+      struct ImageAttribArgs {
+         ImageAttribArgs(): image_arg(0),
+                            width_arg(0),
+                            heigth_arg(0),
+                            depth_arg(0),
+                            channel_data_type_arg(0),
+                            channel_order_arg(0) {}
+         llvm::Argument* image_arg;
+         llvm::Argument* width_arg;
+         llvm::Argument* heigth_arg;
+         llvm::Argument* depth_arg;
+         llvm::Argument* channel_data_type_arg;
+         llvm::Argument* channel_order_arg;
+      };
+   };
+
+   char OpenCLImageBuiltinPass::ID = 0;
+
+   bool
+   OpenCLImageBuiltinPass::runOnFunction(llvm::Function& F)
+   {
+      llvm::Module* mod = F.getParent();
+      llvm::DataLayout TD(mod);
+      llvm::Type* cl_int_type =
+         TD.getSmallestLegalIntType(mod->getContext(), sizeof(cl_int));
+
+      std::vector<ImageAttribArgs> img_args;
+      for (auto arg = F.arg_begin(), E = F.arg_end(); arg != E; ++arg) {
+
+         llvm::Type *arg_type = arg->getType();
+         if (!arg_type->isPointerTy()) continue;
+
+         llvm::Type *elem_type = arg_type->getPointerElementType();
+         if (!elem_type->isStructTy()) continue;
+
+         const llvm::StringRef &type_name = elem_type->getStructName();
+         if (!type_name.startswith("opencl.image2d_t")) continue;
+
+         auto name_suffix = llvm::Twine(img_args.size());
+         ImageAttribArgs attrib_args;
+         attrib_args.image_arg = arg;
+         attrib_args.width_arg = new llvm::Argument(
+            cl_int_type, "image_width" + name_suffix);
+         attrib_args.heigth_arg = new llvm::Argument(
+            cl_int_type, "image_height" + name_suffix);
+         attrib_args.depth_arg = new llvm::Argument(
+            cl_int_type, "image_depth" + name_suffix);
+         attrib_args.channel_data_type_arg = new llvm::Argument(
+            cl_int_type, "image_channel_data_type" + name_suffix);
+         attrib_args.channel_order_arg = new llvm::Argument(
+            cl_int_type, "image_channel_order" + name_suffix);
+
+         auto& args = F.getArgumentList();
+         args.insertAfter(arg, attrib_args.channel_order_arg);
+         args.insertAfter(arg, attrib_args.channel_data_type_arg);
+         args.insertAfter(arg, attrib_args.depth_arg);
+         args.insertAfter(arg, attrib_args.heigth_arg);
+         args.insertAfter(arg, attrib_args.width_arg);
+
+         img_args.push_back(attrib_args);
+      }
+      std::vector<llvm::Instruction*> insts_to_erase;
+      for (auto I = llvm::inst_begin(F), E = llvm::inst_end(F); I != E; ++I) {
+         llvm::CallInst* callInst = llvm::dyn_cast<llvm::CallInst>(&*I);
+         if (!callInst) continue;
+
+         for (auto& op: callInst->arg_operands()) {
+            auto op_arg = op.get();
+            auto image_arg = std::find_if(
+               img_args.begin(), img_args.end(),
+               [op_arg](const ImageAttribArgs& x) {
+                  return x.image_arg == op_arg;
+               });
+            if (image_arg == img_args.end()) continue;
+
+            llvm::Function* callee = callInst->getCalledFunction();
+            if (!callee) continue;
+
+            auto callee_name = callee->getName();
+            if (callee_name.startswith(
+                  "llvm.opencl.image.get.width")) {
+               callInst->replaceAllUsesWith(image_arg->width_arg);
+               insts_to_erase.push_back(callInst);
+            } else if (callee_name.startswith(
+                  "llvm.opencl.image.get.height")) {
+               callInst->replaceAllUsesWith(image_arg->heigth_arg);
+               insts_to_erase.push_back(callInst);
+            } else if (callee_name.startswith(
+                  "llvm.opencl.image.get.depth")) {
+               callInst->replaceAllUsesWith(image_arg->depth_arg);
+               insts_to_erase.push_back(callInst);
+            } else if (callee_name.startswith(
+                  "llvm.opencl.image.get.channel_data_type")) {
+               callInst->replaceAllUsesWith(image_arg->channel_data_type_arg);
+               insts_to_erase.push_back(callInst);
+            } else if (callee_name.startswith(
+                  "llvm.opencl.image.get.channel_order")) {
+               callInst->replaceAllUsesWith(image_arg->channel_order_arg);
+               insts_to_erase.push_back(callInst);
+            }
+         }
+      }
+      for (auto inst: insts_to_erase) {
+         inst->eraseFromParent();
+      }
+
+      return (img_args.size() > 0);
+   }
+
 #if 0
    void
    build_binary(const std::string &source, const std::string &target,
@@ -263,10 +389,21 @@ namespace {
                                                         sizeof(address_spaces));
 
 #if HAVE_LLVM >= 0x0306
-      return act.takeModule().release();
+      llvm::Module * module = act.takeModule().release();
+#else
+      llvm::Module * module = act.takeModule();
+#endif
+
+      // Resolve get_image_* builtins.
+#if HAVE_LLVM >= 0x0307
+      llvm::legacy::PassManager PM;
 #else
-      return act.takeModule();
+      llvm::PassManager PM;
 #endif
+      PM.add(new OpenCLImageBuiltinPass());
+      PM.run(*module);
+
+      return module;
    }
 
    void
@@ -375,6 +512,23 @@ namespace {
          }
 
          if (arg_type->isPointerTy()) {
+            // XXX: Figure out LLVM->OpenCL address space mappings for each
+            // target.  I think we need to ask clang what these are.  For now,
+            // pretend everything is in the global address space.
+            llvm::Type *elem_type = arg_type->getPointerElementType();
+            if (elem_type->isStructTy()) {
+               const llvm::StringRef &type_name = elem_type->getStructName();
+
+               if (type_name.startswith("opencl.image2d_t")) {
+                  args.push_back(module::argument(module::argument::image2d_rd,
+                                 arg_store_size, target_size, target_align,
+                                 module::argument::zero_ext));
+                  // Skip hidden image attribute arguments.
+                  // XXX: Do this based on some kind of metadata.
+                  std::advance(I, 5);
+                  continue;
+               }
+            }
             unsigned address_space = llvm::cast<llvm::PointerType>(arg_type)->getAddressSpace();
             if (address_space == address_spaces[clang::LangAS::opencl_local
                                                      - clang::LangAS::Offset]) {
-- 
2.4.2



More information about the mesa-dev mailing list