summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerge Martin <edb@sigluy.net>2020-10-29 10:05:45 +1000
committerDave Airlie <airlied@redhat.com>2020-12-29 09:07:23 +1000
commit0a6e1cf383d4125630a9758a4069b9f93ed8453b (patch)
tree865a4056a76df22be97dc477a42fdc1853d786ae
parent95527fe22926af9e2d7cbcffe23216ab2837cd85 (diff)
clover: add core clover printf support (v12)
"The implementation is based on what LLVM AMD target expect. The compiler provided an id link to argument desc and format used. The runtime need to store them to be able to parse the buffer filled by the device during the kernel execution, ie, an id value to find the format and followed by the arguments values" v2: airlied Split out the core code to a separate patch, add support for the different global buffer formats, and move the LLVM specific code as much as possible to the backend. v3: handle strings differences better llvm backend stores strings to the printf buffer nir backend stores them to a sideband storage in NIR and stores an index in the buffer. v4: move specifier parsing to util code. v5: rename buffer fmt + make printf code work v6: handle args/specifier number mismatch support v7: move to single string + struct v8: use "%s" to print strings to avoid bad specifier, fix str calcs. v9: move to the same global buffer format as llvm, just strings are different now. This requires changes to nir lowering. buffer format: [0] contains offset into buffer at start contains 8 [1] contains length of buffer v10: printf const clean, add warning, endian assert, print %% at end, fix specifiers to vector v11: minor cleanups, make sure the format string never contains an n. v12: validate format string Reviewed-by: Francisco Jerez <currojerez@riseup.net> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8254>
-rw-r--r--src/gallium/frontends/clover/Makefile.sources2
-rw-r--r--src/gallium/frontends/clover/api/device.cpp4
-rw-r--r--src/gallium/frontends/clover/core/device.cpp5
-rw-r--r--src/gallium/frontends/clover/core/device.hpp1
-rw-r--r--src/gallium/frontends/clover/core/kernel.cpp16
-rw-r--r--src/gallium/frontends/clover/core/kernel.hpp2
-rw-r--r--src/gallium/frontends/clover/core/module.hpp3
-rw-r--r--src/gallium/frontends/clover/core/printf.cpp237
-rw-r--r--src/gallium/frontends/clover/core/printf.hpp60
-rw-r--r--src/gallium/frontends/clover/meson.build2
10 files changed, 327 insertions, 5 deletions
diff --git a/src/gallium/frontends/clover/Makefile.sources b/src/gallium/frontends/clover/Makefile.sources
index 38f94981fb6..534574178c3 100644
--- a/src/gallium/frontends/clover/Makefile.sources
+++ b/src/gallium/frontends/clover/Makefile.sources
@@ -31,6 +31,8 @@ CPP_SOURCES := \
core/object.hpp \
core/platform.cpp \
core/platform.hpp \
+ core/printf.cpp \
+ core/printf.hpp \
core/program.cpp \
core/program.hpp \
core/property.hpp \
diff --git a/src/gallium/frontends/clover/api/device.cpp b/src/gallium/frontends/clover/api/device.cpp
index 80101359368..ed36ad51025 100644
--- a/src/gallium/frontends/clover/api/device.cpp
+++ b/src/gallium/frontends/clover/api/device.cpp
@@ -369,9 +369,7 @@ clGetDeviceInfo(cl_device_id d_dev, cl_device_info param,
break;
case CL_DEVICE_PRINTF_BUFFER_SIZE:
- // Per the spec, the minimum value for the FULL profile is 1 MB.
- // However, clover is not ready yet to support it
- buf.as_scalar<size_t>() = 0 /* 1024 */;
+ buf.as_scalar<size_t>() = dev.max_printf_buffer_size();
break;
case CL_DEVICE_PREFERRED_INTEROP_USER_SYNC:
diff --git a/src/gallium/frontends/clover/core/device.cpp b/src/gallium/frontends/clover/core/device.cpp
index 0a8fa50d338..4e9268eb3e9 100644
--- a/src/gallium/frontends/clover/core/device.cpp
+++ b/src/gallium/frontends/clover/core/device.cpp
@@ -204,6 +204,11 @@ device::max_compute_units() const {
PIPE_COMPUTE_CAP_MAX_COMPUTE_UNITS)[0];
}
+cl_uint
+device::max_printf_buffer_size() const {
+ return 1024 * 1024;
+}
+
bool
device::image_support() const {
return get_compute_param<uint32_t>(pipe, ir_format(),
diff --git a/src/gallium/frontends/clover/core/device.hpp b/src/gallium/frontends/clover/core/device.hpp
index bc57c8d8197..5cf3b6dc6b6 100644
--- a/src/gallium/frontends/clover/core/device.hpp
+++ b/src/gallium/frontends/clover/core/device.hpp
@@ -70,6 +70,7 @@ namespace clover {
cl_ulong max_mem_alloc_size() const;
cl_uint max_clock_frequency() const;
cl_uint max_compute_units() const;
+ cl_uint max_printf_buffer_size() const;
bool image_support() const;
bool has_doubles() const;
bool has_halves() const;
diff --git a/src/gallium/frontends/clover/core/kernel.cpp b/src/gallium/frontends/clover/core/kernel.cpp
index e9169317199..0ca34cb7643 100644
--- a/src/gallium/frontends/clover/core/kernel.cpp
+++ b/src/gallium/frontends/clover/core/kernel.cpp
@@ -173,7 +173,7 @@ kernel::module(const command_queue &q) const {
}
kernel::exec_context::exec_context(kernel &kern) :
- kern(kern), q(NULL), mem_local(0), st(NULL), cs() {
+ kern(kern), q(NULL), print_handler(), mem_local(0), st(NULL), cs() {
}
kernel::exec_context::~exec_context() {
@@ -251,6 +251,17 @@ kernel::exec_context::bind(intrusive_ptr<command_queue> _q,
arg->bind(*this, marg);
break;
}
+ case module::argument::printf_buffer: {
+ print_handler = printf_handler::create(q, m.printf_infos,
+ m.printf_strings_in_buffer,
+ q->device().max_printf_buffer_size());
+ cl_mem print_mem = print_handler->get_mem();
+
+ auto arg = argument::create(marg);
+ arg->set(sizeof(cl_mem), &print_mem);
+ arg->bind(*this, marg);
+ break;
+ }
}
}
@@ -277,6 +288,9 @@ kernel::exec_context::bind(intrusive_ptr<command_queue> _q,
void
kernel::exec_context::unbind() {
+ if (print_handler)
+ print_handler->print();
+
for (auto &arg : kern.args())
arg.unbind(*this);
diff --git a/src/gallium/frontends/clover/core/kernel.hpp b/src/gallium/frontends/clover/core/kernel.hpp
index 31967b66597..57627e983bc 100644
--- a/src/gallium/frontends/clover/core/kernel.hpp
+++ b/src/gallium/frontends/clover/core/kernel.hpp
@@ -27,6 +27,7 @@
#include <memory>
#include "core/object.hpp"
+#include "core/printf.hpp"
#include "core/program.hpp"
#include "core/memory.hpp"
#include "core/sampler.hpp"
@@ -53,6 +54,7 @@ namespace clover {
kernel &kern;
intrusive_ptr<command_queue> q;
+ std::unique_ptr<printf_handler> print_handler;
std::vector<uint8_t> input;
std::vector<void *> samplers;
diff --git a/src/gallium/frontends/clover/core/module.hpp b/src/gallium/frontends/clover/core/module.hpp
index 01694b83222..1d1f803520f 100644
--- a/src/gallium/frontends/clover/core/module.hpp
+++ b/src/gallium/frontends/clover/core/module.hpp
@@ -103,7 +103,8 @@ namespace clover {
grid_offset,
image_size,
image_format,
- constant_buffer
+ constant_buffer,
+ printf_buffer
};
argument(enum type type, size_t size,
diff --git a/src/gallium/frontends/clover/core/printf.cpp b/src/gallium/frontends/clover/core/printf.cpp
new file mode 100644
index 00000000000..d4ec96bd581
--- /dev/null
+++ b/src/gallium/frontends/clover/core/printf.cpp
@@ -0,0 +1,237 @@
+//
+// Copyright 2020 Serge Martin
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <cstring>
+#include <cstdio>
+#include <string>
+#include <iostream>
+
+#include "util/u_math.h"
+#include "core/printf.hpp"
+
+#include "util/u_printf.h"
+using namespace clover;
+
+namespace {
+
+ const cl_uint hdr_dwords = 2;
+ const cl_uint initial_buffer_offset = hdr_dwords * sizeof(cl_uint);
+
+ /* all valid chars that can appear in CL C printf string. */
+ const std::string clc_printf_whitelist = "%0123456789-+ #.AacdeEfFgGhilopsuvxX";
+
+ void
+ print_formatted(const std::vector<module::printf_info> &formatters,
+ bool _strings_in_buffer,
+ const std::vector<char> &buffer) {
+
+ static std::atomic<unsigned> warn_count;
+ if (buffer.empty() && !warn_count++)
+ std::cerr << "Printf used but no printf occurred - may cause perfomance issue." << std::endl;
+
+ for (size_t buf_pos = 0; buf_pos < buffer.size(); ) {
+ cl_uint fmt_idx = *(cl_uint*)&buffer[buf_pos];
+ assert(fmt_idx > 0);
+ module::printf_info fmt = formatters[fmt_idx-1];
+
+ std::string format = (char *)fmt.strings.data();
+ buf_pos += sizeof(cl_uint);
+
+ if (fmt.arg_sizes.empty()) {
+ printf("%s", format.c_str());
+
+ } else {
+ size_t fmt_last_pos = 0;
+ size_t fmt_pos = 0;
+ for (int arg_size : fmt.arg_sizes) {
+ const size_t spec_pos = util_printf_next_spec_pos(format, fmt_pos);
+ const size_t cur_tok = format.rfind('%', spec_pos);
+ const size_t next_spec = util_printf_next_spec_pos(format, spec_pos);
+ const size_t next_tok = next_spec == std::string::npos ? std::string::npos :
+ format.rfind('%', next_spec);
+
+ size_t vec_pos = format.find_first_of("v", cur_tok + 1);
+ size_t mod_pos = format.find_first_of("hl", cur_tok + 1);
+
+ // print the part before the format token
+ if (cur_tok != fmt_last_pos) {
+ std::string s = format.substr(fmt_last_pos,
+ cur_tok - fmt_last_pos);
+ printf("%s", s.c_str());
+ }
+
+ std::string print_str;
+ print_str = format.substr(cur_tok, spec_pos + 1 - cur_tok);
+
+ /* Never pass a 'n' spec to the host printf */
+ bool valid_str = print_str.find_first_not_of(clc_printf_whitelist) ==
+ std::string::npos;
+
+ // print the formated part
+ if (spec_pos != std::string::npos && valid_str) {
+ bool is_vector = vec_pos != std::string::npos &&
+ vec_pos + 1 < spec_pos;
+ bool is_string = format[spec_pos] == 's';
+ bool is_float = std::string("fFeEgGaA")
+ .find(format[spec_pos]) != std::string::npos;
+
+ if (is_string) {
+ if (_strings_in_buffer)
+ printf(print_str.c_str(), &buffer[buf_pos]);
+ else {
+ uint64_t idx;
+ memcpy(&idx, &buffer[buf_pos], 8);
+ printf(print_str.c_str(), &fmt.strings[idx]);
+ }
+ } else {
+ int component_count = 1;
+
+ if (is_vector) {
+ size_t l = std::min(mod_pos, spec_pos) - vec_pos - 1;
+ std::string s = format.substr(vec_pos + 1, l);
+ component_count = std::stoi(s);
+ if (mod_pos != std::string::npos) {
+ // CL C has hl specifier for 32-bit vectors, C doesn't have it
+ // just remove it.
+ std::string mod = format.substr(mod_pos, 2);
+ if (mod == "hl")
+ mod_pos = std::string::npos;
+ }
+ print_str.erase(vec_pos - cur_tok, std::min(mod_pos, spec_pos) - vec_pos);
+ print_str.push_back(',');
+ }
+
+ //in fact vec3 are vec4
+ int men_components =
+ component_count == 3 ? 4 : component_count;
+ size_t elmt_size = arg_size / men_components;
+
+ for (int i = 0; i < component_count; i++) {
+ size_t elmt_buf_pos = buf_pos + i * elmt_size;
+ if (is_vector && i + 1 == component_count)
+ print_str.pop_back();
+
+ if (is_float) {
+ switch (elmt_size) {
+ case 2:
+ cl_half h;
+ std::memcpy(&h, &buffer[elmt_buf_pos], elmt_size);
+ printf(print_str.c_str(), h);
+ break;
+ case 4:
+ cl_float f;
+ std::memcpy(&f, &buffer[elmt_buf_pos], elmt_size);
+ printf(print_str.c_str(), f);
+ break;
+ default:
+ cl_double d;
+ std::memcpy(&d, &buffer[elmt_buf_pos], elmt_size);
+ printf(print_str.c_str(), d);
+ }
+ } else {
+ cl_long l = 0;
+ std::memcpy(&l, &buffer[elmt_buf_pos], elmt_size);
+ printf(print_str.c_str(), l);
+ }
+ }
+ }
+ // print the remaining
+ if (next_tok != spec_pos) {
+ std::string s = format.substr(spec_pos + 1,
+ next_tok - spec_pos - 1);
+ printf("%s", s.c_str());
+ }
+ }
+
+ fmt_pos = spec_pos;
+ fmt_last_pos = next_tok;
+
+ buf_pos += arg_size;
+ buf_pos = ALIGN(buf_pos, 4);
+ }
+ }
+ }
+ }
+}
+
+std::unique_ptr<printf_handler>
+printf_handler::create(const intrusive_ptr<command_queue> &q,
+ const std::vector<module::printf_info> &infos,
+ bool strings_in_buffer,
+ cl_uint size) {
+ return std::unique_ptr<printf_handler>(
+ new printf_handler(q, infos, strings_in_buffer, size));
+}
+
+printf_handler::printf_handler(const intrusive_ptr<command_queue> &q,
+ const std::vector<module::printf_info> &infos,
+ bool strings_in_buffer,
+ cl_uint size) :
+ _q(q), _formatters(infos), _strings_in_buffer(strings_in_buffer), _size(size), _buffer() {
+
+ if (_size) {
+ std::string data;
+ data.reserve(_size);
+ cl_uint header[2] = { 0 };
+
+ header[0] = initial_buffer_offset;
+ header[1] = _size;
+
+ data.append((char *)header, (char *)(header+hdr_dwords));
+ _buffer = std::unique_ptr<root_buffer>(new root_buffer(_q->context,
+ std::vector<cl_mem_properties>(),
+ CL_MEM_COPY_HOST_PTR,
+ _size, (char*)data.data()));
+ }
+}
+
+cl_mem
+printf_handler::get_mem() {
+ return (cl_mem)(_buffer.get());
+}
+
+void
+printf_handler::print() {
+ if (!_buffer)
+ return;
+
+ mapping src = { *_q, _buffer->resource_in(*_q), CL_MAP_READ, true,
+ {{ 0 }}, {{ _size, 1, 1 }} };
+
+ cl_uint header[2] = { 0 };
+ std::memcpy(header,
+ static_cast<const char *>(src),
+ initial_buffer_offset);
+
+ cl_uint buffer_size = header[0];
+ buffer_size -= initial_buffer_offset;
+ std::vector<char> buf;
+ buf.resize(buffer_size);
+
+ std::memcpy(buf.data(),
+ static_cast<const char *>(src) + initial_buffer_offset,
+ buffer_size);
+
+ // mixed endian isn't going to work, sort it out if anyone cares later.
+ assert(_q->device().endianness() == PIPE_ENDIAN_NATIVE);
+ print_formatted(_formatters, _strings_in_buffer, buf);
+}
diff --git a/src/gallium/frontends/clover/core/printf.hpp b/src/gallium/frontends/clover/core/printf.hpp
new file mode 100644
index 00000000000..3fc740c1aea
--- /dev/null
+++ b/src/gallium/frontends/clover/core/printf.hpp
@@ -0,0 +1,60 @@
+//
+// Copyright 2020 Serge Martin
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#ifndef CLOVER_CORE_PRINTF_HANDLER_HPP
+#define CLOVER_CORE_PRINTF_HANDLER_HPP
+
+#include <memory>
+
+#include "core/memory.hpp"
+
+namespace clover {
+ class printf_handler {
+ public:
+ static std::unique_ptr<printf_handler>
+ create(const intrusive_ptr<command_queue> &q,
+ const std::vector<module::printf_info> &info,
+ bool strings_in_buffer, cl_uint size);
+
+ printf_handler(const printf_handler &arg) = delete;
+ printf_handler &
+ operator=(const printf_handler &arg) = delete;
+
+ ~printf_handler() {};
+
+ cl_mem get_mem();
+ void print();
+
+ private:
+ printf_handler(const intrusive_ptr<command_queue> &q,
+ const std::vector<module::printf_info> &infos,
+ bool strings_in_buffer, cl_uint size);
+
+ intrusive_ptr<command_queue> _q;
+ std::vector<module::printf_info> _formatters;
+ bool _strings_in_buffer;
+ cl_uint _size;
+ std::unique_ptr<root_buffer> _buffer;
+ };
+}
+
+#endif
diff --git a/src/gallium/frontends/clover/meson.build b/src/gallium/frontends/clover/meson.build
index b946eefff37..b6a231f9790 100644
--- a/src/gallium/frontends/clover/meson.build
+++ b/src/gallium/frontends/clover/meson.build
@@ -132,6 +132,8 @@ clover_files = files(
'core/object.hpp',
'core/platform.cpp',
'core/platform.hpp',
+ 'core/printf.cpp',
+ 'core/printf.hpp',
'core/program.cpp',
'core/program.hpp',
'core/property.hpp',