#include "util/u_math.h"
#include <stdio.h>
+#if UTIL_ARCH_BIG_ENDIAN
+#include <byteswap.h>
+#endif
void
vtn_log(struct vtn_builder *b, enum nir_spirv_debug_level level,
return si;
}
-static char *
+static const char *
vtn_string_literal(struct vtn_builder *b, const uint32_t *words,
unsigned word_count, unsigned *words_used)
{
- char *dup = ralloc_strndup(b, (char *)words, word_count * sizeof(*words));
- if (words_used) {
- /* Ammount of space taken by the string (including the null) */
- unsigned len = strlen(dup) + 1;
- *words_used = DIV_ROUND_UP(len, sizeof(*words));
+ /* From the SPIR-V spec:
+ *
+ * "A string is interpreted as a nul-terminated stream of characters.
+ * The character set is Unicode in the UTF-8 encoding scheme. The UTF-8
+ * octets (8-bit bytes) are packed four per word, following the
+ * little-endian convention (i.e., the first octet is in the
+ * lowest-order 8 bits of the word). The final word contains the
+ * string’s nul-termination character (0), and all contents past the
+ * end of the string in the final word are padded with 0."
+ *
+ * On big-endian, we need to byte-swap.
+ */
+#if UTIL_ARCH_BIG_ENDIAN
+ {
+ uint32_t *copy = ralloc_array(b, uint32_t, word_count);
+ for (unsigned i = 0; i < word_count; i++)
+ copy[i] = bswap_32(words[i]);
+ words = copy;
}
- return dup;
+#endif
+
+ const char *str = (char *)words;
+ const char *end = memchr(str, 0, word_count * 4);
+ vtn_fail_if(end == NULL, "String is not null-terminated");
+
+ if (words_used)
+ *words_used = DIV_ROUND_UP(end - str + 1, sizeof(*words));
+
+ return str;
}
const uint32_t *
vtn_handle_extension(struct vtn_builder *b, SpvOp opcode,
const uint32_t *w, unsigned count)
{
- const char *ext = (const char *)&w[2];
switch (opcode) {
case SpvOpExtInstImport: {
struct vtn_value *val = vtn_push_value(b, w[1], vtn_value_type_extension);
+ const char *ext = vtn_string_literal(b, &w[2], count - 2, NULL);
if (strcmp(ext, "GLSL.std.450") == 0) {
val->ext_handler = vtn_handle_glsl450_instruction;
} else if ((strcmp(ext, "SPV_AMD_gcn_shader") == 0)
val->constant = vtn_null_constant(b, val->type);
break;
- case SpvOpConstantSampler:
- vtn_fail("OpConstantSampler requires Kernel Capability");
- break;
-
default:
vtn_fail_with_opcode("Unhandled opcode", opcode);
}
vtn_foreach_decoration(b, val, handle_workgroup_size_decoration_cb, NULL);
}
-SpvMemorySemanticsMask
-vtn_storage_class_to_memory_semantics(SpvStorageClass sc)
-{
- switch (sc) {
- case SpvStorageClassStorageBuffer:
- case SpvStorageClassPhysicalStorageBuffer:
- return SpvMemorySemanticsUniformMemoryMask;
- case SpvStorageClassWorkgroup:
- return SpvMemorySemanticsWorkgroupMemoryMask;
- default:
- return SpvMemorySemanticsMaskNone;
- }
-}
-
static void
vtn_split_barrier_semantics(struct vtn_builder *b,
SpvMemorySemanticsMask semantics,
SpvMemorySemanticsOutputMemoryMask);
const SpvMemorySemanticsMask other_semantics =
- semantics & ~(order_semantics | av_vis_semantics | storage_semantics);
+ semantics & ~(order_semantics | av_vis_semantics | storage_semantics |
+ SpvMemorySemanticsVolatileMask);
if (other_semantics)
vtn_warn("Ignoring unhandled memory semantics: %u\n", other_semantics);
if (is_array && texop != nir_texop_lod)
coord_components++;
- coord = vtn_get_nir_ssa(b, w[idx++]);
+ struct vtn_ssa_value *coord_val = vtn_ssa_value(b, w[idx++]);
+ coord = coord_val->def;
p->src = nir_src_for_ssa(nir_channels(&b->nb, coord,
(1 << coord_components) - 1));
+
+ /* OpenCL allows integer sampling coordinates */
+ if (glsl_type_is_integer(coord_val->type) &&
+ opcode == SpvOpImageSampleExplicitLod) {
+ vtn_fail_if(b->shader->info.stage != MESA_SHADER_KERNEL,
+ "Unless the Kernel capability is being used, the coordinate parameter "
+ "OpImageSampleExplicitLod must be floating point.");
+
+ p->src = nir_src_for_ssa(nir_i2f32(&b->nb, p->src.ssa));
+ }
+
p->src_type = nir_tex_src_coord;
p++;
break;
if (sampler && (access & ACCESS_NON_UNIFORM))
instr->sampler_non_uniform = true;
- /* for non-query ops, get dest_type from sampler type */
+ /* for non-query ops, get dest_type from SPIR-V return type */
if (dest_type == nir_type_invalid) {
- switch (glsl_get_sampler_result_type(image->type)) {
+ /* the return type should match the image type, unless the image type is
+ * VOID (CL image), in which case the return type dictates the sampler
+ */
+ enum glsl_base_type sampler_base =
+ glsl_get_sampler_result_type(image->type);
+ enum glsl_base_type ret_base = glsl_get_base_type(ret_type->type);
+ vtn_fail_if(sampler_base != ret_base && sampler_base != GLSL_TYPE_VOID,
+ "SPIR-V return type mismatches image type. This is only valid "
+ "for untyped images (OpenCL).");
+ switch (ret_base) {
case GLSL_TYPE_FLOAT: dest_type = nir_type_float; break;
case GLSL_TYPE_INT: dest_type = nir_type_int; break;
case GLSL_TYPE_UINT: dest_type = nir_type_uint; break;
fill_common_atomic_sources(struct vtn_builder *b, SpvOp opcode,
const uint32_t *w, nir_src *src)
{
+ const struct glsl_type *type = vtn_get_type(b, w[1])->type;
+ unsigned bit_size = glsl_get_bit_size(type);
+
switch (opcode) {
case SpvOpAtomicIIncrement:
- src[0] = nir_src_for_ssa(nir_imm_int(&b->nb, 1));
+ src[0] = nir_src_for_ssa(nir_imm_intN_t(&b->nb, 1, bit_size));
break;
case SpvOpAtomicIDecrement:
- src[0] = nir_src_for_ssa(nir_imm_int(&b->nb, -1));
+ src[0] = nir_src_for_ssa(nir_imm_intN_t(&b->nb, -1, bit_size));
break;
case SpvOpAtomicISub:
access |= ACCESS_COHERENT;
break;
+ case SpvOpImageQuerySizeLod:
+ res_val = vtn_untyped_value(b, w[3]);
+ image.image = vtn_get_image(b, w[3]);
+ image.coord = NULL;
+ image.sample = NULL;
+ image.lod = vtn_ssa_value(b, w[4])->def;
+ break;
+
case SpvOpImageQuerySize:
res_val = vtn_untyped_value(b, w[3]);
image.image = vtn_get_image(b, w[3]);
image.lod = NULL;
break;
+ case SpvOpImageQueryFormat:
+ case SpvOpImageQueryOrder:
+ res_val = vtn_untyped_value(b, w[3]);
+ image.image = vtn_get_image(b, w[3]);
+ image.coord = NULL;
+ image.sample = NULL;
+ image.lod = NULL;
+ break;
+
case SpvOpImageRead: {
res_val = vtn_untyped_value(b, w[3]);
image.image = vtn_get_image(b, w[3]);
image.lod = nir_imm_int(&b->nb, 0);
}
- /* TODO: Volatile. */
+ if (operands & SpvImageOperandsVolatileTexelMask)
+ access |= ACCESS_VOLATILE;
break;
}
image.lod = nir_imm_int(&b->nb, 0);
}
- /* TODO: Volatile. */
+ if (operands & SpvImageOperandsVolatileTexelMask)
+ access |= ACCESS_VOLATILE;
break;
}
vtn_fail_with_opcode("Invalid image opcode", opcode);
}
+ if (semantics & SpvMemorySemanticsVolatileMask)
+ access |= ACCESS_VOLATILE;
+
nir_intrinsic_op op;
switch (opcode) {
#define OP(S, N) case SpvOp##S: op = nir_intrinsic_image_deref_##N; break;
OP(ImageQuerySize, size)
+ OP(ImageQuerySizeLod, size)
OP(ImageRead, load)
OP(ImageWrite, store)
OP(AtomicLoad, load)
OP(AtomicOr, atomic_or)
OP(AtomicXor, atomic_xor)
OP(AtomicFAddEXT, atomic_fadd)
+ OP(ImageQueryFormat, format)
+ OP(ImageQueryOrder, order)
#undef OP
default:
vtn_fail_with_opcode("Invalid image opcode", opcode);
intrin->src[0] = nir_src_for_ssa(&image.image->dest.ssa);
- if (opcode == SpvOpImageQuerySize) {
- /* ImageQuerySize only has an LOD which is currently always 0 */
- intrin->src[1] = nir_src_for_ssa(nir_imm_int(&b->nb, 0));
- } else {
+ switch (opcode) {
+ case SpvOpImageQuerySize:
+ case SpvOpImageQuerySizeLod:
+ case SpvOpImageQueryFormat:
+ case SpvOpImageQueryOrder:
+ break;
+ default:
/* The image coordinate is always 4 components but we may not have that
* many. Swizzle to compensate.
*/
intrin->src[1] = nir_src_for_ssa(expand_to_vec4(&b->nb, image.coord));
intrin->src[2] = nir_src_for_ssa(image.sample);
+ break;
}
/* The Vulkan spec says:
nir_intrinsic_set_access(intrin, access);
switch (opcode) {
- case SpvOpAtomicLoad:
+ case SpvOpImageQueryFormat:
+ case SpvOpImageQueryOrder:
+ /* No additional sources */
+ break;
case SpvOpImageQuerySize:
+ intrin->src[1] = nir_src_for_ssa(nir_imm_int(&b->nb, 0));
+ break;
+ case SpvOpImageQuerySizeLod:
+ intrin->src[1] = nir_src_for_ssa(image.lod);
+ break;
+ case SpvOpAtomicLoad:
case SpvOpImageRead:
- if (opcode == SpvOpImageRead || opcode == SpvOpAtomicLoad) {
- /* Only OpImageRead can support a lod parameter if
- * SPV_AMD_shader_image_load_store_lod is used but the current NIR
- * intrinsics definition for atomics requires us to set it for
- * OpAtomicLoad.
- */
- intrin->src[3] = nir_src_for_ssa(image.lod);
- }
+ /* Only OpImageRead can support a lod parameter if
+ * SPV_AMD_shader_image_load_store_lod is used but the current NIR
+ * intrinsics definition for atomics requires us to set it for
+ * OpAtomicLoad.
+ */
+ intrin->src[3] = nir_src_for_ssa(image.lod);
break;
case SpvOpAtomicStore:
case SpvOpImageWrite: {
SpvScope scope = SpvScopeInvocation;
SpvMemorySemanticsMask semantics = 0;
+ enum gl_access_qualifier access = 0;
switch (opcode) {
case SpvOpAtomicLoad:
vtn_fail_with_opcode("Invalid SPIR-V atomic", opcode);
}
+ if (semantics & SpvMemorySemanticsVolatileMask)
+ access |= ACCESS_VOLATILE;
+
/* uniform as "atomic counter uniform" */
if (ptr->mode == vtn_variable_mode_atomic_counter) {
nir_deref_instr *deref = vtn_pointer_to_deref(b, ptr);
nir_intrinsic_op op = get_ssbo_nir_atomic_op(b, opcode);
atomic = nir_intrinsic_instr_create(b->nb.shader, op);
- nir_intrinsic_set_access(atomic, ACCESS_COHERENT);
+ nir_intrinsic_set_access(atomic, access | ACCESS_COHERENT);
int src = 0;
switch (opcode) {
atomic->src[0] = nir_src_for_ssa(&deref->dest.ssa);
if (ptr->mode != vtn_variable_mode_workgroup)
- nir_intrinsic_set_access(atomic, ACCESS_COHERENT);
+ access |= ACCESS_COHERENT;
+
+ nir_intrinsic_set_access(atomic, access);
switch (opcode) {
case SpvOpAtomicLoad:
/* Atomic ordering operations will implicitly apply to the atomic operation
* storage class, so include that too.
*/
- semantics |= vtn_storage_class_to_memory_semantics(ptr->ptr_type->storage_class);
+ semantics |= vtn_mode_to_memory_semantics(ptr->mode);
SpvMemorySemanticsMask before_semantics;
SpvMemorySemanticsMask after_semantics;
spv_check_supported(kernel_image, cap);
break;
+ case SpvCapabilityLiteralSampler:
+ spv_check_supported(literal_sampler, cap);
+ break;
+
case SpvCapabilityImageReadWrite:
case SpvCapabilityImageMipmap:
case SpvCapabilityPipes:
case SpvCapabilityDeviceEnqueue:
- case SpvCapabilityLiteralSampler:
case SpvCapabilityGenericPointer:
vtn_warn("Unsupported OpenCL-style SPIR-V capability: %s",
spirv_capability_to_string(cap));
case SpvOpConstantFalse:
case SpvOpConstant:
case SpvOpConstantComposite:
- case SpvOpConstantSampler:
case SpvOpConstantNull:
case SpvOpSpecConstantTrue:
case SpvOpSpecConstantFalse:
case SpvOpUndef:
case SpvOpVariable:
+ case SpvOpConstantSampler:
vtn_handle_variables(b, opcode, w, count);
break;
case SpvOpImageFetch:
case SpvOpImageGather:
case SpvOpImageDrefGather:
- case SpvOpImageQuerySizeLod:
case SpvOpImageQueryLod:
case SpvOpImageQueryLevels:
case SpvOpImageQuerySamples:
case SpvOpImageRead:
case SpvOpImageWrite:
case SpvOpImageTexelPointer:
+ case SpvOpImageQueryFormat:
+ case SpvOpImageQueryOrder:
vtn_handle_image(b, opcode, w, count);
break;
+ case SpvOpImageQuerySizeLod:
case SpvOpImageQuerySize: {
struct vtn_type *image_type = vtn_get_value_type(b, w[3]);
vtn_assert(image_type->base_type == vtn_base_type_image);