nir/vtn: Implement printf opcode in terms of intrinsic (v9)

[airlied: rebase fixup types]

v2: add support for storing strings in a sideband storage,
just store the index in print buffer.

v3: move the format strings into the nir shader as well

v4: simplify the write constant string + explicit sizes
move printf cap definition.

v5: just parse the format string to find string specifiers
using util code.
add vtn_fail_if if we can't get the correct type.

v6: use ralloc + avoid instr handler for srcs > 5

v7: use a packed struct 4 bytes align all of it

v8: simplify constant copy

v9: rework to use a single string and common string
extract code, (Jason)

Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
Reviewed-by: Dave Airlie <airlied@redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8254>
This commit is contained in:
Jesse Natalie
2020-06-21 14:36:00 -07:00
committed by Dave Airlie
parent 2a3fe68c33
commit 9524e9dbd0
2 changed files with 111 additions and 7 deletions

View File

@@ -68,6 +68,7 @@ struct spirv_supported_capabilities {
bool multiview; bool multiview;
bool physical_storage_buffer_address; bool physical_storage_buffer_address;
bool post_depth_coverage; bool post_depth_coverage;
bool printf;
bool ray_tracing; bool ray_tracing;
bool ray_query; bool ray_query;
bool ray_traversal_primitive_culling; bool ray_traversal_primitive_culling;

View File

@@ -27,6 +27,7 @@
#include "math.h" #include "math.h"
#include "nir/nir_builtin_builder.h" #include "nir/nir_builtin_builder.h"
#include "util/u_printf.h"
#include "vtn_private.h" #include "vtn_private.h"
#include "OpenCL.std.h" #include "OpenCL.std.h"
@@ -723,13 +724,115 @@ vtn_handle_opencl_vstore_half_r(struct vtn_builder *b, enum OpenCLstd_Entrypoint
vtn_rounding_mode_to_nir(b, w[8])); vtn_rounding_mode_to_nir(b, w[8]));
} }
static nir_ssa_def * static unsigned
handle_printf(struct vtn_builder *b, uint32_t opcode, vtn_add_printf_string(struct vtn_builder *b, uint32_t id, nir_printf_info *info)
unsigned num_srcs, nir_ssa_def **srcs, struct vtn_type **src_types,
const struct vtn_type *dest_type)
{ {
/* hahah, yeah, right.. */ nir_deref_instr *deref = vtn_nir_deref(b, id);
return nir_imm_int(&b->nb, -1);
while (deref && deref->deref_type != nir_deref_type_var)
deref = nir_deref_instr_parent(deref);
vtn_fail_if(deref == NULL || !nir_deref_mode_is(deref, nir_var_mem_constant),
"Printf string argument must be a pointer to a constant variable");
vtn_fail_if(deref->var->constant_initializer == NULL,
"Printf string argument must have an initializer");
vtn_fail_if(!glsl_type_is_array(deref->var->type),
"Printf string must be an char array");
const struct glsl_type *char_type = glsl_get_array_element(deref->var->type);
vtn_fail_if(char_type != glsl_uint8_t_type() &&
char_type != glsl_int8_t_type(),
"Printf string must be an char array");
nir_constant *c = deref->var->constant_initializer;
assert(c->num_elements == glsl_get_length(deref->var->type));
unsigned idx = info->string_size;
info->strings = reralloc_size(b->shader, info->strings,
idx + c->num_elements);
info->string_size += c->num_elements;
char *str = &info->strings[idx];
bool found_null = false;
for (unsigned i = 0; i < c->num_elements; i++) {
memcpy((char *)str + i, c->elements[i]->values, 1);
if (str[i] == '\0')
found_null = true;
}
vtn_fail_if(!found_null, "Printf string must be null terminated");
return idx;
}
/* printf is special because there are no limits on args */
static void
handle_printf(struct vtn_builder *b, uint32_t opcode,
const uint32_t *w_src, unsigned num_srcs, const uint32_t *w_dest)
{
if (!b->options->caps.printf) {
vtn_push_nir_ssa(b, w_dest[1], nir_imm_int(&b->nb, -1));
return;
}
/* Step 1. extract the format string */
/*
* info_idx is 1-based to match clover/llvm
* the backend indexes the info table at info_idx - 1.
*/
b->shader->printf_info_count++;
unsigned info_idx = b->shader->printf_info_count;
b->shader->printf_info = reralloc(b->shader, b->shader->printf_info,
nir_printf_info, info_idx);
nir_printf_info *info = &b->shader->printf_info[info_idx - 1];
info->strings = NULL;
info->string_size = 0;
vtn_add_printf_string(b, w_src[0], info);
info->num_args = num_srcs - 1;
info->arg_sizes = ralloc_array(b->shader, unsigned, info->num_args);
/* Step 2, build an ad-hoc struct type out of the args */
unsigned field_offset = 0;
struct glsl_struct_field *fields =
rzalloc_array(b, struct glsl_struct_field, num_srcs - 1);
for (unsigned i = 1; i < num_srcs; ++i) {
struct vtn_value *val = vtn_untyped_value(b, w_src[i]);
struct vtn_type *src_type = val->type;
fields[i - 1].type = src_type->type;
fields[i - 1].name = ralloc_asprintf(b->shader, "arg_%u", i);
field_offset = align(field_offset, 4);
fields[i - 1].offset = field_offset;
info->arg_sizes[i - 1] = glsl_get_cl_size(src_type->type);
field_offset += glsl_get_cl_size(src_type->type);
}
const struct glsl_type *struct_type =
glsl_struct_type(fields, num_srcs - 1, "printf", true);
/* Step 3, create a variable of that type and populate its fields */
nir_variable *var = nir_local_variable_create(b->func->impl, struct_type, NULL);
nir_deref_instr *deref_var = nir_build_deref_var(&b->nb, var);
size_t fmt_pos = 0;
for (unsigned i = 1; i < num_srcs; ++i) {
nir_deref_instr *field_deref =
nir_build_deref_struct(&b->nb, deref_var, i - 1);
nir_ssa_def *field_src = vtn_ssa_value(b, w_src[i])->def;
/* extract strings */
fmt_pos = util_printf_next_spec_pos(info->strings, fmt_pos);
if (fmt_pos != -1 && info->strings[fmt_pos] == 's') {
unsigned idx = vtn_add_printf_string(b, w_src[i], info);
nir_store_deref(&b->nb, field_deref,
nir_imm_intN_t(&b->nb, idx, field_src->bit_size),
~0 /* write_mask */);
} else
nir_store_deref(&b->nb, field_deref, field_src, ~0);
}
/* Lastly, the actual intrinsic */
nir_ssa_def *fmt_idx = nir_imm_int(&b->nb, info_idx);
nir_ssa_def *ret = nir_printf(&b->nb, fmt_idx, &deref_var->dest.ssa);
vtn_push_nir_ssa(b, w_dest[1], ret);
} }
static nir_ssa_def * static nir_ssa_def *
@@ -977,7 +1080,7 @@ vtn_handle_opencl_instruction(struct vtn_builder *b, SpvOp ext_opcode,
handle_instr(b, ext_opcode, w + 5, count - 5, w + 1, handle_round); handle_instr(b, ext_opcode, w + 5, count - 5, w + 1, handle_round);
return true; return true;
case OpenCLstd_Printf: case OpenCLstd_Printf:
handle_instr(b, ext_opcode, w + 5, count - 5, w + 1, handle_printf); handle_printf(b, ext_opcode, w + 5, count - 5, w + 1);
return true; return true;
case OpenCLstd_Prefetch: case OpenCLstd_Prefetch:
/* TODO maybe add a nir instruction for this? */ /* TODO maybe add a nir instruction for this? */