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

Zoltán Gilián zoltan.gilian at gmail.com
Wed Jun 10 02:54:09 PDT 2015


> Whether you need to pass the image
> dimensions and data type to the kernel as explicit parameters or not
> (e.g. because your hardware already supports some sort of image metadata
> query opcode) is driver-specific, so it would probably be a better idea
> to append these parameters at the end of the input buffer in the r600
> pipe driver itself.

The image data type and channel order get converted to a pipe format value
in clover before it reaches the driver, so either the driver needs to
convert it back to OpenCL constants, or clover has to send the data
explicitly to the pipe e.g. by appending it to the kernel parameters. What
solution do you think is less problematic? Are there better mechanisms to
send the image data type and channel order in OpenCL format to the pipe?

Furthermore IIRC the pipe has no explicit information on the number of
image arguments, so it would have to gather that information to determine
the amount of input memory needed by the kernel. Clover has that
information easily accessible.

How bad would it be if image attributes were handled similarly to grid
dimension and grid offset? The get_image_* builtins could be lowered to
implicit parameter reads, so there is no need to perform the llvm pass.

On Tue, Jun 9, 2015 at 11:09 AM, Zoltán Gilián <zoltan.gilian at gmail.com>
wrote:

> Ok, thanks for the feedback.
>
> On Mon, Jun 8, 2015 at 2:18 PM, Francisco Jerez <currojerez at riseup.net>
> wrote:
>
>> Zoltan Gilian <zoltan.gilian at gmail.com> writes:
>>
>> > 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);
>> > +   }
>> > +
>> This seems to be implementing driver-specific policy in a
>> hardware-independent state tracker.  Whether you need to pass the image
>> dimensions and data type to the kernel as explicit parameters or not
>> (e.g. because your hardware already supports some sort of image metadata
>> query opcode) is driver-specific, so it would probably be a better idea
>> to append these parameters at the end of the input buffer in the r600
>> pipe driver itself.
>>
>> >     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
>> >
>> > _______________________________________________
>> > mesa-dev mailing list
>> > mesa-dev at lists.freedesktop.org
>> > http://lists.freedesktop.org/mailman/listinfo/mesa-dev
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/mesa-dev/attachments/20150610/a5a8dd1a/attachment-0001.html>


More information about the mesa-dev mailing list