clover: add CL_KERNEL_ATTRIBUTES for clGetKernelInfo

Reviewed-by: Francisco Jerez <currojerez@riseup.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4974>
diff --git a/src/gallium/frontends/clover/api/kernel.cpp b/src/gallium/frontends/clover/api/kernel.cpp
index 501e267..773d8e1 100644
--- a/src/gallium/frontends/clover/api/kernel.cpp
+++ b/src/gallium/frontends/clover/api/kernel.cpp
@@ -134,6 +134,10 @@
       buf.as_scalar<cl_program>() = desc(kern.program());
       break;
 
+   case CL_KERNEL_ATTRIBUTES:
+      buf.as_string() = find(name_equals(kern.name()), kern.program().symbols()).attributes;
+      break;
+
    default:
       throw error(CL_INVALID_VALUE);
    }
diff --git a/src/gallium/frontends/clover/core/module.cpp b/src/gallium/frontends/clover/core/module.cpp
index a6c5b98..aa7d3d0 100644
--- a/src/gallium/frontends/clover/core/module.cpp
+++ b/src/gallium/frontends/clover/core/module.cpp
@@ -190,6 +190,7 @@
       static void
       proc(S &s, QT &x) {
          _proc(s, x.name);
+         _proc(s, x.attributes);
          _proc(s, x.section);
          _proc(s, x.offset);
          _proc(s, x.args);
diff --git a/src/gallium/frontends/clover/core/module.hpp b/src/gallium/frontends/clover/core/module.hpp
index 1d938df..6117f7f 100644
--- a/src/gallium/frontends/clover/core/module.hpp
+++ b/src/gallium/frontends/clover/core/module.hpp
@@ -128,12 +128,16 @@
       };
 
       struct symbol {
-         symbol(const std::string &name, resource_id section,
-                size_t offset, const std::vector<argument> &args) :
-                name(name), section(section), offset(offset), args(args) { }
-         symbol() : name(), section(0), offset(0), args() { }
+         symbol(const std::string &name, const std::string &attributes,
+                resource_id section, size_t offset,
+                const std::vector<argument> &args) :
+            name(name), attributes(attributes),
+            section(section),
+            offset(offset), args(args) { }
+         symbol() : name(), attributes(), section(0), offset(0), args() { }
 
          std::string name;
+         std::string attributes;
          resource_id section;
          size_t offset;
          std::vector<argument> args;
diff --git a/src/gallium/frontends/clover/llvm/codegen/common.cpp b/src/gallium/frontends/clover/llvm/codegen/common.cpp
index 698f77c..0cb7148 100644
--- a/src/gallium/frontends/clover/llvm/codegen/common.cpp
+++ b/src/gallium/frontends/clover/llvm/codegen/common.cpp
@@ -103,6 +103,39 @@
                               cl_address_qualifier, cl_access_qualifier);
    }
 
+   std::string
+   kernel_attributes(const Module &mod, const std::string &kernel_name) {
+      std::vector<std::string> attributes;
+
+      const Function &f = *mod.getFunction(kernel_name);
+
+      auto vec_type_hint = get_type_kernel_metadata(f, "vec_type_hint");
+      if (!vec_type_hint.empty())
+         attributes.emplace_back("vec_type_hint(" + vec_type_hint + ")");
+
+      auto work_group_size_hint = get_uint_vector_kernel_metadata(f, "work_group_size_hint");
+      if (!work_group_size_hint.empty()) {
+         std::string s = "work_group_size_hint(";
+         s += detokenize(work_group_size_hint, ",");
+         s += ")";
+         attributes.emplace_back(s);
+      }
+
+      auto reqd_work_group_size = get_uint_vector_kernel_metadata(f, "reqd_work_group_size");
+      if (!reqd_work_group_size.empty()) {
+         std::string s = "reqd_work_group_size(";
+         s += detokenize(reqd_work_group_size, ",");
+         s += ")";
+         attributes.emplace_back(s);
+      }
+
+      auto nosvm = get_str_kernel_metadata(f, "nosvm");
+      if (!nosvm.empty())
+         attributes.emplace_back("nosvm");
+
+      return detokenize(attributes, " ");
+   }
+
    std::vector<module::argument>
    make_kernel_args(const Module &mod, const std::string &kernel_name,
                     const clang::CompilerInstance &c) {
@@ -251,7 +284,8 @@
                                get_kernels(mod))) {
       const ::std::string name(llvm_name);
       if (offsets.count(name))
-         m.syms.emplace_back(name, 0, offsets.at(name),
+         m.syms.emplace_back(name, kernel_attributes(mod, name),
+                             0, offsets.at(name),
                              make_kernel_args(mod, name, c));
    }
 
diff --git a/src/gallium/frontends/clover/llvm/metadata.hpp b/src/gallium/frontends/clover/llvm/metadata.hpp
index 8519d98..be97a00 100644
--- a/src/gallium/frontends/clover/llvm/metadata.hpp
+++ b/src/gallium/frontends/clover/llvm/metadata.hpp
@@ -56,6 +56,94 @@
       }
 
       ///
+      /// Extract the string metadata node \p name.
+      ///
+      inline std::string
+      get_str_kernel_metadata(const ::llvm::Function &f,
+                          const std::string &name) {
+         auto operands = detail::get_kernel_metadata_operands(f, name);
+         if (operands.size()) {
+            return ::llvm::cast< ::llvm::MDString>(
+               detail::get_kernel_metadata_operands(f, name)[0])
+               ->getString().str();
+         } else {
+            return "";
+         }
+      }
+
+      ///
+      /// Extract the string metadata node \p name.
+      ///
+      inline std::vector<size_t>
+      get_uint_vector_kernel_metadata(const ::llvm::Function &f,
+                          const std::string &name) {
+         auto operands = detail::get_kernel_metadata_operands(f, name);
+         if (operands.size()) {
+            return map([=](const ::llvm::MDOperand& o) {
+               auto value = ::llvm::cast< ::llvm::ConstantAsMetadata>(o)
+                                                               ->getValue();
+               return ::llvm::cast< ::llvm::ConstantInt>(value)
+                                                ->getLimitedValue(UINT_MAX);
+            }, operands);
+         } else {
+            return {};
+         }
+      }
+
+      ///
+      /// Extract the string metadata node \p name.
+      ///
+      inline std::string
+      get_type_kernel_metadata(const ::llvm::Function &f,
+                          const std::string &name) {
+         auto operands = detail::get_kernel_metadata_operands(f, name);
+         if (operands.size()) {
+            auto value = ::llvm::cast< ::llvm::ConstantAsMetadata>(operands[0])
+                                                               ->getValue();
+            auto type = ::llvm::cast< ::llvm::UndefValue>(value)
+                                                               ->getType();
+
+            value = ::llvm::cast< ::llvm::ConstantAsMetadata>(operands[1])
+                                                               ->getValue();
+            bool is_signed = ::llvm::cast< ::llvm::ConstantInt>(value)
+                                                ->getLimitedValue(UINT_MAX);
+
+            std::string data;
+            if (type->isIntOrIntVectorTy()) {
+               if (!is_signed)
+                  data = "unsigned ";
+
+               const auto size = type->getScalarSizeInBits();
+               switch(size) {
+                  case 8:
+                     data += "char";
+                     break;
+                  case 16:
+                     data += "short";
+                     break;
+                  case 32:
+                     data += "int";
+                     break;
+                  case 64:
+                     data += "long";
+                     break;
+               }
+               if (type->isVectorTy())
+                  data += std::to_string(((::llvm::VectorType*)type)->getNumElements());
+
+            } else {
+               ::llvm::raw_string_ostream os { data };
+               type->print(os);
+               os.flush();
+            }
+
+            return data;
+         } else {
+            return "";
+         }
+      }
+
+      ///
       /// Extract the string metadata node \p name corresponding to the kernel
       /// argument given by \p arg.
       ///
diff --git a/src/gallium/frontends/clover/nir/invocation.cpp b/src/gallium/frontends/clover/nir/invocation.cpp
index c0bf646..991ebd6 100644
--- a/src/gallium/frontends/clover/nir/invocation.cpp
+++ b/src/gallium/frontends/clover/nir/invocation.cpp
@@ -390,7 +390,7 @@
                        reinterpret_cast<const char *>(&header) + sizeof(header));
       text.data.insert(text.data.end(), blob.data, blob.data + blob.size);
 
-      m.syms.emplace_back(sym.name, section_id, 0, args);
+      m.syms.emplace_back(sym.name, std::string(), section_id, 0, args);
       m.secs.push_back(text);
       section_id++;
    }
diff --git a/src/gallium/frontends/clover/spirv/invocation.cpp b/src/gallium/frontends/clover/spirv/invocation.cpp
index 9b33e66..eaf99fa 100644
--- a/src/gallium/frontends/clover/spirv/invocation.cpp
+++ b/src/gallium/frontends/clover/spirv/invocation.cpp
@@ -346,7 +346,7 @@
          case SpvOpFunctionEnd:
             if (kernel_name.empty())
                break;
-            m.syms.emplace_back(kernel_name, 0, kernel_nb, args);
+            m.syms.emplace_back(kernel_name, std::string(), 0, kernel_nb, args);
             ++kernel_nb;
             kernel_name.clear();
             args.clear();
diff --git a/src/gallium/frontends/clover/util/algorithm.hpp b/src/gallium/frontends/clover/util/algorithm.hpp
index 1658458..0841dd1 100644
--- a/src/gallium/frontends/clover/util/algorithm.hpp
+++ b/src/gallium/frontends/clover/util/algorithm.hpp
@@ -213,6 +213,34 @@
 
       r.erase(i, e);
    }
+
+   ///
+   /// Build a \a sep separated string from a vector of T
+   ///
+   template<typename T>
+   std::string
+   detokenize(const std::vector<T> &ss, const std::string &sep) {
+      std::string r;
+
+      for (const auto &s : ss)
+         r += (r.empty() ? "" : sep) + std::to_string(s);
+
+      return r;
+   }
+
+   ///
+   /// Build a \a sep separated string from a vector of string
+   ///
+   template <>
+   inline std::string
+   detokenize(const std::vector<std::string> &ss, const std::string &sep) {
+      std::string r;
+
+      for (const auto &s : ss)
+         r += (r.empty() || s.empty() ? "" : sep) + s;
+
+      return r;
+   }
 }
 
 #endif