blob: df6d02168bd9fb2084a4f031fa0d3b5285cd3bb2 [file] [log] [blame]
/*
* Copyright 2018 Collabora Ltd.
*
* 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
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, 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 (including the next
* paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS 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 "spirv_builder.h"
#include "util/macros.h"
#include "util/ralloc.h"
#include "util/u_bitcast.h"
#include "util/u_memory.h"
#include "util/hash_table.h"
#define XXH_INLINE_ALL
#include "util/xxhash.h"
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
static bool
spirv_buffer_grow(struct spirv_buffer *b, void *mem_ctx, size_t needed)
{
size_t new_room = MAX3(64, (b->room * 3) / 2, needed);
uint32_t *new_words = reralloc_size(mem_ctx, b->words,
new_room * sizeof(uint32_t));
if (!new_words)
return false;
b->words = new_words;
b->room = new_room;
return true;
}
static inline bool
spirv_buffer_prepare(struct spirv_buffer *b, void *mem_ctx, size_t needed)
{
needed += b->num_words;
if (b->room >= b->num_words + needed)
return true;
return spirv_buffer_grow(b, mem_ctx, needed);
}
static inline void
spirv_buffer_emit_word(struct spirv_buffer *b, uint32_t word)
{
assert(b->num_words < b->room);
b->words[b->num_words++] = word;
}
static int
spirv_buffer_emit_string(struct spirv_buffer *b, void *mem_ctx,
const char *str)
{
int pos = 0;
uint32_t word = 0;
while (str[pos] != '\0') {
word |= str[pos] << (8 * (pos % 4));
if (++pos % 4 == 0) {
spirv_buffer_prepare(b, mem_ctx, 1);
spirv_buffer_emit_word(b, word);
word = 0;
}
}
spirv_buffer_prepare(b, mem_ctx, 1);
spirv_buffer_emit_word(b, word);
return 1 + pos / 4;
}
void
spirv_builder_emit_cap(struct spirv_builder *b, SpvCapability cap)
{
spirv_buffer_prepare(&b->capabilities, b->mem_ctx, 2);
spirv_buffer_emit_word(&b->capabilities, SpvOpCapability | (2 << 16));
spirv_buffer_emit_word(&b->capabilities, cap);
}
void
spirv_builder_emit_extension(struct spirv_builder *b, const char *name)
{
size_t pos = b->extensions.num_words;
spirv_buffer_prepare(&b->extensions, b->mem_ctx, 1);
spirv_buffer_emit_word(&b->extensions, SpvOpExtension);
int len = spirv_buffer_emit_string(&b->extensions, b->mem_ctx, name);
b->extensions.words[pos] |= (1 + len) << 16;
}
void
spirv_builder_emit_source(struct spirv_builder *b, SpvSourceLanguage lang,
uint32_t version)
{
spirv_buffer_prepare(&b->debug_names, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->debug_names, SpvOpSource | (3 << 16));
spirv_buffer_emit_word(&b->debug_names, lang);
spirv_buffer_emit_word(&b->debug_names, version);
}
void
spirv_builder_emit_mem_model(struct spirv_builder *b,
SpvAddressingModel addr_model,
SpvMemoryModel mem_model)
{
spirv_buffer_prepare(&b->memory_model, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->memory_model, SpvOpMemoryModel | (3 << 16));
spirv_buffer_emit_word(&b->memory_model, addr_model);
spirv_buffer_emit_word(&b->memory_model, mem_model);
}
void
spirv_builder_emit_entry_point(struct spirv_builder *b,
SpvExecutionModel exec_model, SpvId entry_point,
const char *name, const SpvId interfaces[],
size_t num_interfaces)
{
size_t pos = b->entry_points.num_words;
spirv_buffer_prepare(&b->entry_points, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->entry_points, SpvOpEntryPoint);
spirv_buffer_emit_word(&b->entry_points, exec_model);
spirv_buffer_emit_word(&b->entry_points, entry_point);
int len = spirv_buffer_emit_string(&b->entry_points, b->mem_ctx, name);
b->entry_points.words[pos] |= (3 + len + num_interfaces) << 16;
spirv_buffer_prepare(&b->entry_points, b->mem_ctx, num_interfaces);
for (int i = 0; i < num_interfaces; ++i)
spirv_buffer_emit_word(&b->entry_points, interfaces[i]);
}
void
spirv_builder_emit_exec_mode_literal(struct spirv_builder *b, SpvId entry_point,
SpvExecutionMode exec_mode, uint32_t param)
{
spirv_buffer_prepare(&b->exec_modes, b->mem_ctx, 4);
spirv_buffer_emit_word(&b->exec_modes, SpvOpExecutionMode | (4 << 16));
spirv_buffer_emit_word(&b->exec_modes, entry_point);
spirv_buffer_emit_word(&b->exec_modes, exec_mode);
spirv_buffer_emit_word(&b->exec_modes, param);
}
void
spirv_builder_emit_exec_mode(struct spirv_builder *b, SpvId entry_point,
SpvExecutionMode exec_mode)
{
spirv_buffer_prepare(&b->exec_modes, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->exec_modes, SpvOpExecutionMode | (3 << 16));
spirv_buffer_emit_word(&b->exec_modes, entry_point);
spirv_buffer_emit_word(&b->exec_modes, exec_mode);
}
void
spirv_builder_emit_name(struct spirv_builder *b, SpvId target,
const char *name)
{
size_t pos = b->debug_names.num_words;
spirv_buffer_prepare(&b->debug_names, b->mem_ctx, 2);
spirv_buffer_emit_word(&b->debug_names, SpvOpName);
spirv_buffer_emit_word(&b->debug_names, target);
int len = spirv_buffer_emit_string(&b->debug_names, b->mem_ctx, name);
b->debug_names.words[pos] |= (2 + len) << 16;
}
static void
emit_decoration(struct spirv_builder *b, SpvId target,
SpvDecoration decoration, const uint32_t extra_operands[],
size_t num_extra_operands)
{
int words = 3 + num_extra_operands;
spirv_buffer_prepare(&b->decorations, b->mem_ctx, words);
spirv_buffer_emit_word(&b->decorations, SpvOpDecorate | (words << 16));
spirv_buffer_emit_word(&b->decorations, target);
spirv_buffer_emit_word(&b->decorations, decoration);
for (int i = 0; i < num_extra_operands; ++i)
spirv_buffer_emit_word(&b->decorations, extra_operands[i]);
}
void
spirv_builder_emit_decoration(struct spirv_builder *b, SpvId target,
SpvDecoration decoration)
{
emit_decoration(b, target, decoration, NULL, 0);
}
void
spirv_builder_emit_location(struct spirv_builder *b, SpvId target,
uint32_t location)
{
uint32_t args[] = { location };
emit_decoration(b, target, SpvDecorationLocation, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_component(struct spirv_builder *b, SpvId target,
uint32_t component)
{
uint32_t args[] = { component };
emit_decoration(b, target, SpvDecorationComponent, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_builtin(struct spirv_builder *b, SpvId target,
SpvBuiltIn builtin)
{
uint32_t args[] = { builtin };
emit_decoration(b, target, SpvDecorationBuiltIn, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_vertex(struct spirv_builder *b)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 1);
spirv_buffer_emit_word(&b->instructions, SpvOpEmitVertex | (1 << 16));
}
void
spirv_builder_end_primitive(struct spirv_builder *b)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 1);
spirv_buffer_emit_word(&b->instructions, SpvOpEndPrimitive | (1 << 16));
}
void
spirv_builder_emit_descriptor_set(struct spirv_builder *b, SpvId target,
uint32_t descriptor_set)
{
uint32_t args[] = { descriptor_set };
emit_decoration(b, target, SpvDecorationDescriptorSet, args,
ARRAY_SIZE(args));
}
void
spirv_builder_emit_binding(struct spirv_builder *b, SpvId target,
uint32_t binding)
{
uint32_t args[] = { binding };
emit_decoration(b, target, SpvDecorationBinding, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_array_stride(struct spirv_builder *b, SpvId target,
uint32_t stride)
{
uint32_t args[] = { stride };
emit_decoration(b, target, SpvDecorationArrayStride, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_offset(struct spirv_builder *b, SpvId target,
uint32_t offset)
{
uint32_t args[] = { offset };
emit_decoration(b, target, SpvDecorationOffset, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_xfb_buffer(struct spirv_builder *b, SpvId target,
uint32_t buffer)
{
uint32_t args[] = { buffer };
emit_decoration(b, target, SpvDecorationXfbBuffer, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_xfb_stride(struct spirv_builder *b, SpvId target,
uint32_t stride)
{
uint32_t args[] = { stride };
emit_decoration(b, target, SpvDecorationXfbStride, args, ARRAY_SIZE(args));
}
void
spirv_builder_emit_index(struct spirv_builder *b, SpvId target, int index)
{
uint32_t args[] = { index };
emit_decoration(b, target, SpvDecorationIndex, args, ARRAY_SIZE(args));
}
static void
emit_member_decoration(struct spirv_builder *b, SpvId target, uint32_t member,
SpvDecoration decoration, const uint32_t extra_operands[],
size_t num_extra_operands)
{
int words = 4 + num_extra_operands;
spirv_buffer_prepare(&b->decorations, b->mem_ctx, words);
spirv_buffer_emit_word(&b->decorations,
SpvOpMemberDecorate | (words << 16));
spirv_buffer_emit_word(&b->decorations, target);
spirv_buffer_emit_word(&b->decorations, member);
spirv_buffer_emit_word(&b->decorations, decoration);
for (int i = 0; i < num_extra_operands; ++i)
spirv_buffer_emit_word(&b->decorations, extra_operands[i]);
}
void
spirv_builder_emit_member_offset(struct spirv_builder *b, SpvId target,
uint32_t member, uint32_t offset)
{
uint32_t args[] = { offset };
emit_member_decoration(b, target, member, SpvDecorationOffset,
args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_emit_undef(struct spirv_builder *b, SpvId result_type)
{
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->instructions, SpvOpUndef | (3 << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
return result;
}
void
spirv_builder_function(struct spirv_builder *b, SpvId result,
SpvId return_type,
SpvFunctionControlMask function_control,
SpvId function_type)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 5);
spirv_buffer_emit_word(&b->instructions, SpvOpFunction | (5 << 16));
spirv_buffer_emit_word(&b->instructions, return_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, function_control);
spirv_buffer_emit_word(&b->instructions, function_type);
}
void
spirv_builder_function_end(struct spirv_builder *b)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 1);
spirv_buffer_emit_word(&b->instructions, SpvOpFunctionEnd | (1 << 16));
}
void
spirv_builder_label(struct spirv_builder *b, SpvId label)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 2);
spirv_buffer_emit_word(&b->instructions, SpvOpLabel | (2 << 16));
spirv_buffer_emit_word(&b->instructions, label);
}
void
spirv_builder_return(struct spirv_builder *b)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 1);
spirv_buffer_emit_word(&b->instructions, SpvOpReturn | (1 << 16));
}
SpvId
spirv_builder_emit_load(struct spirv_builder *b, SpvId result_type,
SpvId pointer)
{
return spirv_builder_emit_unop(b, SpvOpLoad, result_type, pointer);
}
void
spirv_builder_emit_store(struct spirv_builder *b, SpvId pointer, SpvId object)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->instructions, SpvOpStore | (3 << 16));
spirv_buffer_emit_word(&b->instructions, pointer);
spirv_buffer_emit_word(&b->instructions, object);
}
SpvId
spirv_builder_emit_access_chain(struct spirv_builder *b, SpvId result_type,
SpvId base, const SpvId indexes[],
size_t num_indexes)
{
assert(base);
assert(result_type);
SpvId result = spirv_builder_new_id(b);
int words = 4 + num_indexes;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, SpvOpAccessChain | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, base);
for (int i = 0; i < num_indexes; ++i)
spirv_buffer_emit_word(&b->instructions, indexes[i]);
return result;
}
SpvId
spirv_builder_emit_unop(struct spirv_builder *b, SpvOp op, SpvId result_type,
SpvId operand)
{
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 4);
spirv_buffer_emit_word(&b->instructions, op | (4 << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, operand);
return result;
}
SpvId
spirv_builder_emit_binop(struct spirv_builder *b, SpvOp op, SpvId result_type,
SpvId operand0, SpvId operand1)
{
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 5);
spirv_buffer_emit_word(&b->instructions, op | (5 << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, operand0);
spirv_buffer_emit_word(&b->instructions, operand1);
return result;
}
SpvId
spirv_builder_emit_triop(struct spirv_builder *b, SpvOp op, SpvId result_type,
SpvId operand0, SpvId operand1, SpvId operand2)
{
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 6);
spirv_buffer_emit_word(&b->instructions, op | (6 << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, operand0);
spirv_buffer_emit_word(&b->instructions, operand1);
spirv_buffer_emit_word(&b->instructions, operand2);
return result;
}
SpvId
spirv_builder_emit_quadop(struct spirv_builder *b, SpvOp op, SpvId result_type,
SpvId operand0, SpvId operand1, SpvId operand2, SpvId operand3)
{
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 7);
spirv_buffer_emit_word(&b->instructions, op | (7 << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, operand0);
spirv_buffer_emit_word(&b->instructions, operand1);
spirv_buffer_emit_word(&b->instructions, operand2);
spirv_buffer_emit_word(&b->instructions, operand3);
return result;
}
SpvId
spirv_builder_emit_composite_extract(struct spirv_builder *b, SpvId result_type,
SpvId composite, const uint32_t indexes[],
size_t num_indexes)
{
SpvId result = spirv_builder_new_id(b);
assert(num_indexes > 0);
int words = 4 + num_indexes;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions,
SpvOpCompositeExtract | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, composite);
for (int i = 0; i < num_indexes; ++i)
spirv_buffer_emit_word(&b->instructions, indexes[i]);
return result;
}
SpvId
spirv_builder_emit_composite_construct(struct spirv_builder *b,
SpvId result_type,
const SpvId constituents[],
size_t num_constituents)
{
SpvId result = spirv_builder_new_id(b);
assert(num_constituents > 0);
int words = 3 + num_constituents;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions,
SpvOpCompositeConstruct | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
for (int i = 0; i < num_constituents; ++i)
spirv_buffer_emit_word(&b->instructions, constituents[i]);
return result;
}
SpvId
spirv_builder_emit_vector_shuffle(struct spirv_builder *b, SpvId result_type,
SpvId vector_1, SpvId vector_2,
const uint32_t components[],
size_t num_components)
{
SpvId result = spirv_builder_new_id(b);
assert(num_components > 0);
int words = 5 + num_components;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, SpvOpVectorShuffle | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, vector_1);
spirv_buffer_emit_word(&b->instructions, vector_2);
for (int i = 0; i < num_components; ++i)
spirv_buffer_emit_word(&b->instructions, components[i]);
return result;
}
SpvId
spirv_builder_emit_vector_extract(struct spirv_builder *b, SpvId result_type,
SpvId vector_1,
uint32_t component)
{
SpvId result = spirv_builder_new_id(b);
int words = 5;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, SpvOpVectorExtractDynamic | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, vector_1);
spirv_buffer_emit_word(&b->instructions, spirv_builder_const_uint(b, 32, component));
return result;
}
SpvId
spirv_builder_emit_vector_insert(struct spirv_builder *b, SpvId result_type,
SpvId vector_1,
SpvId component,
uint32_t index)
{
SpvId result = spirv_builder_new_id(b);
int words = 6;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, SpvOpVectorInsertDynamic | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, vector_1);
spirv_buffer_emit_word(&b->instructions, component);
spirv_buffer_emit_word(&b->instructions, spirv_builder_const_uint(b, 32, index));
return result;
}
void
spirv_builder_emit_branch(struct spirv_builder *b, SpvId label)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 2);
spirv_buffer_emit_word(&b->instructions, SpvOpBranch | (2 << 16));
spirv_buffer_emit_word(&b->instructions, label);
}
void
spirv_builder_emit_selection_merge(struct spirv_builder *b, SpvId merge_block,
SpvSelectionControlMask selection_control)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->instructions, SpvOpSelectionMerge | (3 << 16));
spirv_buffer_emit_word(&b->instructions, merge_block);
spirv_buffer_emit_word(&b->instructions, selection_control);
}
void
spirv_builder_loop_merge(struct spirv_builder *b, SpvId merge_block,
SpvId cont_target, SpvLoopControlMask loop_control)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 4);
spirv_buffer_emit_word(&b->instructions, SpvOpLoopMerge | (4 << 16));
spirv_buffer_emit_word(&b->instructions, merge_block);
spirv_buffer_emit_word(&b->instructions, cont_target);
spirv_buffer_emit_word(&b->instructions, loop_control);
}
void
spirv_builder_emit_branch_conditional(struct spirv_builder *b, SpvId condition,
SpvId true_label, SpvId false_label)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 4);
spirv_buffer_emit_word(&b->instructions, SpvOpBranchConditional | (4 << 16));
spirv_buffer_emit_word(&b->instructions, condition);
spirv_buffer_emit_word(&b->instructions, true_label);
spirv_buffer_emit_word(&b->instructions, false_label);
}
SpvId
spirv_builder_emit_phi(struct spirv_builder *b, SpvId result_type,
size_t num_vars, size_t *position)
{
SpvId result = spirv_builder_new_id(b);
assert(num_vars > 0);
int words = 3 + 2 * num_vars;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, SpvOpPhi | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
*position = b->instructions.num_words;
for (int i = 0; i < 2 * num_vars; ++i)
spirv_buffer_emit_word(&b->instructions, 0);
return result;
}
void
spirv_builder_set_phi_operand(struct spirv_builder *b, size_t position,
size_t index, SpvId variable, SpvId parent)
{
b->instructions.words[position + index * 2 + 0] = variable;
b->instructions.words[position + index * 2 + 1] = parent;
}
void
spirv_builder_emit_kill(struct spirv_builder *b)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 1);
spirv_buffer_emit_word(&b->instructions, SpvOpKill | (1 << 16));
}
SpvId
spirv_builder_emit_image_sample(struct spirv_builder *b,
SpvId result_type,
SpvId sampled_image,
SpvId coordinate,
bool proj,
SpvId lod,
SpvId bias,
SpvId dref,
SpvId dx,
SpvId dy,
SpvId offset)
{
SpvId result = spirv_builder_new_id(b);
int opcode = SpvOpImageSampleImplicitLod;
int operands = 5;
if (proj)
opcode += SpvOpImageSampleProjImplicitLod - SpvOpImageSampleImplicitLod;
if (lod || (dx && dy))
opcode += SpvOpImageSampleExplicitLod - SpvOpImageSampleImplicitLod;
if (dref) {
opcode += SpvOpImageSampleDrefImplicitLod - SpvOpImageSampleImplicitLod;
operands++;
}
SpvImageOperandsMask operand_mask = SpvImageOperandsMaskNone;
SpvId extra_operands[5];
int num_extra_operands = 0;
if (bias) {
extra_operands[++num_extra_operands] = bias;
operand_mask |= SpvImageOperandsBiasMask;
}
if (lod) {
extra_operands[++num_extra_operands] = lod;
operand_mask |= SpvImageOperandsLodMask;
} else if (dx && dy) {
extra_operands[++num_extra_operands] = dx;
extra_operands[++num_extra_operands] = dy;
operand_mask |= SpvImageOperandsGradMask;
}
if (offset) {
extra_operands[++num_extra_operands] = offset;
operand_mask |= SpvImageOperandsOffsetMask;
}
/* finalize num_extra_operands / extra_operands */
if (num_extra_operands > 0) {
extra_operands[0] = operand_mask;
num_extra_operands++;
}
spirv_buffer_prepare(&b->instructions, b->mem_ctx, operands + num_extra_operands);
spirv_buffer_emit_word(&b->instructions, opcode | ((operands + num_extra_operands) << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, sampled_image);
spirv_buffer_emit_word(&b->instructions, coordinate);
if (dref)
spirv_buffer_emit_word(&b->instructions, dref);
for (int i = 0; i < num_extra_operands; ++i)
spirv_buffer_emit_word(&b->instructions, extra_operands[i]);
return result;
}
SpvId
spirv_builder_emit_image(struct spirv_builder *b, SpvId result_type,
SpvId sampled_image)
{
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 4);
spirv_buffer_emit_word(&b->instructions, SpvOpImage | (4 << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, sampled_image);
return result;
}
SpvId
spirv_builder_emit_image_fetch(struct spirv_builder *b,
SpvId result_type,
SpvId image,
SpvId coordinate,
SpvId lod,
SpvId sample)
{
SpvId result = spirv_builder_new_id(b);
SpvImageOperandsMask operand_mask = SpvImageOperandsMaskNone;
SpvId extra_operands[3];
int num_extra_operands = 0;
if (lod) {
extra_operands[++num_extra_operands] = lod;
operand_mask |= SpvImageOperandsLodMask;
}
if (sample) {
extra_operands[++num_extra_operands] = sample;
operand_mask |= SpvImageOperandsSampleMask;
}
/* finalize num_extra_operands / extra_operands */
if (num_extra_operands > 0) {
extra_operands[0] = operand_mask;
num_extra_operands++;
}
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 5 + num_extra_operands);
spirv_buffer_emit_word(&b->instructions, SpvOpImageFetch |
((5 + num_extra_operands) << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, image);
spirv_buffer_emit_word(&b->instructions, coordinate);
for (int i = 0; i < num_extra_operands; ++i)
spirv_buffer_emit_word(&b->instructions, extra_operands[i]);
return result;
}
SpvId
spirv_builder_emit_image_query_size(struct spirv_builder *b,
SpvId result_type,
SpvId image,
SpvId lod)
{
int opcode = SpvOpImageQuerySize;
int words = 4;
if (lod) {
words++;
opcode = SpvOpImageQuerySizeLod;
}
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, opcode | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, image);
if (lod)
spirv_buffer_emit_word(&b->instructions, lod);
return result;
}
SpvId
spirv_builder_emit_image_query_lod(struct spirv_builder *b,
SpvId result_type,
SpvId image,
SpvId coords)
{
int opcode = SpvOpImageQueryLod;
int words = 5;
SpvId result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, opcode | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, image);
spirv_buffer_emit_word(&b->instructions, coords);
return result;
}
SpvId
spirv_builder_emit_ext_inst(struct spirv_builder *b, SpvId result_type,
SpvId set, uint32_t instruction,
const SpvId *args, size_t num_args)
{
SpvId result = spirv_builder_new_id(b);
int words = 5 + num_args;
spirv_buffer_prepare(&b->instructions, b->mem_ctx, words);
spirv_buffer_emit_word(&b->instructions, SpvOpExtInst | (words << 16));
spirv_buffer_emit_word(&b->instructions, result_type);
spirv_buffer_emit_word(&b->instructions, result);
spirv_buffer_emit_word(&b->instructions, set);
spirv_buffer_emit_word(&b->instructions, instruction);
for (int i = 0; i < num_args; ++i)
spirv_buffer_emit_word(&b->instructions, args[i]);
return result;
}
struct spirv_type {
SpvOp op;
uint32_t args[8];
size_t num_args;
SpvId type;
};
static uint32_t
non_aggregate_type_hash(const void *arg)
{
const struct spirv_type *type = arg;
uint32_t hash = 0;
hash = XXH32(&type->op, sizeof(type->op), hash);
hash = XXH32(type->args, sizeof(uint32_t) * type->num_args, hash);
return hash;
}
static bool
non_aggregate_type_equals(const void *a, const void *b)
{
const struct spirv_type *ta = a, *tb = b;
if (ta->op != tb->op)
return false;
assert(ta->num_args == tb->num_args);
return memcmp(ta->args, tb->args, sizeof(uint32_t) * ta->num_args) == 0;
}
static SpvId
get_type_def(struct spirv_builder *b, SpvOp op, const uint32_t args[],
size_t num_args)
{
/* According to the SPIR-V specification:
*
* "Two different type <id>s form, by definition, two different types. It
* is valid to declare multiple aggregate type <id>s having the same
* opcode and operands. This is to allow multiple instances of aggregate
* types with the same structure to be decorated differently. (Different
* decorations are not required; two different aggregate type <id>s are
* allowed to have identical declarations and decorations, and will still
* be two different types.) Non-aggregate types are different: It is
* invalid to declare multiple type <id>s for the same scalar, vector, or
* matrix type. That is, non-aggregate type declarations must all have
* different opcodes or operands. (Note that non-aggregate types cannot
* be decorated in ways that affect their type.)"
*
* ..so, we need to prevent the same non-aggregate type to be re-defined
* with a new <id>. We do this by putting the definitions in a hash-map, so
* we can easily look up and reuse them.
*/
struct spirv_type key;
assert(num_args <= ARRAY_SIZE(key.args));
key.op = op;
memcpy(&key.args, args, sizeof(uint32_t) * num_args);
key.num_args = num_args;
struct hash_entry *entry;
if (b->types) {
entry = _mesa_hash_table_search(b->types, &key);
if (entry)
return ((struct spirv_type *)entry->data)->type;
} else {
b->types = _mesa_hash_table_create(b->mem_ctx,
non_aggregate_type_hash,
non_aggregate_type_equals);
assert(b->types);
}
struct spirv_type *type = rzalloc(b->mem_ctx, struct spirv_type);
if (!type)
return 0;
type->op = op;
memcpy(&type->args, args, sizeof(uint32_t) * num_args);
type->num_args = num_args;
type->type = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->types_const_defs, b->mem_ctx, 2 + num_args);
spirv_buffer_emit_word(&b->types_const_defs, op | ((2 + num_args) << 16));
spirv_buffer_emit_word(&b->types_const_defs, type->type);
for (int i = 0; i < num_args; ++i)
spirv_buffer_emit_word(&b->types_const_defs, args[i]);
entry = _mesa_hash_table_insert(b->types, type, type);
assert(entry);
return ((struct spirv_type *)entry->data)->type;
}
SpvId
spirv_builder_type_void(struct spirv_builder *b)
{
return get_type_def(b, SpvOpTypeVoid, NULL, 0);
}
SpvId
spirv_builder_type_bool(struct spirv_builder *b)
{
return get_type_def(b, SpvOpTypeBool, NULL, 0);
}
SpvId
spirv_builder_type_int(struct spirv_builder *b, unsigned width)
{
uint32_t args[] = { width, 1 };
return get_type_def(b, SpvOpTypeInt, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_uint(struct spirv_builder *b, unsigned width)
{
uint32_t args[] = { width, 0 };
return get_type_def(b, SpvOpTypeInt, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_float(struct spirv_builder *b, unsigned width)
{
uint32_t args[] = { width };
return get_type_def(b, SpvOpTypeFloat, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_image(struct spirv_builder *b, SpvId sampled_type,
SpvDim dim, bool depth, bool arrayed, bool ms,
unsigned sampled, SpvImageFormat image_format)
{
assert(sampled < 3);
uint32_t args[] = {
sampled_type, dim, depth ? 1 : 0, arrayed ? 1 : 0, ms ? 1 : 0, sampled,
image_format
};
return get_type_def(b, SpvOpTypeImage, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_sampled_image(struct spirv_builder *b, SpvId image_type)
{
uint32_t args[] = { image_type };
return get_type_def(b, SpvOpTypeSampledImage, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_pointer(struct spirv_builder *b,
SpvStorageClass storage_class, SpvId type)
{
uint32_t args[] = { storage_class, type };
return get_type_def(b, SpvOpTypePointer, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_vector(struct spirv_builder *b, SpvId component_type,
unsigned component_count)
{
assert(component_count > 1);
uint32_t args[] = { component_type, component_count };
return get_type_def(b, SpvOpTypeVector, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_matrix(struct spirv_builder *b, SpvId component_type,
unsigned component_count)
{
assert(component_count > 1);
uint32_t args[] = { component_type, component_count };
return get_type_def(b, SpvOpTypeMatrix, args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_type_array(struct spirv_builder *b, SpvId component_type,
SpvId length)
{
SpvId type = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->types_const_defs, b->mem_ctx, 4);
spirv_buffer_emit_word(&b->types_const_defs, SpvOpTypeArray | (4 << 16));
spirv_buffer_emit_word(&b->types_const_defs, type);
spirv_buffer_emit_word(&b->types_const_defs, component_type);
spirv_buffer_emit_word(&b->types_const_defs, length);
return type;
}
SpvId
spirv_builder_type_struct(struct spirv_builder *b, const SpvId member_types[],
size_t num_member_types)
{
int words = 2 + num_member_types;
SpvId type = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->types_const_defs, b->mem_ctx, words);
spirv_buffer_emit_word(&b->types_const_defs, SpvOpTypeStruct | (words << 16));
spirv_buffer_emit_word(&b->types_const_defs, type);
for (int i = 0; i < num_member_types; ++i)
spirv_buffer_emit_word(&b->types_const_defs, member_types[i]);
return type;
}
SpvId
spirv_builder_type_function(struct spirv_builder *b, SpvId return_type,
const SpvId parameter_types[],
size_t num_parameter_types)
{
int words = 3 + num_parameter_types;
SpvId type = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->types_const_defs, b->mem_ctx, words);
spirv_buffer_emit_word(&b->types_const_defs, SpvOpTypeFunction | (words << 16));
spirv_buffer_emit_word(&b->types_const_defs, type);
spirv_buffer_emit_word(&b->types_const_defs, return_type);
for (int i = 0; i < num_parameter_types; ++i)
spirv_buffer_emit_word(&b->types_const_defs, parameter_types[i]);
return type;
}
struct spirv_const {
SpvOp op, type;
uint32_t args[8];
size_t num_args;
SpvId result;
};
static uint32_t
const_hash(const void *arg)
{
const struct spirv_const *key = arg;
uint32_t hash = 0;
hash = XXH32(&key->op, sizeof(key->op), hash);
hash = XXH32(&key->type, sizeof(key->type), hash);
hash = XXH32(key->args, sizeof(uint32_t) * key->num_args, hash);
return hash;
}
static bool
const_equals(const void *a, const void *b)
{
const struct spirv_const *ca = a, *cb = b;
if (ca->op != cb->op ||
ca->type != cb->type)
return false;
assert(ca->num_args == cb->num_args);
return memcmp(ca->args, cb->args, sizeof(uint32_t) * ca->num_args) == 0;
}
static SpvId
get_const_def(struct spirv_builder *b, SpvOp op, SpvId type,
const uint32_t args[], size_t num_args)
{
struct spirv_const key;
assert(num_args <= ARRAY_SIZE(key.args));
key.op = op;
key.type = type;
memcpy(&key.args, args, sizeof(uint32_t) * num_args);
key.num_args = num_args;
struct hash_entry *entry;
if (b->consts) {
entry = _mesa_hash_table_search(b->consts, &key);
if (entry)
return ((struct spirv_const *)entry->data)->result;
} else {
b->consts = _mesa_hash_table_create(b->mem_ctx, const_hash,
const_equals);
assert(b->consts);
}
struct spirv_const *cnst = rzalloc(b->mem_ctx, struct spirv_const);
if (!cnst)
return 0;
cnst->op = op;
cnst->type = type;
memcpy(&cnst->args, args, sizeof(uint32_t) * num_args);
cnst->num_args = num_args;
cnst->result = spirv_builder_new_id(b);
spirv_buffer_prepare(&b->types_const_defs, b->mem_ctx, 3 + num_args);
spirv_buffer_emit_word(&b->types_const_defs, op | ((3 + num_args) << 16));
spirv_buffer_emit_word(&b->types_const_defs, type);
spirv_buffer_emit_word(&b->types_const_defs, cnst->result);
for (int i = 0; i < num_args; ++i)
spirv_buffer_emit_word(&b->types_const_defs, args[i]);
entry = _mesa_hash_table_insert(b->consts, cnst, cnst);
assert(entry);
return ((struct spirv_const *)entry->data)->result;
}
SpvId
spirv_builder_const_bool(struct spirv_builder *b, bool val)
{
return get_const_def(b, val ? SpvOpConstantTrue : SpvOpConstantFalse,
spirv_builder_type_bool(b), NULL, 0);
}
SpvId
spirv_builder_const_int(struct spirv_builder *b, int width, int32_t val)
{
assert(width <= 32);
uint32_t args[] = { val };
return get_const_def(b, SpvOpConstant, spirv_builder_type_int(b, width),
args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_const_uint(struct spirv_builder *b, int width, uint32_t val)
{
assert(width <= 32);
uint32_t args[] = { val };
return get_const_def(b, SpvOpConstant, spirv_builder_type_uint(b, width),
args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_const_float(struct spirv_builder *b, int width, float val)
{
assert(width <= 32);
uint32_t args[] = { u_bitcast_f2u(val) };
return get_const_def(b, SpvOpConstant, spirv_builder_type_float(b, width),
args, ARRAY_SIZE(args));
}
SpvId
spirv_builder_const_composite(struct spirv_builder *b, SpvId result_type,
const SpvId constituents[],
size_t num_constituents)
{
return get_const_def(b, SpvOpConstantComposite, result_type,
(const uint32_t *)constituents,
num_constituents);
}
SpvId
spirv_builder_emit_var(struct spirv_builder *b, SpvId type,
SpvStorageClass storage_class)
{
assert(storage_class != SpvStorageClassGeneric);
struct spirv_buffer *buf = storage_class != SpvStorageClassFunction ?
&b->types_const_defs : &b->instructions;
SpvId ret = spirv_builder_new_id(b);
spirv_buffer_prepare(buf, b->mem_ctx, 4);
spirv_buffer_emit_word(buf, SpvOpVariable | (4 << 16));
spirv_buffer_emit_word(buf, type);
spirv_buffer_emit_word(buf, ret);
spirv_buffer_emit_word(buf, storage_class);
return ret;
}
void
spirv_builder_emit_memory_barrier(struct spirv_builder *b, SpvScope scope, SpvMemorySemanticsMask semantics)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 3);
spirv_buffer_emit_word(&b->instructions, SpvOpMemoryBarrier | (3 << 16));
spirv_buffer_emit_word(&b->instructions, spirv_builder_const_uint(b, 32, scope));
spirv_buffer_emit_word(&b->instructions, spirv_builder_const_uint(b, 32, semantics));
}
void
spirv_builder_emit_control_barrier(struct spirv_builder *b, SpvScope scope, SpvScope mem_scope, SpvMemorySemanticsMask semantics)
{
spirv_buffer_prepare(&b->instructions, b->mem_ctx, 4);
spirv_buffer_emit_word(&b->instructions, SpvOpControlBarrier | (4 << 16));
spirv_buffer_emit_word(&b->instructions, spirv_builder_const_uint(b, 32, scope));
spirv_buffer_emit_word(&b->instructions, spirv_builder_const_uint(b, 32, mem_scope));
spirv_buffer_emit_word(&b->instructions, spirv_builder_const_uint(b, 32, semantics));
}
SpvId
spirv_builder_import(struct spirv_builder *b, const char *name)
{
SpvId result = spirv_builder_new_id(b);
size_t pos = b->imports.num_words;
spirv_buffer_prepare(&b->imports, b->mem_ctx, 2);
spirv_buffer_emit_word(&b->imports, SpvOpExtInstImport);
spirv_buffer_emit_word(&b->imports, result);
int len = spirv_buffer_emit_string(&b->imports, b->mem_ctx, name);
b->imports.words[pos] |= (2 + len) << 16;
return result;
}
size_t
spirv_builder_get_num_words(struct spirv_builder *b)
{
const size_t header_size = 5;
return header_size +
b->capabilities.num_words +
b->extensions.num_words +
b->imports.num_words +
b->memory_model.num_words +
b->entry_points.num_words +
b->exec_modes.num_words +
b->debug_names.num_words +
b->decorations.num_words +
b->types_const_defs.num_words +
b->instructions.num_words;
}
size_t
spirv_builder_get_words(struct spirv_builder *b, uint32_t *words,
size_t num_words)
{
assert(num_words >= spirv_builder_get_num_words(b));
size_t written = 0;
words[written++] = SpvMagicNumber;
words[written++] = 0x00010000;
words[written++] = 0;
words[written++] = b->prev_id + 1;
words[written++] = 0;
const struct spirv_buffer *buffers[] = {
&b->capabilities,
&b->extensions,
&b->imports,
&b->memory_model,
&b->entry_points,
&b->exec_modes,
&b->debug_names,
&b->decorations,
&b->types_const_defs,
&b->instructions
};
for (int i = 0; i < ARRAY_SIZE(buffers); ++i) {
const struct spirv_buffer *buffer = buffers[i];
for (int j = 0; j < buffer->num_words; ++j)
words[written++] = buffer->words[j];
}
assert(written == spirv_builder_get_num_words(b));
return written;
}