* IN THE SOFTWARE.
*/
+#include <math.h>
#include "vtn_private.h"
+#include "spirv_info.h"
/*
* Normally, column vectors in SPIR-V correspond to a single NIR SSA
{
struct vtn_ssa_value *dest = vtn_create_ssa_value(b, mat->type);
for (unsigned i = 0; i < glsl_get_matrix_columns(mat->type); i++) {
- if (glsl_get_base_type(mat->type) == GLSL_TYPE_FLOAT)
- dest->elems[i]->def = nir_fmul(&b->nb, mat->elems[i]->def, scalar);
- else
+ if (glsl_base_type_is_integer(glsl_get_base_type(mat->type)))
dest->elems[i]->def = nir_imul(&b->nb, mat->elems[i]->def, scalar);
+ else
+ dest->elems[i]->def = nir_fmul(&b->nb, mat->elems[i]->def, scalar);
}
return dest;
}
break;
- default: unreachable("unknown matrix opcode");
+ default: vtn_fail_with_opcode("unknown matrix opcode", opcode);
}
}
nir_op
-vtn_nir_alu_op_for_spirv_opcode(SpvOp opcode, bool *swap)
+vtn_nir_alu_op_for_spirv_opcode(struct vtn_builder *b,
+ SpvOp opcode, bool *swap,
+ unsigned src_bit_size, unsigned dst_bit_size)
{
/* Indicates that the first two arguments should be swapped. This is
* used for implementing greater-than and less-than-or-equal.
case SpvOpBitReverse: return nir_op_bitfield_reverse;
case SpvOpBitCount: return nir_op_bit_count;
- /* Comparisons: (TODO: How do we want to handled ordered/unordered?) */
+ /* The ordered / unordered operators need special implementation besides
+ * the logical operator to use since they also need to check if operands are
+ * ordered.
+ */
case SpvOpFOrdEqual: return nir_op_feq;
case SpvOpFUnordEqual: return nir_op_feq;
case SpvOpINotEqual: return nir_op_ine;
case SpvOpFUnordGreaterThanEqual: return nir_op_fge;
/* Conversions: */
- case SpvOpConvertFToU: return nir_op_f2u;
- case SpvOpConvertFToS: return nir_op_f2i;
- case SpvOpConvertSToF: return nir_op_i2f;
- case SpvOpConvertUToF: return nir_op_u2f;
- case SpvOpBitcast: return nir_op_imov;
- case SpvOpUConvert:
case SpvOpQuantizeToF16: return nir_op_fquantize2f16;
- /* TODO: NIR is 32-bit only; these are no-ops. */
- case SpvOpSConvert: return nir_op_imov;
- case SpvOpFConvert: return nir_op_fmov;
-
+ case SpvOpUConvert:
+ case SpvOpConvertFToU:
+ case SpvOpConvertFToS:
+ case SpvOpConvertSToF:
+ case SpvOpConvertUToF:
+ case SpvOpSConvert:
+ case SpvOpFConvert: {
+ nir_alu_type src_type;
+ nir_alu_type dst_type;
+
+ switch (opcode) {
+ case SpvOpConvertFToS:
+ src_type = nir_type_float;
+ dst_type = nir_type_int;
+ break;
+ case SpvOpConvertFToU:
+ src_type = nir_type_float;
+ dst_type = nir_type_uint;
+ break;
+ case SpvOpFConvert:
+ src_type = dst_type = nir_type_float;
+ break;
+ case SpvOpConvertSToF:
+ src_type = nir_type_int;
+ dst_type = nir_type_float;
+ break;
+ case SpvOpSConvert:
+ src_type = dst_type = nir_type_int;
+ break;
+ case SpvOpConvertUToF:
+ src_type = nir_type_uint;
+ dst_type = nir_type_float;
+ break;
+ case SpvOpUConvert:
+ src_type = dst_type = nir_type_uint;
+ break;
+ default:
+ unreachable("Invalid opcode");
+ }
+ src_type |= src_bit_size;
+ dst_type |= dst_bit_size;
+ return nir_type_conversion_op(src_type, dst_type, nir_rounding_mode_undef);
+ }
/* Derivatives: */
case SpvOpDPdx: return nir_op_fddx;
case SpvOpDPdy: return nir_op_fddy;
case SpvOpDPdyCoarse: return nir_op_fddy_coarse;
default:
- unreachable("No NIR equivalent");
+ vtn_fail("No NIR equivalent: %u", opcode);
}
}
handle_no_contraction(struct vtn_builder *b, struct vtn_value *val, int member,
const struct vtn_decoration *dec, void *_void)
{
- assert(dec->scope == VTN_DEC_DECORATION);
+ vtn_assert(dec->scope == VTN_DEC_DECORATION);
if (dec->decoration != SpvDecorationNoContraction)
return;
b->nb.exact = true;
}
+static void
+handle_rounding_mode(struct vtn_builder *b, struct vtn_value *val, int member,
+ const struct vtn_decoration *dec, void *_out_rounding_mode)
+{
+ nir_rounding_mode *out_rounding_mode = _out_rounding_mode;
+ assert(dec->scope == VTN_DEC_DECORATION);
+ if (dec->decoration != SpvDecorationFPRoundingMode)
+ return;
+ switch (dec->operands[0]) {
+ case SpvFPRoundingModeRTE:
+ *out_rounding_mode = nir_rounding_mode_rtne;
+ break;
+ case SpvFPRoundingModeRTZ:
+ *out_rounding_mode = nir_rounding_mode_rtz;
+ break;
+ default:
+ unreachable("Not supported rounding mode");
+ break;
+ }
+}
+
+static void
+handle_no_wrap(struct vtn_builder *b, struct vtn_value *val, int member,
+ const struct vtn_decoration *dec, void *_alu)
+{
+ nir_alu_instr *alu = _alu;
+ switch (dec->decoration) {
+ case SpvDecorationNoSignedWrap:
+ alu->no_signed_wrap = true;
+ break;
+ case SpvDecorationNoUnsignedWrap:
+ alu->no_unsigned_wrap = true;
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+}
+
void
vtn_handle_alu(struct vtn_builder *b, SpvOp opcode,
const uint32_t *w, unsigned count)
if (glsl_type_is_matrix(vtn_src[0]->type) ||
(num_inputs >= 2 && glsl_type_is_matrix(vtn_src[1]->type))) {
vtn_handle_matrix_alu(b, opcode, val, vtn_src[0], vtn_src[1]);
- b->nb.exact = false;
+ b->nb.exact = b->exact;
return;
}
val->ssa = vtn_create_ssa_value(b, type);
nir_ssa_def *src[4] = { NULL, };
for (unsigned i = 0; i < num_inputs; i++) {
- assert(glsl_type_is_vector_or_scalar(vtn_src[i]->type));
+ vtn_assert(glsl_type_is_vector_or_scalar(vtn_src[i]->type));
src[i] = vtn_src[i]->def;
}
switch (opcode) {
case SpvOpAny:
if (src[0]->num_components == 1) {
- val->ssa->def = nir_imov(&b->nb, src[0]);
+ val->ssa->def = nir_mov(&b->nb, src[0]);
} else {
nir_op op;
switch (src[0]->num_components) {
case 2: op = nir_op_bany_inequal2; break;
case 3: op = nir_op_bany_inequal3; break;
case 4: op = nir_op_bany_inequal4; break;
+ default: vtn_fail("invalid number of components");
}
val->ssa->def = nir_build_alu(&b->nb, op, src[0],
- nir_imm_int(&b->nb, NIR_FALSE),
+ nir_imm_false(&b->nb),
NULL, NULL);
}
break;
case SpvOpAll:
if (src[0]->num_components == 1) {
- val->ssa->def = nir_imov(&b->nb, src[0]);
+ val->ssa->def = nir_mov(&b->nb, src[0]);
} else {
nir_op op;
switch (src[0]->num_components) {
case 2: op = nir_op_ball_iequal2; break;
case 3: op = nir_op_ball_iequal3; break;
case 4: op = nir_op_ball_iequal4; break;
+ default: vtn_fail("invalid number of components");
}
val->ssa->def = nir_build_alu(&b->nb, op, src[0],
- nir_imm_int(&b->nb, NIR_TRUE),
+ nir_imm_true(&b->nb),
NULL, NULL);
}
break;
break;
case SpvOpIAddCarry:
- assert(glsl_type_is_struct(val->ssa->type));
+ vtn_assert(glsl_type_is_struct_or_ifc(val->ssa->type));
val->ssa->elems[0]->def = nir_iadd(&b->nb, src[0], src[1]);
val->ssa->elems[1]->def = nir_uadd_carry(&b->nb, src[0], src[1]);
break;
case SpvOpISubBorrow:
- assert(glsl_type_is_struct(val->ssa->type));
+ vtn_assert(glsl_type_is_struct_or_ifc(val->ssa->type));
val->ssa->elems[0]->def = nir_isub(&b->nb, src[0], src[1]);
val->ssa->elems[1]->def = nir_usub_borrow(&b->nb, src[0], src[1]);
break;
- case SpvOpUMulExtended:
- assert(glsl_type_is_struct(val->ssa->type));
- val->ssa->elems[0]->def = nir_imul(&b->nb, src[0], src[1]);
- val->ssa->elems[1]->def = nir_umul_high(&b->nb, src[0], src[1]);
+ case SpvOpUMulExtended: {
+ vtn_assert(glsl_type_is_struct_or_ifc(val->ssa->type));
+ nir_ssa_def *umul = nir_umul_2x32_64(&b->nb, src[0], src[1]);
+ val->ssa->elems[0]->def = nir_unpack_64_2x32_split_x(&b->nb, umul);
+ val->ssa->elems[1]->def = nir_unpack_64_2x32_split_y(&b->nb, umul);
break;
+ }
- case SpvOpSMulExtended:
- assert(glsl_type_is_struct(val->ssa->type));
- val->ssa->elems[0]->def = nir_imul(&b->nb, src[0], src[1]);
- val->ssa->elems[1]->def = nir_imul_high(&b->nb, src[0], src[1]);
+ case SpvOpSMulExtended: {
+ vtn_assert(glsl_type_is_struct_or_ifc(val->ssa->type));
+ nir_ssa_def *smul = nir_imul_2x32_64(&b->nb, src[0], src[1]);
+ val->ssa->elems[0]->def = nir_unpack_64_2x32_split_x(&b->nb, smul);
+ val->ssa->elems[1]->def = nir_unpack_64_2x32_split_y(&b->nb, smul);
break;
+ }
case SpvOpFwidth:
val->ssa->def = nir_fadd(&b->nb,
val->ssa->def = nir_fne(&b->nb, src[0], src[0]);
break;
- case SpvOpIsInf:
- val->ssa->def = nir_feq(&b->nb, nir_fabs(&b->nb, src[0]),
- nir_imm_float(&b->nb, INFINITY));
+ case SpvOpIsInf: {
+ nir_ssa_def *inf = nir_imm_floatN_t(&b->nb, INFINITY, src[0]->bit_size);
+ val->ssa->def = nir_ieq(&b->nb, nir_fabs(&b->nb, src[0]), inf);
+ break;
+ }
+
+ case SpvOpFUnordEqual:
+ case SpvOpFUnordNotEqual:
+ case SpvOpFUnordLessThan:
+ case SpvOpFUnordGreaterThan:
+ case SpvOpFUnordLessThanEqual:
+ case SpvOpFUnordGreaterThanEqual: {
+ bool swap;
+ unsigned src_bit_size = glsl_get_bit_size(vtn_src[0]->type);
+ unsigned dst_bit_size = glsl_get_bit_size(type);
+ nir_op op = vtn_nir_alu_op_for_spirv_opcode(b, opcode, &swap,
+ src_bit_size, dst_bit_size);
+
+ if (swap) {
+ nir_ssa_def *tmp = src[0];
+ src[0] = src[1];
+ src[1] = tmp;
+ }
+
+ val->ssa->def =
+ nir_ior(&b->nb,
+ nir_build_alu(&b->nb, op, src[0], src[1], NULL, NULL),
+ nir_ior(&b->nb,
+ nir_fne(&b->nb, src[0], src[0]),
+ nir_fne(&b->nb, src[1], src[1])));
+ break;
+ }
+
+ case SpvOpFOrdNotEqual: {
+ /* For all the SpvOpFOrd* comparisons apart from NotEqual, the value
+ * from the ALU will probably already be false if the operands are not
+ * ordered so we don’t need to handle it specially.
+ */
+ bool swap;
+ unsigned src_bit_size = glsl_get_bit_size(vtn_src[0]->type);
+ unsigned dst_bit_size = glsl_get_bit_size(type);
+ nir_op op = vtn_nir_alu_op_for_spirv_opcode(b, opcode, &swap,
+ src_bit_size, dst_bit_size);
+
+ assert(!swap);
+
+ val->ssa->def =
+ nir_iand(&b->nb,
+ nir_build_alu(&b->nb, op, src[0], src[1], NULL, NULL),
+ nir_iand(&b->nb,
+ nir_feq(&b->nb, src[0], src[0]),
+ nir_feq(&b->nb, src[1], src[1])));
+ break;
+ }
+
+ case SpvOpFConvert: {
+ nir_alu_type src_alu_type = nir_get_nir_type_for_glsl_type(vtn_src[0]->type);
+ nir_alu_type dst_alu_type = nir_get_nir_type_for_glsl_type(type);
+ nir_rounding_mode rounding_mode = nir_rounding_mode_undef;
+
+ vtn_foreach_decoration(b, val, handle_rounding_mode, &rounding_mode);
+ nir_op op = nir_type_conversion_op(src_alu_type, dst_alu_type, rounding_mode);
+
+ val->ssa->def = nir_build_alu(&b->nb, op, src[0], src[1], NULL, NULL);
+ break;
+ }
+
+ case SpvOpBitFieldInsert:
+ case SpvOpBitFieldSExtract:
+ case SpvOpBitFieldUExtract:
+ case SpvOpShiftLeftLogical:
+ case SpvOpShiftRightArithmetic:
+ case SpvOpShiftRightLogical: {
+ bool swap;
+ unsigned src0_bit_size = glsl_get_bit_size(vtn_src[0]->type);
+ unsigned dst_bit_size = glsl_get_bit_size(type);
+ nir_op op = vtn_nir_alu_op_for_spirv_opcode(b, opcode, &swap,
+ src0_bit_size, dst_bit_size);
+
+ assert (op == nir_op_ushr || op == nir_op_ishr || op == nir_op_ishl ||
+ op == nir_op_bitfield_insert || op == nir_op_ubitfield_extract ||
+ op == nir_op_ibitfield_extract);
+
+ for (unsigned i = 0; i < nir_op_infos[op].num_inputs; i++) {
+ unsigned src_bit_size =
+ nir_alu_type_get_type_size(nir_op_infos[op].input_types[i]);
+ if (src_bit_size == 0)
+ continue;
+ if (src_bit_size != src[i]->bit_size) {
+ assert(src_bit_size == 32);
+ /* Convert the Shift, Offset and Count operands to 32 bits, which is the bitsize
+ * supported by the NIR instructions. See discussion here:
+ *
+ * https://lists.freedesktop.org/archives/mesa-dev/2018-April/193026.html
+ */
+ src[i] = nir_u2u32(&b->nb, src[i]);
+ }
+ }
+ val->ssa->def = nir_build_alu(&b->nb, op, src[0], src[1], src[2], src[3]);
+ break;
+ }
+
+ case SpvOpSignBitSet: {
+ unsigned src_bit_size = glsl_get_bit_size(vtn_src[0]->type);
+ if (src[0]->num_components == 1)
+ val->ssa->def =
+ nir_ushr(&b->nb, src[0], nir_imm_int(&b->nb, src_bit_size - 1));
+ else
+ val->ssa->def =
+ nir_ishr(&b->nb, src[0], nir_imm_int(&b->nb, src_bit_size - 1));
+
+ val->ssa->def = nir_i2b(&b->nb, val->ssa->def);
break;
+ }
default: {
bool swap;
- nir_op op = vtn_nir_alu_op_for_spirv_opcode(opcode, &swap);
+ unsigned src_bit_size = glsl_get_bit_size(vtn_src[0]->type);
+ unsigned dst_bit_size = glsl_get_bit_size(type);
+ nir_op op = vtn_nir_alu_op_for_spirv_opcode(b, opcode, &swap,
+ src_bit_size, dst_bit_size);
if (swap) {
nir_ssa_def *tmp = src[0];
src[1] = tmp;
}
+ switch (op) {
+ case nir_op_ishl:
+ case nir_op_ishr:
+ case nir_op_ushr:
+ if (src[1]->bit_size != 32)
+ src[1] = nir_u2u32(&b->nb, src[1]);
+ break;
+ default:
+ break;
+ }
+
val->ssa->def = nir_build_alu(&b->nb, op, src[0], src[1], src[2], src[3]);
break;
} /* default */
}
- b->nb.exact = false;
+ switch (opcode) {
+ case SpvOpIAdd:
+ case SpvOpIMul:
+ case SpvOpISub:
+ case SpvOpShiftLeftLogical:
+ case SpvOpSNegate: {
+ nir_alu_instr *alu = nir_instr_as_alu(val->ssa->def->parent_instr);
+ vtn_foreach_decoration(b, val, handle_no_wrap, alu);
+ break;
+ }
+ default:
+ /* Do nothing. */
+ break;
+ }
+
+ b->nb.exact = b->exact;
+}
+
+void
+vtn_handle_bitcast(struct vtn_builder *b, const uint32_t *w, unsigned count)
+{
+ vtn_assert(count == 4);
+ /* From the definition of OpBitcast in the SPIR-V 1.2 spec:
+ *
+ * "If Result Type has the same number of components as Operand, they
+ * must also have the same component width, and results are computed per
+ * component.
+ *
+ * If Result Type has a different number of components than Operand, the
+ * total number of bits in Result Type must equal the total number of
+ * bits in Operand. Let L be the type, either Result Type or Operand’s
+ * type, that has the larger number of components. Let S be the other
+ * type, with the smaller number of components. The number of components
+ * in L must be an integer multiple of the number of components in S.
+ * The first component (that is, the only or lowest-numbered component)
+ * of S maps to the first components of L, and so on, up to the last
+ * component of S mapping to the last components of L. Within this
+ * mapping, any single component of S (mapping to multiple components of
+ * L) maps its lower-ordered bits to the lower-numbered components of L."
+ */
+
+ struct vtn_type *type = vtn_value(b, w[1], vtn_value_type_type)->type;
+ struct vtn_ssa_value *vtn_src = vtn_ssa_value(b, w[3]);
+ struct nir_ssa_def *src = vtn_src->def;
+ struct vtn_ssa_value *val = vtn_create_ssa_value(b, type->type);
+
+ vtn_assert(glsl_type_is_vector_or_scalar(vtn_src->type));
+
+ vtn_fail_if(src->num_components * src->bit_size !=
+ glsl_get_vector_elements(type->type) * glsl_get_bit_size(type->type),
+ "Source and destination of OpBitcast must have the same "
+ "total number of bits");
+ val->def = nir_bitcast_vector(&b->nb, src, glsl_get_bit_size(type->type));
+ vtn_push_ssa(b, w[2], type, val);
}