Implement a simple evaluator of specialization constants.
In some cases, we need to get a literal value from a spec constant op. Mostly relevant when emitting buffers, so implement a 32-bit integer scalar subset of the evaluator. Can be extended as needed to support evaluating any specialization constant operation.
This commit is contained in:
parent
bdbef7b1f3
commit
66afe8c499
@ -0,0 +1,321 @@
|
||||
#version 450
|
||||
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_2
|
||||
#define SPIRV_CROSS_CONSTANT_ID_2 1
|
||||
#endif
|
||||
const int SONE = SPIRV_CROSS_CONSTANT_ID_2;
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_3
|
||||
#define SPIRV_CROSS_CONSTANT_ID_3 2
|
||||
#endif
|
||||
const int STWO = SPIRV_CROSS_CONSTANT_ID_3;
|
||||
const int _10 = (SONE + STWO);
|
||||
const uint _13 = (uint(_10) + 0u);
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_5
|
||||
#define SPIRV_CROSS_CONSTANT_ID_5 1u
|
||||
#endif
|
||||
const uint UONE = SPIRV_CROSS_CONSTANT_ID_5;
|
||||
const uint _15 = (_13 + UONE);
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_6
|
||||
#define SPIRV_CROSS_CONSTANT_ID_6 2u
|
||||
#endif
|
||||
const uint UTWO = SPIRV_CROSS_CONSTANT_ID_6;
|
||||
const uint IADD = (_15 + UTWO);
|
||||
const uint _19 = (IADD - 5u);
|
||||
const uint _28 = (uint(SONE) + 0u);
|
||||
const uint ISUB = (UTWO - _28);
|
||||
const uint IMUL = (UTWO * UTWO);
|
||||
const uint _37 = (IMUL - 3u);
|
||||
const uint UDIV = (UTWO / UTWO);
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_4
|
||||
#define SPIRV_CROSS_CONSTANT_ID_4 -2
|
||||
#endif
|
||||
const int SNEG_TWO = SPIRV_CROSS_CONSTANT_ID_4;
|
||||
const int SDIV = (STWO / SNEG_TWO);
|
||||
const int _52 = (SDIV + 2);
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_7
|
||||
#define SPIRV_CROSS_CONSTANT_ID_7 -3
|
||||
#endif
|
||||
const int SNEG_THREE = SPIRV_CROSS_CONSTANT_ID_7;
|
||||
const int SMOD = (STWO % SNEG_THREE);
|
||||
const int _66 = (SMOD + 2);
|
||||
const uint UMOD = (IADD % IMUL);
|
||||
const uint _73 = (UMOD - 1u);
|
||||
const uint LSHL = (IADD << ISUB);
|
||||
const uint _81 = (LSHL - 11u);
|
||||
const uint RSHL = (IADD >> ISUB);
|
||||
const uint _89 = (RSHL - 2u);
|
||||
const int _95 = int(IADD + 0u);
|
||||
const int _96 = (-_95);
|
||||
const int _97 = (-SDIV);
|
||||
const int RSHA = (_96 >> _97);
|
||||
const int _100 = (RSHA + 4);
|
||||
const bool IEQ = (IADD == ISUB);
|
||||
const int _109 = IEQ ? 2 : 1;
|
||||
const bool INEQ = (IADD != ISUB);
|
||||
const int _116 = INEQ ? 1 : 2;
|
||||
const bool ULT = (IADD < ISUB);
|
||||
const int _123 = ULT ? 2 : 1;
|
||||
const bool ULE = (IADD <= ISUB);
|
||||
const int _130 = ULE ? 2 : 1;
|
||||
const bool UGT = (IADD > ISUB);
|
||||
const int _137 = UGT ? 1 : 2;
|
||||
const bool UGE = (IADD >= ISUB);
|
||||
const int _144 = UGE ? 1 : 2;
|
||||
const bool SLT = (SMOD < 1);
|
||||
const int _151 = SLT ? 1 : 2;
|
||||
const bool SLE = (SMOD <= 1);
|
||||
const int _158 = SLE ? 1 : 2;
|
||||
const bool SGT = (SMOD > 1);
|
||||
const int _165 = SGT ? 2 : 1;
|
||||
const bool SGE = (SMOD >= 1);
|
||||
const int _172 = SGE ? 2 : 1;
|
||||
const bool LOR = (IEQ || SLT);
|
||||
const int _179 = LOR ? 1 : 2;
|
||||
const bool LAND = (IEQ && SLT);
|
||||
const int _186 = LAND ? 2 : 1;
|
||||
const bool LNOT = (!LOR);
|
||||
const int _193 = LNOT ? 2 : 1;
|
||||
const uint AND = (IADD & IADD);
|
||||
const uint _200 = (AND - 5u);
|
||||
const uint OR = (IADD | ISUB);
|
||||
const uint _208 = (OR - 6u);
|
||||
const uint XOR = (IADD ^ IADD);
|
||||
const uint _215 = (XOR + 1u);
|
||||
const uint NOT = (~XOR);
|
||||
const uint _223 = (NOT - 4294967294u);
|
||||
const bool LEQ = (LAND == LNOT);
|
||||
const int _230 = LEQ ? 1 : 2;
|
||||
const bool LNEQ = (LAND != LNOT);
|
||||
const int _237 = LNEQ ? 2 : 1;
|
||||
const uint SEL = IEQ ? IADD : ISUB;
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_0
|
||||
#define SPIRV_CROSS_CONSTANT_ID_0 true
|
||||
#endif
|
||||
const bool TRUE = SPIRV_CROSS_CONSTANT_ID_0;
|
||||
#ifndef SPIRV_CROSS_CONSTANT_ID_1
|
||||
#define SPIRV_CROSS_CONSTANT_ID_1 false
|
||||
#endif
|
||||
const bool FALSE = SPIRV_CROSS_CONSTANT_ID_1;
|
||||
|
||||
layout(binding = 0, std430) buffer SSBO_IAdd
|
||||
{
|
||||
float val[_19];
|
||||
float dummy;
|
||||
} IAdd;
|
||||
|
||||
layout(binding = 1, std430) buffer SSBO_ISub
|
||||
{
|
||||
float val[ISUB];
|
||||
float dummy;
|
||||
} ISub;
|
||||
|
||||
layout(binding = 2, std430) buffer SSBO_IMul
|
||||
{
|
||||
float val[_37];
|
||||
float dummy;
|
||||
} IMul;
|
||||
|
||||
layout(binding = 3, std430) buffer SSBO_UDiv
|
||||
{
|
||||
float val[UDIV];
|
||||
float dummy;
|
||||
} UDiv;
|
||||
|
||||
layout(binding = 4, std430) buffer SSBO_SDiv
|
||||
{
|
||||
float val[_52];
|
||||
float dummy;
|
||||
} SDiv;
|
||||
|
||||
layout(binding = 5, std430) buffer SSBO_SRem
|
||||
{
|
||||
float val[1];
|
||||
float dummy;
|
||||
} SRem;
|
||||
|
||||
layout(binding = 6, std430) buffer SSBO_SMod
|
||||
{
|
||||
float val[_66];
|
||||
float dummy;
|
||||
} SMod;
|
||||
|
||||
layout(binding = 7, std430) buffer SSBO_UMod
|
||||
{
|
||||
float val[_73];
|
||||
float dummy;
|
||||
} UMod;
|
||||
|
||||
layout(binding = 8, std430) buffer SSBO_LShl
|
||||
{
|
||||
float val[_81];
|
||||
float dummy;
|
||||
} LShl;
|
||||
|
||||
layout(binding = 9, std430) buffer SSBO_RShl
|
||||
{
|
||||
float val[_89];
|
||||
float dummy;
|
||||
} RShl;
|
||||
|
||||
layout(binding = 10, std430) buffer SSBO_RSha
|
||||
{
|
||||
float val[_100];
|
||||
float dummy;
|
||||
} RSha;
|
||||
|
||||
layout(binding = 11, std430) buffer SSBO_IEq
|
||||
{
|
||||
float val[_109];
|
||||
float dummy;
|
||||
} IEq;
|
||||
|
||||
layout(binding = 12, std430) buffer SSBO_INeq
|
||||
{
|
||||
float val[_116];
|
||||
float dummy;
|
||||
} INeq;
|
||||
|
||||
layout(binding = 13, std430) buffer SSBO_Ult
|
||||
{
|
||||
float val[_123];
|
||||
float dummy;
|
||||
} Ult;
|
||||
|
||||
layout(binding = 14, std430) buffer SSBO_Ule
|
||||
{
|
||||
float val[_130];
|
||||
float dummy;
|
||||
} Ule;
|
||||
|
||||
layout(binding = 15, std430) buffer SSBO_Ugt
|
||||
{
|
||||
float val[_137];
|
||||
float dummy;
|
||||
} Ugt;
|
||||
|
||||
layout(binding = 16, std430) buffer SSBO_Uge
|
||||
{
|
||||
float val[_144];
|
||||
float dummy;
|
||||
} Uge;
|
||||
|
||||
layout(binding = 17, std430) buffer SSBO_Slt
|
||||
{
|
||||
float val[_151];
|
||||
float dummy;
|
||||
} Slt;
|
||||
|
||||
layout(binding = 18, std430) buffer SSBO_Sle
|
||||
{
|
||||
float val[_158];
|
||||
float dummy;
|
||||
} Sle;
|
||||
|
||||
layout(binding = 19, std430) buffer SSBO_Sgt
|
||||
{
|
||||
float val[_165];
|
||||
float dummy;
|
||||
} Sgt;
|
||||
|
||||
layout(binding = 20, std430) buffer SSBO_Sge
|
||||
{
|
||||
float val[_172];
|
||||
float dummy;
|
||||
} Sge;
|
||||
|
||||
layout(binding = 21, std430) buffer SSBO_Lor
|
||||
{
|
||||
float val[_179];
|
||||
float dummy;
|
||||
} Lor;
|
||||
|
||||
layout(binding = 22, std430) buffer SSBO_Land
|
||||
{
|
||||
float val[_186];
|
||||
float dummy;
|
||||
} Land;
|
||||
|
||||
layout(binding = 23, std430) buffer SSBO_Lnot
|
||||
{
|
||||
float val[_193];
|
||||
float dummy;
|
||||
} Lnot;
|
||||
|
||||
layout(binding = 24, std430) buffer SSBO_And
|
||||
{
|
||||
float val[_200];
|
||||
float dummy;
|
||||
} And;
|
||||
|
||||
layout(binding = 24, std430) buffer SSBO_Or
|
||||
{
|
||||
float val[_208];
|
||||
float dummy;
|
||||
} Or;
|
||||
|
||||
layout(binding = 24, std430) buffer SSBO_Xor
|
||||
{
|
||||
float val[_215];
|
||||
float dummy;
|
||||
} Xor;
|
||||
|
||||
layout(binding = 25, std430) buffer SSBO_Not
|
||||
{
|
||||
float val[_223];
|
||||
float dummy;
|
||||
} Not;
|
||||
|
||||
layout(binding = 26, std430) buffer SSBO_Leq
|
||||
{
|
||||
float val[_230];
|
||||
float dummy;
|
||||
} Leq;
|
||||
|
||||
layout(binding = 27, std430) buffer SSBO_Lneq
|
||||
{
|
||||
float val[_237];
|
||||
float dummy;
|
||||
} Lneq;
|
||||
|
||||
layout(binding = 28, std430) buffer SSBO_Sel
|
||||
{
|
||||
float val[SEL];
|
||||
float dummy;
|
||||
} Sel;
|
||||
|
||||
void main()
|
||||
{
|
||||
IAdd.val[0] = 0.0;
|
||||
ISub.val[0] = 0.0;
|
||||
IMul.val[0] = 0.0;
|
||||
UDiv.val[0] = 0.0;
|
||||
SDiv.val[0] = 0.0;
|
||||
SRem.val[0] = 0.0;
|
||||
SMod.val[0] = 0.0;
|
||||
UMod.val[0] = 0.0;
|
||||
LShl.val[0] = 0.0;
|
||||
RShl.val[0] = 0.0;
|
||||
RSha.val[0] = 0.0;
|
||||
IEq.val[0] = 0.0;
|
||||
INeq.val[0] = 0.0;
|
||||
Ult.val[0] = 0.0;
|
||||
Ule.val[0] = 0.0;
|
||||
Ugt.val[0] = 0.0;
|
||||
Uge.val[0] = 0.0;
|
||||
Slt.val[0] = 0.0;
|
||||
Sle.val[0] = 0.0;
|
||||
Sgt.val[0] = 0.0;
|
||||
Sge.val[0] = 0.0;
|
||||
Lor.val[0] = 0.0;
|
||||
Land.val[0] = 0.0;
|
||||
Lnot.val[0] = 0.0;
|
||||
And.val[0] = 0.0;
|
||||
Or.val[0] = 0.0;
|
||||
Xor.val[0] = 0.0;
|
||||
Not.val[0] = 0.0;
|
||||
Leq.val[0] = 0.0;
|
||||
Lneq.val[0] = 0.0;
|
||||
Sel.val[0] = 0.0;
|
||||
}
|
||||
|
123
shaders-no-opt/comp/specialization-constant-evaluation.comp
Normal file
123
shaders-no-opt/comp/specialization-constant-evaluation.comp
Normal file
@ -0,0 +1,123 @@
|
||||
#version 450
|
||||
|
||||
layout(local_size_x = 1) in;
|
||||
|
||||
layout(constant_id = 0) const bool TRUE = true;
|
||||
layout(constant_id = 1) const bool FALSE = false;
|
||||
layout(constant_id = 2) const int SONE = 1;
|
||||
layout(constant_id = 3) const int STWO = 2;
|
||||
layout(constant_id = 4) const int SNEG_TWO = -2;
|
||||
layout(constant_id = 5) const uint UONE = 1;
|
||||
layout(constant_id = 6) const uint UTWO = 2;
|
||||
layout(constant_id = 7) const int SNEG_THREE = -3;
|
||||
|
||||
const uint IADD = SONE + STWO + UONE + UTWO; // 6
|
||||
const uint ISUB = UTWO - SONE; // 1
|
||||
const uint IMUL = UTWO * UTWO; // 4
|
||||
const uint UDIV = UTWO / UTWO; // 1
|
||||
const int SDIV = STWO / SNEG_TWO; // -1
|
||||
//const int SREM = STWO % SNEG_THREE; // 1
|
||||
const int SREM = 1;
|
||||
const int SMOD = STWO % SNEG_THREE; // -1
|
||||
const uint UMOD = IADD % IMUL; // 2
|
||||
|
||||
const uint LSHL = IADD << ISUB; // 12
|
||||
const uint RSHL = IADD >> ISUB; // 3
|
||||
const int RSHA = (-int(IADD)) >> (-SDIV); // -3
|
||||
|
||||
const bool IEQ = IADD == ISUB; // false
|
||||
const bool INEQ = IADD != ISUB; // true
|
||||
const bool ULT = IADD < ISUB; // false
|
||||
const bool ULE = IADD <= ISUB; // false
|
||||
const bool UGT = IADD > ISUB; // true
|
||||
const bool UGE = IADD >= ISUB; // true
|
||||
|
||||
const bool SLT = SMOD < SREM; // true
|
||||
const bool SLE = SMOD <= SREM; // true
|
||||
const bool SGT = SMOD > SREM; // false
|
||||
const bool SGE = SMOD >= SREM; // false
|
||||
|
||||
const bool LOR = IEQ || SLT; // true
|
||||
const bool LAND = IEQ && SLT; // false
|
||||
const bool LNOT = !LOR; // false
|
||||
|
||||
const uint AND = IADD & IADD; // 6
|
||||
const uint OR = IADD | ISUB; // 7
|
||||
const uint XOR = IADD ^ IADD; // 0
|
||||
const uint NOT = ~XOR; // UINT_MAX
|
||||
|
||||
const bool LEQ = LAND == LNOT; // true
|
||||
const bool LNEQ = LAND != LNOT; // false
|
||||
|
||||
const uint SEL = IEQ ? IADD : ISUB; // 1
|
||||
|
||||
#define DUMMY_SSBO(name, bind, size) layout(std430, set = 0, binding = bind) buffer SSBO_##name { float val[size]; float dummy; } name
|
||||
|
||||
// Normalize all sizes to 1 element so that the default offsets in glslang matches up with what we should be computing.
|
||||
// If we do it right, we should get no layout(offset = N) expressions.
|
||||
DUMMY_SSBO(IAdd, 0, IADD - 5);
|
||||
DUMMY_SSBO(ISub, 1, ISUB);
|
||||
DUMMY_SSBO(IMul, 2, IMUL - 3);
|
||||
DUMMY_SSBO(UDiv, 3, UDIV);
|
||||
DUMMY_SSBO(SDiv, 4, SDIV + 2);
|
||||
DUMMY_SSBO(SRem, 5, SREM);
|
||||
DUMMY_SSBO(SMod, 6, SMOD + 2);
|
||||
DUMMY_SSBO(UMod, 7, UMOD - 1);
|
||||
DUMMY_SSBO(LShl, 8, LSHL - 11);
|
||||
DUMMY_SSBO(RShl, 9, RSHL - 2);
|
||||
DUMMY_SSBO(RSha, 10, RSHA + 4);
|
||||
DUMMY_SSBO(IEq, 11, IEQ ? 2 : 1);
|
||||
DUMMY_SSBO(INeq, 12, INEQ ? 1 : 2);
|
||||
DUMMY_SSBO(Ult, 13, ULT ? 2 : 1);
|
||||
DUMMY_SSBO(Ule, 14, ULE ? 2 : 1);
|
||||
DUMMY_SSBO(Ugt, 15, UGT ? 1 : 2);
|
||||
DUMMY_SSBO(Uge, 16, UGE ? 1 : 2);
|
||||
DUMMY_SSBO(Slt, 17, SLT ? 1 : 2);
|
||||
DUMMY_SSBO(Sle, 18, SLE ? 1 : 2);
|
||||
DUMMY_SSBO(Sgt, 19, SGT ? 2 : 1);
|
||||
DUMMY_SSBO(Sge, 20, SGE ? 2 : 1);
|
||||
DUMMY_SSBO(Lor, 21, LOR ? 1 : 2);
|
||||
DUMMY_SSBO(Land, 22, LAND ? 2 : 1);
|
||||
DUMMY_SSBO(Lnot, 23, LNOT ? 2 : 1);
|
||||
DUMMY_SSBO(And, 24, AND - 5);
|
||||
DUMMY_SSBO(Or, 24, OR - 6);
|
||||
DUMMY_SSBO(Xor, 24, XOR + 1);
|
||||
DUMMY_SSBO(Not, 25, NOT - 0xfffffffeu);
|
||||
DUMMY_SSBO(Leq, 26, LEQ ? 1 : 2);
|
||||
DUMMY_SSBO(Lneq, 27, LNEQ ? 2 : 1);
|
||||
DUMMY_SSBO(Sel, 28, SEL);
|
||||
|
||||
void main()
|
||||
{
|
||||
IAdd.val[0] = 0.0;
|
||||
ISub.val[0] = 0.0;
|
||||
IMul.val[0] = 0.0;
|
||||
UDiv.val[0] = 0.0;
|
||||
SDiv.val[0] = 0.0;
|
||||
SRem.val[0] = 0.0;
|
||||
SMod.val[0] = 0.0;
|
||||
UMod.val[0] = 0.0;
|
||||
LShl.val[0] = 0.0;
|
||||
RShl.val[0] = 0.0;
|
||||
RSha.val[0] = 0.0;
|
||||
IEq.val[0] = 0.0;
|
||||
INeq.val[0] = 0.0;
|
||||
Ult.val[0] = 0.0;
|
||||
Ule.val[0] = 0.0;
|
||||
Ugt.val[0] = 0.0;
|
||||
Uge.val[0] = 0.0;
|
||||
Slt.val[0] = 0.0;
|
||||
Sle.val[0] = 0.0;
|
||||
Sgt.val[0] = 0.0;
|
||||
Sge.val[0] = 0.0;
|
||||
Lor.val[0] = 0.0;
|
||||
Land.val[0] = 0.0;
|
||||
Lnot.val[0] = 0.0;
|
||||
And.val[0] = 0.0;
|
||||
Or.val[0] = 0.0;
|
||||
Xor.val[0] = 0.0;
|
||||
Not.val[0] = 0.0;
|
||||
Leq.val[0] = 0.0;
|
||||
Lneq.val[0] = 0.0;
|
||||
Sel.val[0] = 0.0;
|
||||
}
|
144
spirv_cross.cpp
144
spirv_cross.cpp
@ -1652,6 +1652,148 @@ size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, si
|
||||
return size;
|
||||
}
|
||||
|
||||
uint32_t Compiler::evaluate_spec_constant_u32(const SPIRConstantOp &spec) const
|
||||
{
|
||||
auto &result_type = get<SPIRType>(spec.basetype);
|
||||
if (result_type.basetype != SPIRType::UInt && result_type.basetype != SPIRType::Int && result_type.basetype != SPIRType::Boolean)
|
||||
SPIRV_CROSS_THROW("Only 32-bit integers and booleans are currently supported when evaluating specialization constants.\n");
|
||||
if (!is_scalar(result_type))
|
||||
SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n");
|
||||
|
||||
uint32_t value = 0;
|
||||
|
||||
const auto eval_u32 = [&](uint32_t id) -> uint32_t {
|
||||
auto &type = expression_type(id);
|
||||
if (type.basetype != SPIRType::UInt && type.basetype != SPIRType::Int && type.basetype != SPIRType::Boolean)
|
||||
SPIRV_CROSS_THROW("Only 32-bit integers and booleans are currently supported when evaluating specialization constants.\n");
|
||||
if (!is_scalar(type))
|
||||
SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n");
|
||||
if (const auto *c = this->maybe_get<SPIRConstant>(id))
|
||||
return c->scalar();
|
||||
else
|
||||
return evaluate_spec_constant_u32(this->get<SPIRConstantOp>(id));
|
||||
};
|
||||
|
||||
#define binary_spec_op(op, binary_op) \
|
||||
case Op##op: value = eval_u32(spec.arguments[0]) binary_op eval_u32(spec.arguments[1]); break
|
||||
#define binary_spec_op_cast(op, binary_op, type) \
|
||||
case Op##op: value = uint32_t(type(eval_u32(spec.arguments[0])) binary_op type(eval_u32(spec.arguments[1]))); break
|
||||
|
||||
// Support the basic opcodes which are typically used when computing array sizes.
|
||||
switch (spec.opcode)
|
||||
{
|
||||
binary_spec_op(IAdd, +);
|
||||
binary_spec_op(ISub, -);
|
||||
binary_spec_op(IMul, *);
|
||||
binary_spec_op(BitwiseAnd, &);
|
||||
binary_spec_op(BitwiseOr, |);
|
||||
binary_spec_op(BitwiseXor, ^);
|
||||
binary_spec_op(LogicalAnd, &);
|
||||
binary_spec_op(LogicalOr, |);
|
||||
binary_spec_op(ShiftLeftLogical, <<);
|
||||
binary_spec_op(ShiftRightLogical, >>);
|
||||
binary_spec_op_cast(ShiftRightArithmetic, >>, int32_t);
|
||||
binary_spec_op(LogicalEqual, ==);
|
||||
binary_spec_op(LogicalNotEqual, !=);
|
||||
binary_spec_op(IEqual, ==);
|
||||
binary_spec_op(INotEqual, !=);
|
||||
binary_spec_op(ULessThan, <);
|
||||
binary_spec_op(ULessThanEqual, <=);
|
||||
binary_spec_op(UGreaterThan, >);
|
||||
binary_spec_op(UGreaterThanEqual, >=);
|
||||
binary_spec_op_cast(SLessThan, <, int32_t);
|
||||
binary_spec_op_cast(SLessThanEqual, <=, int32_t);
|
||||
binary_spec_op_cast(SGreaterThan, >, int32_t);
|
||||
binary_spec_op_cast(SGreaterThanEqual, >=, int32_t);
|
||||
#undef binary_spec_op
|
||||
#undef binary_spec_op_cast
|
||||
|
||||
case OpLogicalNot:
|
||||
value = uint32_t(!eval_u32(spec.arguments[0]));
|
||||
break;
|
||||
|
||||
case OpNot:
|
||||
value = ~eval_u32(spec.arguments[0]);
|
||||
break;
|
||||
|
||||
case OpSNegate:
|
||||
value = -eval_u32(spec.arguments[0]);
|
||||
break;
|
||||
|
||||
case OpSelect:
|
||||
value = eval_u32(spec.arguments[0]) ? eval_u32(spec.arguments[1]) : eval_u32(spec.arguments[2]);
|
||||
break;
|
||||
|
||||
case OpUMod:
|
||||
{
|
||||
uint32_t a = eval_u32(spec.arguments[0]);
|
||||
uint32_t b = eval_u32(spec.arguments[1]);
|
||||
if (b == 0)
|
||||
SPIRV_CROSS_THROW("Undefined behavior in UMod, b == 0.\n");
|
||||
value = a % b;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpSRem:
|
||||
{
|
||||
auto a = int32_t(eval_u32(spec.arguments[0]));
|
||||
auto b = int32_t(eval_u32(spec.arguments[1]));
|
||||
if (b == 0)
|
||||
SPIRV_CROSS_THROW("Undefined behavior in SRem, b == 0.\n");
|
||||
value = a % b;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpSMod:
|
||||
{
|
||||
auto a = int32_t(eval_u32(spec.arguments[0]));
|
||||
auto b = int32_t(eval_u32(spec.arguments[1]));
|
||||
if (b == 0)
|
||||
SPIRV_CROSS_THROW("Undefined behavior in SMod, b == 0.\n");
|
||||
auto v = a % b;
|
||||
|
||||
// Makes sure we match the sign of b, not a.
|
||||
if ((b < 0 && v > 0) || (b > 0 && v < 0))
|
||||
v += b;
|
||||
value = v;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpUDiv:
|
||||
{
|
||||
uint32_t a = eval_u32(spec.arguments[0]);
|
||||
uint32_t b = eval_u32(spec.arguments[1]);
|
||||
if (b == 0)
|
||||
SPIRV_CROSS_THROW("Undefined behavior in UDiv, b == 0.\n");
|
||||
value = a / b;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpSDiv:
|
||||
{
|
||||
auto a = int32_t(eval_u32(spec.arguments[0]));
|
||||
auto b = int32_t(eval_u32(spec.arguments[1]));
|
||||
if (b == 0)
|
||||
SPIRV_CROSS_THROW("Undefined behavior in SDiv, b == 0.\n");
|
||||
value = a / b;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SPIRV_CROSS_THROW("Unsupported spec constant opcode for evaluation.\n");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t Compiler::evaluate_constant_u32(uint32_t id) const
|
||||
{
|
||||
if (const auto *c = maybe_get<SPIRConstant>(id))
|
||||
return c->scalar();
|
||||
else
|
||||
return evaluate_spec_constant_u32(get<SPIRConstantOp>(id));
|
||||
}
|
||||
|
||||
size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const
|
||||
{
|
||||
if (struct_type.member_types.empty())
|
||||
@ -1686,7 +1828,7 @@ size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, ui
|
||||
{
|
||||
// For arrays, we can use ArrayStride to get an easy check.
|
||||
bool array_size_literal = type.array_size_literal.back();
|
||||
uint32_t array_size = array_size_literal ? type.array.back() : get<SPIRConstant>(type.array.back()).scalar();
|
||||
uint32_t array_size = array_size_literal ? type.array.back() : evaluate_constant_u32(type.array.back());
|
||||
return type_struct_member_array_stride(struct_type, index) * array_size;
|
||||
}
|
||||
else if (type.basetype == SPIRType::Struct)
|
||||
|
@ -1060,6 +1060,9 @@ protected:
|
||||
|
||||
bool flush_phi_required(BlockID from, BlockID to) const;
|
||||
|
||||
uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const;
|
||||
uint32_t evaluate_constant_u32(uint32_t id) const;
|
||||
|
||||
private:
|
||||
// Used only to implement the old deprecated get_entry_point() interface.
|
||||
const SPIREntryPoint &get_first_entry_point(const std::string &name) const;
|
||||
|
@ -6755,7 +6755,7 @@ void CompilerGLSL::emit_subgroup_op(const Instruction &i)
|
||||
uint32_t result_type = ops[0];
|
||||
uint32_t id = ops[1];
|
||||
|
||||
auto scope = static_cast<Scope>(get<SPIRConstant>(ops[2]).scalar());
|
||||
auto scope = static_cast<Scope>(evaluate_constant_u32(ops[2]));
|
||||
if (scope != ScopeSubgroup)
|
||||
SPIRV_CROSS_THROW("Only subgroup scope is supported.");
|
||||
|
||||
@ -6889,7 +6889,7 @@ case OpGroupNonUniform##op: \
|
||||
|
||||
case OpGroupNonUniformQuadSwap:
|
||||
{
|
||||
uint32_t direction = get<SPIRConstant>(ops[4]).scalar();
|
||||
uint32_t direction = evaluate_constant_u32(ops[4]);
|
||||
if (direction == 0)
|
||||
emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapHorizontal");
|
||||
else if (direction == 1)
|
||||
@ -7635,7 +7635,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
|
||||
else if (type->basetype == SPIRType::Struct)
|
||||
{
|
||||
if (!is_literal)
|
||||
index = get<SPIRConstant>(index).scalar();
|
||||
index = evaluate_constant_u32(index);
|
||||
|
||||
if (index >= type->member_types.size())
|
||||
SPIRV_CROSS_THROW("Member index is out of bounds!");
|
||||
@ -8156,7 +8156,7 @@ std::pair<std::string, uint32_t> CompilerGLSL::flattened_access_chain_offset(
|
||||
// We also check if this member is a builtin, since we then replace the entire expression with the builtin one.
|
||||
else if (type->basetype == SPIRType::Struct)
|
||||
{
|
||||
index = get<SPIRConstant>(index).scalar();
|
||||
index = evaluate_constant_u32(index);
|
||||
|
||||
if (index >= type->member_types.size())
|
||||
SPIRV_CROSS_THROW("Member index is out of bounds!");
|
||||
@ -8184,7 +8184,7 @@ std::pair<std::string, uint32_t> CompilerGLSL::flattened_access_chain_offset(
|
||||
auto *constant = maybe_get<SPIRConstant>(index);
|
||||
if (constant)
|
||||
{
|
||||
index = get<SPIRConstant>(index).scalar();
|
||||
index = evaluate_constant_u32(index);
|
||||
offset += index * (row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride);
|
||||
}
|
||||
else
|
||||
@ -8213,7 +8213,7 @@ std::pair<std::string, uint32_t> CompilerGLSL::flattened_access_chain_offset(
|
||||
auto *constant = maybe_get<SPIRConstant>(index);
|
||||
if (constant)
|
||||
{
|
||||
index = get<SPIRConstant>(index).scalar();
|
||||
index = evaluate_constant_u32(index);
|
||||
offset += index * (row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8));
|
||||
}
|
||||
else
|
||||
@ -10805,14 +10805,14 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
|
||||
if (opcode == OpMemoryBarrier)
|
||||
{
|
||||
memory = get<SPIRConstant>(ops[0]).scalar();
|
||||
semantics = get<SPIRConstant>(ops[1]).scalar();
|
||||
memory = evaluate_constant_u32(ops[0]);
|
||||
semantics = evaluate_constant_u32(ops[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
execution_scope = get<SPIRConstant>(ops[0]).scalar();
|
||||
memory = get<SPIRConstant>(ops[1]).scalar();
|
||||
semantics = get<SPIRConstant>(ops[2]).scalar();
|
||||
execution_scope = evaluate_constant_u32(ops[0]);
|
||||
memory = evaluate_constant_u32(ops[1]);
|
||||
semantics = evaluate_constant_u32(ops[2]);
|
||||
}
|
||||
|
||||
if (execution_scope == ScopeSubgroup || memory == ScopeSubgroup)
|
||||
@ -10841,8 +10841,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
if (next && next->op == OpControlBarrier)
|
||||
{
|
||||
auto *next_ops = stream(*next);
|
||||
uint32_t next_memory = get<SPIRConstant>(next_ops[1]).scalar();
|
||||
uint32_t next_semantics = get<SPIRConstant>(next_ops[2]).scalar();
|
||||
uint32_t next_memory = evaluate_constant_u32(next_ops[1]);
|
||||
uint32_t next_semantics = evaluate_constant_u32(next_ops[2]);
|
||||
next_semantics = mask_relevant_memory_semantics(next_semantics);
|
||||
|
||||
bool memory_scope_covered = false;
|
||||
@ -11795,15 +11795,7 @@ uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type, uint32_t inde
|
||||
{
|
||||
// Use the default spec constant value.
|
||||
// This is the best we can do.
|
||||
uint32_t array_size_id = type.array[index];
|
||||
|
||||
// Explicitly check for this case. The error message you would get (bad cast) makes no sense otherwise.
|
||||
if (ir.ids[array_size_id].get_type() == TypeConstantOp)
|
||||
SPIRV_CROSS_THROW("An array size was found to be an OpSpecConstantOp. This is not supported since "
|
||||
"SPIRV-Cross cannot deduce the actual size here.");
|
||||
|
||||
uint32_t array_size = get<SPIRConstant>(array_size_id).scalar();
|
||||
return array_size;
|
||||
return evaluate_constant_u32(type.array[index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -790,7 +790,7 @@ uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const
|
||||
if (type.array_size_literal[i])
|
||||
array_multiplier *= type.array[i];
|
||||
else
|
||||
array_multiplier *= get<SPIRConstant>(type.array[i]).scalar();
|
||||
array_multiplier *= evaluate_constant_u32(type.array[i]);
|
||||
}
|
||||
elements += array_multiplier * type.columns;
|
||||
}
|
||||
@ -2860,7 +2860,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
|
||||
}
|
||||
else if (gather)
|
||||
{
|
||||
uint32_t comp_num = get<SPIRConstant>(comp).scalar();
|
||||
uint32_t comp_num = evaluate_constant_u32(comp);
|
||||
if (hlsl_options.shader_model >= 50)
|
||||
{
|
||||
switch (comp_num)
|
||||
@ -4454,7 +4454,7 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
|
||||
uint32_t result_type = ops[0];
|
||||
uint32_t id = ops[1];
|
||||
|
||||
auto scope = static_cast<Scope>(get<SPIRConstant>(ops[2]).scalar());
|
||||
auto scope = static_cast<Scope>(evaluate_constant_u32(ops[2]));
|
||||
if (scope != ScopeSubgroup)
|
||||
SPIRV_CROSS_THROW("Only subgroup scope is supported.");
|
||||
|
||||
@ -4611,7 +4611,7 @@ case OpGroupNonUniform##op: \
|
||||
|
||||
case OpGroupNonUniformQuadSwap:
|
||||
{
|
||||
uint32_t direction = get<SPIRConstant>(ops[4]).scalar();
|
||||
uint32_t direction = evaluate_constant_u32(ops[4]);
|
||||
if (direction == 0)
|
||||
emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossX");
|
||||
else if (direction == 1)
|
||||
@ -5269,13 +5269,13 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
|
||||
if (opcode == OpMemoryBarrier)
|
||||
{
|
||||
memory = get<SPIRConstant>(ops[0]).scalar();
|
||||
semantics = get<SPIRConstant>(ops[1]).scalar();
|
||||
memory = evaluate_constant_u32(ops[0]);
|
||||
semantics = evaluate_constant_u32(ops[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
memory = get<SPIRConstant>(ops[1]).scalar();
|
||||
semantics = get<SPIRConstant>(ops[2]).scalar();
|
||||
memory = evaluate_constant_u32(ops[1]);
|
||||
semantics = evaluate_constant_u32(ops[2]);
|
||||
}
|
||||
|
||||
if (memory == ScopeSubgroup)
|
||||
@ -5295,8 +5295,8 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
if (next && next->op == OpControlBarrier)
|
||||
{
|
||||
auto *next_ops = stream(*next);
|
||||
uint32_t next_memory = get<SPIRConstant>(next_ops[1]).scalar();
|
||||
uint32_t next_semantics = get<SPIRConstant>(next_ops[2]).scalar();
|
||||
uint32_t next_memory = evaluate_constant_u32(next_ops[1]);
|
||||
uint32_t next_semantics = evaluate_constant_u32(next_ops[2]);
|
||||
next_semantics = mask_relevant_memory_semantics(next_semantics);
|
||||
|
||||
// There is no "just execution barrier" in HLSL.
|
||||
|
@ -7175,8 +7175,8 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin
|
||||
if (get_execution_model() != ExecutionModelGLCompute && get_execution_model() != ExecutionModelTessellationControl)
|
||||
return;
|
||||
|
||||
uint32_t exe_scope = id_exe_scope ? get<SPIRConstant>(id_exe_scope).scalar() : uint32_t(ScopeInvocation);
|
||||
uint32_t mem_scope = id_mem_scope ? get<SPIRConstant>(id_mem_scope).scalar() : uint32_t(ScopeInvocation);
|
||||
uint32_t exe_scope = id_exe_scope ? evaluate_constant_u32(id_exe_scope) : uint32_t(ScopeInvocation);
|
||||
uint32_t mem_scope = id_mem_scope ? evaluate_constant_u32(id_mem_scope) : uint32_t(ScopeInvocation);
|
||||
// Use the wider of the two scopes (smaller value)
|
||||
exe_scope = min(exe_scope, mem_scope);
|
||||
|
||||
@ -7187,7 +7187,7 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin
|
||||
bar_stmt = "threadgroup_barrier";
|
||||
bar_stmt += "(";
|
||||
|
||||
uint32_t mem_sem = id_mem_sem ? get<SPIRConstant>(id_mem_sem).scalar() : uint32_t(MemorySemanticsMaskNone);
|
||||
uint32_t mem_sem = id_mem_sem ? evaluate_constant_u32(id_mem_sem) : uint32_t(MemorySemanticsMaskNone);
|
||||
|
||||
// Use the | operator to combine flags if we can.
|
||||
if (msl_options.supports_msl_version(1, 2))
|
||||
@ -8534,13 +8534,7 @@ string CompilerMSL::round_fp_tex_coords(string tex_coords, bool coord_is_fp)
|
||||
// The ID must be a scalar constant.
|
||||
string CompilerMSL::to_component_argument(uint32_t id)
|
||||
{
|
||||
if (ir.ids[id].get_type() != TypeConstant)
|
||||
{
|
||||
SPIRV_CROSS_THROW("ID " + to_string(id) + " is not an OpConstant.");
|
||||
return "component::x";
|
||||
}
|
||||
|
||||
uint32_t component_index = get<SPIRConstant>(id).scalar();
|
||||
uint32_t component_index = evaluate_constant_u32(id);
|
||||
switch (component_index)
|
||||
{
|
||||
case 0:
|
||||
@ -11820,7 +11814,7 @@ void CompilerMSL::emit_subgroup_op(const Instruction &i)
|
||||
uint32_t result_type = ops[0];
|
||||
uint32_t id = ops[1];
|
||||
|
||||
auto scope = static_cast<Scope>(get<SPIRConstant>(ops[2]).scalar());
|
||||
auto scope = static_cast<Scope>(evaluate_constant_u32(ops[2]));
|
||||
if (scope != ScopeSubgroup)
|
||||
SPIRV_CROSS_THROW("Only subgroup scope is supported.");
|
||||
|
||||
@ -11920,7 +11914,7 @@ case OpGroupNonUniform##op: \
|
||||
else if (operation == GroupOperationClusteredReduce) \
|
||||
{ \
|
||||
/* Only cluster sizes of 4 are supported. */ \
|
||||
uint32_t cluster_size = get<SPIRConstant>(ops[5]).scalar(); \
|
||||
uint32_t cluster_size = evaluate_constant_u32(ops[5]); \
|
||||
if (cluster_size != 4) \
|
||||
SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \
|
||||
emit_unary_func_op(result_type, id, ops[4], "quad_" #msl_op); \
|
||||
@ -11949,7 +11943,7 @@ case OpGroupNonUniform##op: \
|
||||
else if (operation == GroupOperationClusteredReduce) \
|
||||
{ \
|
||||
/* Only cluster sizes of 4 are supported. */ \
|
||||
uint32_t cluster_size = get<SPIRConstant>(ops[5]).scalar(); \
|
||||
uint32_t cluster_size = evaluate_constant_u32(ops[5]); \
|
||||
if (cluster_size != 4) \
|
||||
SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \
|
||||
emit_unary_func_op(result_type, id, ops[4], "quad_" #msl_op); \
|
||||
@ -11972,7 +11966,7 @@ case OpGroupNonUniform##op: \
|
||||
else if (operation == GroupOperationClusteredReduce) \
|
||||
{ \
|
||||
/* Only cluster sizes of 4 are supported. */ \
|
||||
uint32_t cluster_size = get<SPIRConstant>(ops[5]).scalar(); \
|
||||
uint32_t cluster_size = evaluate_constant_u32(ops[5]); \
|
||||
if (cluster_size != 4) \
|
||||
SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \
|
||||
emit_unary_func_op_cast(result_type, id, ops[4], "quad_" #msl_op, type, type); \
|
||||
@ -12010,7 +12004,7 @@ case OpGroupNonUniform##op: \
|
||||
// n 2 | 3 0 1
|
||||
// e 3 | 2 1 0
|
||||
// Notice that target = source ^ (direction + 1).
|
||||
uint32_t mask = get<SPIRConstant>(ops[4]).scalar() + 1;
|
||||
uint32_t mask = evaluate_constant_u32(ops[4]) + 1;
|
||||
uint32_t mask_id = ir.increase_bound_by(1);
|
||||
set<SPIRConstant>(mask_id, expression_type_id(ops[4]), mask, false);
|
||||
emit_binary_func_op(result_type, id, ops[3], mask_id, "quad_shuffle_xor");
|
||||
|
Loading…
Reference in New Issue
Block a user