mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 12:42:34 +01:00
Updated spirv-tools.
This commit is contained in:
@@ -1 +1 @@
|
||||
"v2025.3", "SPIRV-Tools v2025.3 v2025.3.rc1-110-g8fbe2387"
|
||||
"v2025.4", "SPIRV-Tools v2025.4 v2025.4-28-g8b4ee452"
|
||||
|
||||
15891
3rdparty/spirv-tools/include/generated/core_tables_body.inc
vendored
15891
3rdparty/spirv-tools/include/generated/core_tables_body.inc
vendored
File diff suppressed because it is too large
Load Diff
@@ -130,6 +130,7 @@ enum Extension : uint32_t {
|
||||
kSPV_KHR_expect_assume,
|
||||
kSPV_KHR_float_controls,
|
||||
kSPV_KHR_float_controls2,
|
||||
kSPV_KHR_fma,
|
||||
kSPV_KHR_fragment_shader_barycentric,
|
||||
kSPV_KHR_fragment_shading_rate,
|
||||
kSPV_KHR_integer_dot_product,
|
||||
|
||||
@@ -46,4 +46,5 @@
|
||||
{45, "Kitsunebi Games", "Nuvk SPIR-V Emitter and DLSL compiler", "Kitsunebi Games Nuvk SPIR-V Emitter and DLSL compiler"},
|
||||
{46, "Nintendo", "", "Nintendo"},
|
||||
{47, "ARM", "", "ARM"},
|
||||
{48, "Goopax", "", "Goopax"},
|
||||
{48, "Goopax", "", "Goopax"},
|
||||
{49, "Icyllis Milica", "Arc3D Shader Compiler", "Icyllis Milica Arc3D Shader Compiler"},
|
||||
@@ -365,6 +365,7 @@ typedef enum spv_ext_inst_type_t {
|
||||
SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100,
|
||||
SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION,
|
||||
SPV_EXT_INST_TYPE_TOSA_001000_1,
|
||||
SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100,
|
||||
|
||||
// Multiple distinct extended instruction set types could return this
|
||||
// value, if they are prefixed with NonSemantic. and are otherwise
|
||||
|
||||
3
3rdparty/spirv-tools/source/ext_inst.cpp
vendored
3
3rdparty/spirv-tools/source/ext_inst.cpp
vendored
@@ -58,6 +58,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
|
||||
if (!strcmp("TOSA.001000.1", name)) {
|
||||
return SPV_EXT_INST_TYPE_TOSA_001000_1;
|
||||
}
|
||||
if (!strcmp("Arm.MotionEngine.100", name)) {
|
||||
return SPV_EXT_INST_TYPE_ARM_MOTION_ENGINE_100;
|
||||
}
|
||||
// ensure to add any known non-semantic extended instruction sets
|
||||
// above this point, and update spvExtInstIsNonSemantic()
|
||||
if (!strncmp("NonSemantic.", name, 12)) {
|
||||
|
||||
@@ -273,29 +273,30 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
|
||||
});
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::AggressiveDCE(Function* func) {
|
||||
if (func->IsDeclaration()) return false;
|
||||
Pass::Status AggressiveDCEPass::AggressiveDCE(Function* func) {
|
||||
if (func->IsDeclaration()) return Pass::Status::SuccessWithoutChange;
|
||||
std::list<BasicBlock*> structured_order;
|
||||
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
|
||||
live_local_vars_.clear();
|
||||
InitializeWorkList(func, structured_order);
|
||||
ProcessWorkList(func);
|
||||
ProcessDebugInformation(structured_order);
|
||||
if (ProcessDebugInformation(structured_order) == Pass::Status::Failure)
|
||||
return Pass::Status::Failure;
|
||||
ProcessWorkList(func);
|
||||
return KillDeadInstructions(func, structured_order);
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::ProcessDebugInformation(
|
||||
Pass::Status AggressiveDCEPass::ProcessDebugInformation(
|
||||
std::list<BasicBlock*>& structured_order) {
|
||||
for (auto bi = structured_order.begin(); bi != structured_order.end(); bi++) {
|
||||
(*bi)->ForEachInst([this](Instruction* inst) {
|
||||
bool succeeded = (*bi)->WhileEachInst([this](Instruction* inst) {
|
||||
// DebugDeclare is not dead. It must be converted to DebugValue in a
|
||||
// later pass
|
||||
if (inst->IsNonSemanticInstruction() &&
|
||||
inst->GetShader100DebugOpcode() ==
|
||||
NonSemanticShaderDebugInfo100DebugDeclare) {
|
||||
AddToWorklist(inst);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the Value of a DebugValue is killed, set Value operand to Undef
|
||||
@@ -306,28 +307,33 @@ void AggressiveDCEPass::ProcessDebugInformation(
|
||||
auto def = get_def_use_mgr()->GetDef(id);
|
||||
if (!live_insts_.Set(def->unique_id())) {
|
||||
AddToWorklist(inst);
|
||||
uint32_t undef_id = Type2Undef(def->type_id());
|
||||
if (undef_id == 0) {
|
||||
return false;
|
||||
}
|
||||
inst->SetInOperand(kDebugValueValue, {undef_id});
|
||||
context()->get_def_use_mgr()->UpdateDefUse(inst);
|
||||
worklist_.push(def);
|
||||
def->SetOpcode(spv::Op::OpUndef);
|
||||
def->SetInOperands({});
|
||||
id = inst->GetSingleWordInOperand(kDebugValueLocalVariable);
|
||||
auto localVar = get_def_use_mgr()->GetDef(id);
|
||||
AddToWorklist(localVar);
|
||||
context()->get_def_use_mgr()->UpdateDefUse(localVar);
|
||||
AddOperandsToWorkList(localVar);
|
||||
context()->get_def_use_mgr()->UpdateDefUse(def);
|
||||
id = inst->GetSingleWordInOperand(kDebugValueExpression);
|
||||
auto expression = get_def_use_mgr()->GetDef(id);
|
||||
AddToWorklist(expression);
|
||||
context()->get_def_use_mgr()->UpdateDefUse(expression);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!succeeded) return Pass::Status::Failure;
|
||||
}
|
||||
return Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool AggressiveDCEPass::KillDeadInstructions(
|
||||
Pass::Status AggressiveDCEPass::KillDeadInstructions(
|
||||
const Function* func, std::list<BasicBlock*>& structured_order) {
|
||||
bool modified = false;
|
||||
for (auto bi = structured_order.begin(); bi != structured_order.end();) {
|
||||
@@ -362,6 +368,9 @@ bool AggressiveDCEPass::KillDeadInstructions(
|
||||
// Find an undef for the return value and make sure it gets kept by
|
||||
// the pass.
|
||||
auto undef_id = Type2Undef(func->type_id());
|
||||
if (undef_id == 0) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
auto undef = get_def_use_mgr()->GetDef(undef_id);
|
||||
live_insts_.Set(undef->unique_id());
|
||||
merge_terminator->SetOpcode(spv::Op::OpReturnValue);
|
||||
@@ -380,7 +389,8 @@ bool AggressiveDCEPass::KillDeadInstructions(
|
||||
++bi;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
return modified ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::ProcessWorkList(Function* func) {
|
||||
@@ -640,7 +650,7 @@ void AggressiveDCEPass::InitializeWorkList(
|
||||
}
|
||||
}
|
||||
|
||||
void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
Pass::Status AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
// Keep all execution modes.
|
||||
for (auto& exec : get_module()->execution_modes()) {
|
||||
AddToWorklist(&exec);
|
||||
@@ -715,6 +725,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
}
|
||||
if (debug_global_seen) {
|
||||
auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
|
||||
if (dbg_none == nullptr) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
AddToWorklist(dbg_none);
|
||||
}
|
||||
|
||||
@@ -728,6 +741,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
|
||||
AddToWorklist(&dbg);
|
||||
}
|
||||
}
|
||||
return Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
@@ -755,7 +769,9 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
// Eliminate Dead functions.
|
||||
bool modified = EliminateDeadFunctions();
|
||||
|
||||
InitializeModuleScopeLiveInstructions();
|
||||
if (InitializeModuleScopeLiveInstructions() == Pass::Status::Failure) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
|
||||
// Run |AggressiveDCE| on the remaining functions. The order does not matter,
|
||||
// since |AggressiveDCE| is intra-procedural. This can mean that function
|
||||
@@ -763,7 +779,13 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
|
||||
// function will still be in the module after this pass. We expect this to be
|
||||
// rare.
|
||||
for (Function& fp : *context()->module()) {
|
||||
modified |= AggressiveDCE(&fp);
|
||||
Pass::Status function_status = AggressiveDCE(&fp);
|
||||
if (function_status == Pass::Status::Failure) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
if (function_status == Pass::Status::SuccessWithChange) {
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If the decoration manager is kept live then the context will try to keep it
|
||||
|
||||
@@ -85,8 +85,10 @@ class AggressiveDCEPass : public MemPass {
|
||||
}
|
||||
|
||||
// Adds entry points, execution modes and workgroup size decorations to the
|
||||
// worklist for processing with the first function.
|
||||
void InitializeModuleScopeLiveInstructions();
|
||||
// worklist for processing with the first function. Returns
|
||||
// Pass::Status::Failure if it could not create a required debug instruction.
|
||||
// Returns Pass::Status::SuccessWithoutChange otherwise.
|
||||
Pass::Status InitializeModuleScopeLiveInstructions();
|
||||
|
||||
// Add |inst| to worklist_ and live_insts_.
|
||||
void AddToWorklist(Instruction* inst) {
|
||||
@@ -136,7 +138,7 @@ class AggressiveDCEPass : public MemPass {
|
||||
// existing control structures will remain. This can leave not-insignificant
|
||||
// sequences of ultimately useless code.
|
||||
// TODO(): Remove useless control constructs.
|
||||
bool AggressiveDCE(Function* func);
|
||||
Pass::Status AggressiveDCE(Function* func);
|
||||
|
||||
Pass::Status ProcessImpl();
|
||||
|
||||
@@ -154,11 +156,17 @@ class AggressiveDCEPass : public MemPass {
|
||||
// marked as live in the work list. DebugDeclare's are marked live now, and
|
||||
// DebugValue Value operands are set to OpUndef. The work list will be empty
|
||||
// at the end.
|
||||
void ProcessDebugInformation(std::list<BasicBlock*>& structured_order);
|
||||
// Returns Pass::Status::Failure if it could not create an OpUndef.
|
||||
// Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
|
||||
Pass::Status ProcessDebugInformation(
|
||||
std::list<BasicBlock*>& structured_order);
|
||||
|
||||
// Kills any instructions in |func| that have not been marked as live.
|
||||
bool KillDeadInstructions(const Function* func,
|
||||
std::list<BasicBlock*>& structured_order);
|
||||
// Returns Pass::Status::Failure if it could not create an OpUndef.
|
||||
// Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
|
||||
// and Pass::Status::SuccessWithoutChange otherwise.
|
||||
Pass::Status KillDeadInstructions(const Function* func,
|
||||
std::list<BasicBlock*>& structured_order);
|
||||
|
||||
// Adds the instructions that define the operands of |inst| to the work list.
|
||||
void AddOperandsToWorkList(const Instruction* inst);
|
||||
|
||||
@@ -232,6 +232,7 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
|
||||
ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
|
||||
uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
|
||||
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
|
||||
|
||||
uint32_t quad_mask = ir_builder.GetUintConstantId(3);
|
||||
@@ -239,21 +240,25 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
|
||||
// This gives the offset in the group of 4 of this invocation.
|
||||
Instruction* quad_idx = ir_builder.AddBinaryOp(
|
||||
uint_type_id, spv::Op::OpBitwiseAnd, id->result_id(), quad_mask);
|
||||
if (quad_idx == nullptr) return false;
|
||||
|
||||
// Get the invocation id of the first invocation in the group of 4.
|
||||
Instruction* quad_ldr =
|
||||
ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseXor,
|
||||
id->result_id(), quad_idx->result_id());
|
||||
if (quad_ldr == nullptr) return false;
|
||||
|
||||
// Get the offset of the target invocation from the offset vector.
|
||||
Instruction* my_offset =
|
||||
ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpVectorExtractDynamic,
|
||||
offset_id, quad_idx->result_id());
|
||||
if (my_offset == nullptr) return false;
|
||||
|
||||
// Determine the index of the invocation to read from.
|
||||
Instruction* target_inv =
|
||||
ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpIAdd,
|
||||
quad_ldr->result_id(), my_offset->result_id());
|
||||
if (target_inv == nullptr) return false;
|
||||
|
||||
// Do the group operations
|
||||
uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
|
||||
@@ -264,9 +269,11 @@ bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst,
|
||||
{uint_max_id, uint_max_id, uint_max_id, uint_max_id});
|
||||
Instruction* ballot_value =
|
||||
const_mgr->GetDefiningInstruction(ballot_value_const);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* is_active = ir_builder.AddNaryOp(
|
||||
type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
|
||||
{subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* shuffle =
|
||||
ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
|
||||
{subgroup_scope, data_id, target_inv->result_id()});
|
||||
@@ -358,19 +365,24 @@ bool ReplaceSwizzleInvocationsMasked(
|
||||
ctx->get_def_use_mgr()->GetDef(var_inst->type_id());
|
||||
uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1);
|
||||
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* id = ir_builder.AddLoad(uint_type_id, var_id);
|
||||
|
||||
// Do the bitwise operations.
|
||||
uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0);
|
||||
Instruction* and_mask = ir_builder.AddBinaryOp(
|
||||
uint_type_id, spv::Op::OpBitwiseOr, uint_x, mask_extended);
|
||||
if (and_mask == nullptr) return false;
|
||||
Instruction* and_result =
|
||||
ir_builder.AddBinaryOp(uint_type_id, spv::Op::OpBitwiseAnd,
|
||||
id->result_id(), and_mask->result_id());
|
||||
if (and_result == nullptr) return false;
|
||||
Instruction* or_result = ir_builder.AddBinaryOp(
|
||||
uint_type_id, spv::Op::OpBitwiseOr, and_result->result_id(), uint_y);
|
||||
if (or_result == nullptr) return false;
|
||||
Instruction* target_inv = ir_builder.AddBinaryOp(
|
||||
uint_type_id, spv::Op::OpBitwiseXor, or_result->result_id(), uint_z);
|
||||
if (target_inv == nullptr) return false;
|
||||
|
||||
// Do the group operations
|
||||
uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF);
|
||||
@@ -381,9 +393,11 @@ bool ReplaceSwizzleInvocationsMasked(
|
||||
{uint_max_id, uint_max_id, uint_max_id, uint_max_id});
|
||||
Instruction* ballot_value =
|
||||
const_mgr->GetDefiningInstruction(ballot_value_const);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* is_active = ir_builder.AddNaryOp(
|
||||
type_mgr->GetBoolTypeId(), spv::Op::OpGroupNonUniformBallotBitExtract,
|
||||
{subgroup_scope, ballot_value->result_id(), target_inv->result_id()});
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* shuffle =
|
||||
ir_builder.AddNaryOp(inst->type_id(), spv::Op::OpGroupNonUniformShuffle,
|
||||
{subgroup_scope, data_id, target_inv->result_id()});
|
||||
@@ -435,6 +449,7 @@ bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst,
|
||||
InstructionBuilder ir_builder(
|
||||
ctx, inst,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* t =
|
||||
ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id);
|
||||
analysis::Bool bool_type;
|
||||
@@ -442,6 +457,7 @@ bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst,
|
||||
Instruction* cmp =
|
||||
ir_builder.AddBinaryOp(bool_type_id, spv::Op::OpIEqual, t->result_id(),
|
||||
inst->GetSingleWordInOperand(4));
|
||||
if (cmp == nullptr) return false;
|
||||
|
||||
// Build a select.
|
||||
inst->SetOpcode(spv::Op::OpSelect);
|
||||
@@ -517,6 +533,7 @@ bool ReplaceMbcnt(IRContext* context, Instruction* inst,
|
||||
Instruction* t =
|
||||
ir_builder.AddBinaryOp(mask_inst->type_id(), spv::Op::OpBitwiseAnd,
|
||||
bitcast->result_id(), mask_id);
|
||||
if (t == nullptr) return false;
|
||||
|
||||
inst->SetOpcode(spv::Op::OpBitCount);
|
||||
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}});
|
||||
@@ -598,8 +615,11 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
|
||||
const_mgr->GetDefiningInstruction(vec_const)->result_id();
|
||||
|
||||
// Extract the input values.
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
|
||||
// TODO(1-841): Handle id overflow.
|
||||
Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
|
||||
|
||||
// Negate the input values.
|
||||
@@ -621,10 +641,13 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
|
||||
// Find which values are negative. Used in later computations.
|
||||
Instruction* is_z_neg = ir_builder.AddBinaryOp(
|
||||
bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
|
||||
if (is_z_neg == nullptr) return false;
|
||||
Instruction* is_y_neg = ir_builder.AddBinaryOp(
|
||||
bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
|
||||
if (is_y_neg == nullptr) return false;
|
||||
Instruction* is_x_neg = ir_builder.AddBinaryOp(
|
||||
bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
|
||||
if (is_x_neg == nullptr) return false;
|
||||
|
||||
// Compute cubema
|
||||
Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
|
||||
@@ -635,36 +658,46 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
|
||||
{az->result_id(), amax_x_y->result_id()});
|
||||
Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, spv::Op::OpFMul,
|
||||
f2_const_id, amax->result_id());
|
||||
if (cubema == nullptr) return false;
|
||||
|
||||
// Do the comparisons needed for computing cubesc and cubetc.
|
||||
Instruction* is_z_max =
|
||||
ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
|
||||
az->result_id(), amax_x_y->result_id());
|
||||
if (is_z_max == nullptr) return false;
|
||||
Instruction* not_is_z_max = ir_builder.AddUnaryOp(
|
||||
bool_id, spv::Op::OpLogicalNot, is_z_max->result_id());
|
||||
Instruction* y_gr_x =
|
||||
ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
|
||||
ay->result_id(), ax->result_id());
|
||||
if (y_gr_x == nullptr) return false;
|
||||
Instruction* is_y_max =
|
||||
ir_builder.AddBinaryOp(bool_id, spv::Op::OpLogicalAnd,
|
||||
not_is_z_max->result_id(), y_gr_x->result_id());
|
||||
if (is_y_max == nullptr) return false;
|
||||
|
||||
// Select the correct value for cubesc.
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* cubesc_case_1 = ir_builder.AddSelect(
|
||||
float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id());
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* cubesc_case_2 = ir_builder.AddSelect(
|
||||
float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id());
|
||||
Instruction* sel =
|
||||
// TODO(1841): Handle id overflow.
|
||||
ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(),
|
||||
cubesc_case_2->result_id());
|
||||
Instruction* cubesc =
|
||||
// TODO(1841): Handle id overflow.
|
||||
ir_builder.AddSelect(float_type_id, is_z_max->result_id(),
|
||||
cubesc_case_1->result_id(), sel->result_id());
|
||||
|
||||
// Select the correct value for cubetc.
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* cubetc_case_1 = ir_builder.AddSelect(
|
||||
float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id());
|
||||
Instruction* cubetc =
|
||||
// TODO(1841): Handle id overflow.
|
||||
ir_builder.AddSelect(float_type_id, is_y_max->result_id(),
|
||||
cubetc_case_1->result_id(), ny->result_id());
|
||||
|
||||
@@ -675,6 +708,7 @@ bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst,
|
||||
v2_float_type_id, {cubema->result_id(), cubema->result_id()});
|
||||
Instruction* div = ir_builder.AddBinaryOp(
|
||||
v2_float_type_id, spv::Op::OpFDiv, cube->result_id(), denom->result_id());
|
||||
if (div == nullptr) return false;
|
||||
|
||||
// Get the final result by adding 0.5 to |div|.
|
||||
inst->SetOpcode(spv::Op::OpFAdd);
|
||||
@@ -746,8 +780,11 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
|
||||
uint32_t f5_const_id = const_mgr->GetFloatConstId(5.0);
|
||||
|
||||
// Extract the input values.
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0});
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1});
|
||||
// TODO(1-841): Handle id overflow.
|
||||
Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2});
|
||||
|
||||
// Get the absolute values of the inputs.
|
||||
@@ -761,10 +798,13 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
|
||||
// Find which values are negative. Used in later computations.
|
||||
Instruction* is_z_neg = ir_builder.AddBinaryOp(
|
||||
bool_id, spv::Op::OpFOrdLessThan, z->result_id(), f0_const_id);
|
||||
if (is_z_neg == nullptr) return false;
|
||||
Instruction* is_y_neg = ir_builder.AddBinaryOp(
|
||||
bool_id, spv::Op::OpFOrdLessThan, y->result_id(), f0_const_id);
|
||||
if (is_y_neg == nullptr) return false;
|
||||
Instruction* is_x_neg = ir_builder.AddBinaryOp(
|
||||
bool_id, spv::Op::OpFOrdLessThan, x->result_id(), f0_const_id);
|
||||
if (is_x_neg == nullptr) return false;
|
||||
|
||||
// Find the max value.
|
||||
Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction(
|
||||
@@ -773,20 +813,26 @@ bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst,
|
||||
Instruction* is_z_max =
|
||||
ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
|
||||
az->result_id(), amax_x_y->result_id());
|
||||
if (is_z_max == nullptr) return false;
|
||||
Instruction* y_gr_x =
|
||||
ir_builder.AddBinaryOp(bool_id, spv::Op::OpFOrdGreaterThanEqual,
|
||||
ay->result_id(), ax->result_id());
|
||||
if (y_gr_x == nullptr) return false;
|
||||
|
||||
// Get the value for each case.
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* case_z = ir_builder.AddSelect(
|
||||
float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* case_y = ir_builder.AddSelect(
|
||||
float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* case_x = ir_builder.AddSelect(
|
||||
float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id);
|
||||
|
||||
// Select the correct case.
|
||||
Instruction* sel =
|
||||
// TODO(1841): Handle id overflow.
|
||||
ir_builder.AddSelect(float_type_id, y_gr_x->result_id(),
|
||||
case_y->result_id(), case_x->result_id());
|
||||
|
||||
|
||||
14
3rdparty/spirv-tools/source/opt/basic_block.cpp
vendored
14
3rdparty/spirv-tools/source/opt/basic_block.cpp
vendored
@@ -29,11 +29,19 @@ constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
|
||||
} // namespace
|
||||
|
||||
BasicBlock* BasicBlock::Clone(IRContext* context) const {
|
||||
BasicBlock* clone = new BasicBlock(
|
||||
std::unique_ptr<Instruction>(GetLabelInst()->Clone(context)));
|
||||
Instruction* label_clone = GetLabelInst()->Clone(context);
|
||||
if (!label_clone) {
|
||||
return nullptr;
|
||||
}
|
||||
BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone));
|
||||
for (const auto& inst : insts_) {
|
||||
// Use the incoming context
|
||||
clone->AddInstruction(std::unique_ptr<Instruction>(inst.Clone(context)));
|
||||
Instruction* inst_clone = inst.Clone(context);
|
||||
if (!inst_clone) {
|
||||
delete clone;
|
||||
return nullptr;
|
||||
}
|
||||
clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone));
|
||||
}
|
||||
|
||||
if (context->AreAnalysesValid(
|
||||
|
||||
4
3rdparty/spirv-tools/source/opt/ccp_pass.cpp
vendored
4
3rdparty/spirv-tools/source/opt/ccp_pass.cpp
vendored
@@ -165,6 +165,9 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) {
|
||||
context()->get_instruction_folder().FoldInstructionToConstant(instr,
|
||||
map_func);
|
||||
|
||||
if (folded_inst && context()->id_overflow()) {
|
||||
return SSAPropagator::kFailed;
|
||||
}
|
||||
if (folded_inst != nullptr) {
|
||||
// We do not want to change the body of the function by adding new
|
||||
// instructions. When folding we can only generate new constants.
|
||||
@@ -376,6 +379,7 @@ Pass::Status CCPPass::Process() {
|
||||
// Process all entry point functions.
|
||||
ProcessFunction pfn = [this](Function* fp) { return PropagateConstants(fp); };
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
if (context()->id_overflow()) return Pass::Status::Failure;
|
||||
return modified ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
23
3rdparty/spirv-tools/source/opt/cfg.cpp
vendored
23
3rdparty/spirv-tools/source/opt/cfg.cpp
vendored
@@ -254,12 +254,13 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
|
||||
}
|
||||
|
||||
// Adjust the OpPhi instructions as needed.
|
||||
bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
|
||||
bool ok = bb->WhileEachPhiInst([latch_block, bb, new_header,
|
||||
context](Instruction* phi) -> bool {
|
||||
std::vector<uint32_t> preheader_phi_ops;
|
||||
std::vector<Operand> header_phi_ops;
|
||||
|
||||
// Identify where the original inputs to original OpPhi belong: header or
|
||||
// preheader.
|
||||
// Identify where the original inputs to original OpPhi belong: header
|
||||
// or preheader.
|
||||
for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
|
||||
uint32_t def_id = phi->GetSingleWordInOperand(i);
|
||||
uint32_t branch_id = phi->GetSingleWordInOperand(i + 1);
|
||||
@@ -272,21 +273,24 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a phi instruction if and only if the preheader_phi_ops has more
|
||||
// than one pair.
|
||||
// Create a phi instruction if and only if the preheader_phi_ops has
|
||||
// more than one pair.
|
||||
if (preheader_phi_ops.size() > 2) {
|
||||
InstructionBuilder builder(
|
||||
context, &*bb->begin(),
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
|
||||
Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops);
|
||||
if (!new_phi) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the OpPhi to the header bb.
|
||||
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}});
|
||||
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
|
||||
} else {
|
||||
// An OpPhi with a single entry is just a copy. In this case use the same
|
||||
// instruction in the new header.
|
||||
// An OpPhi with a single entry is just a copy. In this case use the
|
||||
// same instruction in the new header.
|
||||
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}});
|
||||
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
|
||||
}
|
||||
@@ -297,8 +301,13 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
|
||||
new_header->begin()->InsertBefore(std::move(phi_owner));
|
||||
context->set_instr_block(phi, new_header);
|
||||
context->AnalyzeUses(phi);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!ok) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Add a branch to the new header.
|
||||
InstructionBuilder branch_builder(
|
||||
context, bb,
|
||||
|
||||
@@ -158,6 +158,7 @@ bool CombineAccessChains::CombineIndices(Instruction* ptr_input,
|
||||
InstructionBuilder builder(
|
||||
context(), inst,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* addition = builder.AddIAdd(last_index_inst->type_id(),
|
||||
last_index_inst->result_id(),
|
||||
element_inst->result_id());
|
||||
|
||||
@@ -829,7 +829,9 @@ const analysis::Constant* FoldFPBinaryOp(
|
||||
// Build the constant object and return it.
|
||||
std::vector<uint32_t> ids;
|
||||
for (const analysis::Constant* member : results_components) {
|
||||
ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id());
|
||||
Instruction* def = const_mgr->GetDefiningInstruction(member);
|
||||
if (!def) return nullptr;
|
||||
ids.push_back(def->result_id());
|
||||
}
|
||||
return const_mgr->GetConstant(vector_type, ids);
|
||||
} else {
|
||||
@@ -1404,7 +1406,9 @@ ConstantFoldingRule FoldFMix() {
|
||||
}
|
||||
|
||||
if (is_vector) {
|
||||
uint32_t one_id = const_mgr->GetDefiningInstruction(one)->result_id();
|
||||
Instruction* one_inst = const_mgr->GetDefiningInstruction(one);
|
||||
if (one_inst == nullptr) return nullptr;
|
||||
uint32_t one_id = one_inst->result_id();
|
||||
one =
|
||||
const_mgr->GetConstant(result_type, std::vector<uint32_t>(4, one_id));
|
||||
}
|
||||
|
||||
@@ -75,6 +75,9 @@ analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) {
|
||||
analysis::Type* ConvertToHalfPass::FloatVectorType(uint32_t v_len,
|
||||
uint32_t width) {
|
||||
analysis::Type* reg_float_ty = FloatScalarType(width);
|
||||
if (reg_float_ty == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
analysis::Vector vec_ty(reg_float_ty, v_len);
|
||||
return context()->get_type_mgr()->GetRegisteredType(&vec_ty);
|
||||
}
|
||||
@@ -85,6 +88,9 @@ analysis::Type* ConvertToHalfPass::FloatMatrixType(uint32_t v_cnt,
|
||||
Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id);
|
||||
uint32_t v_len = vty_inst->GetSingleWordInOperand(1);
|
||||
analysis::Type* reg_vec_ty = FloatVectorType(v_len, width);
|
||||
if (reg_vec_ty == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
analysis::Matrix mat_ty(reg_vec_ty, v_cnt);
|
||||
return context()->get_type_mgr()->GetRegisteredType(&mat_ty);
|
||||
}
|
||||
@@ -99,6 +105,9 @@ uint32_t ConvertToHalfPass::EquivFloatTypeId(uint32_t ty_id, uint32_t width) {
|
||||
reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width);
|
||||
else // spv::Op::OpTypeFloat
|
||||
reg_equiv_ty = FloatScalarType(width);
|
||||
if (reg_equiv_ty == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty);
|
||||
}
|
||||
|
||||
@@ -107,6 +116,10 @@ void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
|
||||
Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp);
|
||||
uint32_t ty_id = val_inst->type_id();
|
||||
uint32_t nty_id = EquivFloatTypeId(ty_id, width);
|
||||
if (nty_id == 0) {
|
||||
status_ = Status::Failure;
|
||||
return;
|
||||
}
|
||||
if (nty_id == ty_id) return;
|
||||
Instruction* cvt_inst;
|
||||
InstructionBuilder builder(
|
||||
@@ -116,6 +129,10 @@ void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width,
|
||||
cvt_inst = builder.AddNullaryOp(nty_id, spv::Op::OpUndef);
|
||||
else
|
||||
cvt_inst = builder.AddUnaryOp(nty_id, spv::Op::OpFConvert, *val_idp);
|
||||
if (cvt_inst == nullptr) {
|
||||
status_ = Status::Failure;
|
||||
return;
|
||||
}
|
||||
*val_idp = cvt_inst->result_id();
|
||||
}
|
||||
|
||||
@@ -137,22 +154,43 @@ bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) {
|
||||
uint32_t orig_width = (cty_inst->GetSingleWordInOperand(0) == 16) ? 32 : 16;
|
||||
uint32_t orig_mat_id = inst->GetSingleWordInOperand(0);
|
||||
uint32_t orig_vty_id = EquivFloatTypeId(vty_id, orig_width);
|
||||
if (orig_vty_id == 0) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
std::vector<Operand> opnds = {};
|
||||
for (uint32_t vidx = 0; vidx < v_cnt; ++vidx) {
|
||||
Instruction* ext_inst = builder.AddIdLiteralOp(
|
||||
orig_vty_id, spv::Op::OpCompositeExtract, orig_mat_id, vidx);
|
||||
if (ext_inst == nullptr) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
Instruction* cvt_inst =
|
||||
builder.AddUnaryOp(vty_id, spv::Op::OpFConvert, ext_inst->result_id());
|
||||
if (cvt_inst == nullptr) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
opnds.push_back({SPV_OPERAND_TYPE_ID, {cvt_inst->result_id()}});
|
||||
}
|
||||
uint32_t mat_id = TakeNextId();
|
||||
if (mat_id == 0) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<Instruction> mat_inst(new Instruction(
|
||||
context(), spv::Op::OpCompositeConstruct, mty_id, mat_id, opnds));
|
||||
(void)builder.AddInstruction(std::move(mat_inst));
|
||||
context()->ReplaceAllUsesWith(inst->result_id(), mat_id);
|
||||
// Turn original instruction into copy so it is valid.
|
||||
uint32_t new_type_id = EquivFloatTypeId(mty_id, orig_width);
|
||||
if (new_type_id == 0) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
inst->SetOpcode(spv::Op::OpCopyObject);
|
||||
inst->SetResultType(EquivFloatTypeId(mty_id, orig_width));
|
||||
inst->SetResultType(new_type_id);
|
||||
get_def_use_mgr()->AnalyzeInstUse(inst);
|
||||
return true;
|
||||
}
|
||||
@@ -187,13 +225,24 @@ bool ConvertToHalfPass::GenHalfArith(Instruction* inst) {
|
||||
// Convert all float32 based operands to float16 equivalent and change
|
||||
// instruction type to float16 equivalent.
|
||||
inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
|
||||
if (status_ == Status::Failure) {
|
||||
return;
|
||||
}
|
||||
Instruction* op_inst = get_def_use_mgr()->GetDef(*idp);
|
||||
if (!IsFloat(op_inst, 32)) return;
|
||||
GenConvert(idp, 16, inst);
|
||||
modified = true;
|
||||
});
|
||||
if (status_ == Status::Failure) {
|
||||
return false;
|
||||
}
|
||||
if (IsFloat(inst, 32)) {
|
||||
inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
|
||||
uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16);
|
||||
if (new_type_id == 0) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
inst->SetResultType(new_type_id);
|
||||
converted_ids_.insert(inst->result_id());
|
||||
modified = true;
|
||||
}
|
||||
@@ -211,6 +260,9 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width,
|
||||
bool modified = false;
|
||||
inst->ForEachInId([&ocnt, &prev_idp, &from_width, &to_width, &modified,
|
||||
this](uint32_t* idp) {
|
||||
if (status_ == Status::Failure) {
|
||||
return;
|
||||
}
|
||||
if (ocnt % 2 == 0) {
|
||||
prev_idp = idp;
|
||||
} else {
|
||||
@@ -230,8 +282,16 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width,
|
||||
}
|
||||
++ocnt;
|
||||
});
|
||||
if (status_ == Status::Failure) {
|
||||
return false;
|
||||
}
|
||||
if (to_width == 16u) {
|
||||
inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16u));
|
||||
uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16u);
|
||||
if (new_type_id == 0) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
inst->SetResultType(new_type_id);
|
||||
converted_ids_.insert(inst->result_id());
|
||||
modified = true;
|
||||
}
|
||||
@@ -242,7 +302,12 @@ bool ConvertToHalfPass::ProcessPhi(Instruction* inst, uint32_t from_width,
|
||||
bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
|
||||
// If float32 and relaxed, change to float16 convert
|
||||
if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) {
|
||||
inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16));
|
||||
uint32_t new_type_id = EquivFloatTypeId(inst->type_id(), 16);
|
||||
if (new_type_id == 0) {
|
||||
status_ = Status::Failure;
|
||||
return false;
|
||||
}
|
||||
inst->SetResultType(new_type_id);
|
||||
get_def_use_mgr()->AnalyzeInstUse(inst);
|
||||
converted_ids_.insert(inst->result_id());
|
||||
}
|
||||
@@ -255,7 +320,7 @@ bool ConvertToHalfPass::ProcessConvert(Instruction* inst) {
|
||||
Instruction* val_inst = get_def_use_mgr()->GetDef(val_id);
|
||||
if (inst->type_id() == val_inst->type_id())
|
||||
inst->SetOpcode(spv::Op::OpCopyObject);
|
||||
return true; // modified
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
|
||||
@@ -265,6 +330,9 @@ bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) {
|
||||
uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx);
|
||||
if (converted_ids_.count(dref_id) > 0) {
|
||||
GenConvert(&dref_id, 32, inst);
|
||||
if (status_ == Status::Failure) {
|
||||
return false;
|
||||
}
|
||||
inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id});
|
||||
get_def_use_mgr()->AnalyzeInstUse(inst);
|
||||
modified = true;
|
||||
@@ -279,11 +347,17 @@ bool ConvertToHalfPass::ProcessDefault(Instruction* inst) {
|
||||
if (inst->opcode() == spv::Op::OpPhi) return ProcessPhi(inst, 16u, 32u);
|
||||
bool modified = false;
|
||||
inst->ForEachInId([&inst, &modified, this](uint32_t* idp) {
|
||||
if (status_ == Status::Failure) {
|
||||
return;
|
||||
}
|
||||
if (converted_ids_.count(*idp) == 0) return;
|
||||
uint32_t old_id = *idp;
|
||||
GenConvert(idp, 32, inst);
|
||||
if (*idp != old_id) modified = true;
|
||||
});
|
||||
if (status_ == Status::Failure) {
|
||||
return false;
|
||||
}
|
||||
if (modified) get_def_use_mgr()->AnalyzeInstUse(inst);
|
||||
return modified;
|
||||
}
|
||||
@@ -370,19 +444,38 @@ bool ConvertToHalfPass::ProcessFunction(Function* func) {
|
||||
});
|
||||
// Replace invalid converts of matrix into equivalent vector extracts,
|
||||
// converts and finally a composite construct
|
||||
bool ok = true;
|
||||
cfg()->ForEachBlockInReversePostOrder(
|
||||
func->entry().get(), [&modified, this](BasicBlock* bb) {
|
||||
for (auto ii = bb->begin(); ii != bb->end(); ++ii)
|
||||
modified |= MatConvertCleanup(&*ii);
|
||||
func->entry().get(), [&modified, &ok, this](BasicBlock* bb) {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
for (auto ii = bb->begin(); ii != bb->end(); ++ii) {
|
||||
bool Mmodified = MatConvertCleanup(&*ii);
|
||||
if (status_ == Status::Failure) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
modified |= Mmodified;
|
||||
}
|
||||
});
|
||||
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
Pass::Status ConvertToHalfPass::ProcessImpl() {
|
||||
status_ = Status::SuccessWithoutChange;
|
||||
Pass::ProcessFunction pfn = [this](Function* fp) {
|
||||
return ProcessFunction(fp);
|
||||
};
|
||||
bool modified = context()->ProcessReachableCallTree(pfn);
|
||||
if (status_ == Status::Failure) {
|
||||
return status_;
|
||||
}
|
||||
|
||||
// If modified, make sure module has Float16 capability
|
||||
if (modified) context()->AddCapability(spv::Capability::Float16);
|
||||
// Remove all RelaxedPrecision decorations from instructions and globals
|
||||
@@ -514,4 +607,4 @@ void ConvertToHalfPass::Initialize() {
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
} // namespace spvtools
|
||||
@@ -130,6 +130,9 @@ class ConvertToHalfPass : public Pass {
|
||||
}
|
||||
};
|
||||
|
||||
// The status of the pass.
|
||||
Pass::Status status_;
|
||||
|
||||
// Set of core operations to be processed
|
||||
std::unordered_set<spv::Op, hasher> target_ops_core_;
|
||||
|
||||
|
||||
@@ -246,9 +246,10 @@ Instruction* ConvertToSampledImagePass::CreateImageExtraction(
|
||||
InstructionBuilder builder(
|
||||
context(), sampled_image->NextNode(),
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
return builder.AddUnaryOp(
|
||||
Instruction* result = builder.AddUnaryOp(
|
||||
GetImageTypeOfSampledImage(context()->get_type_mgr(), sampled_image),
|
||||
spv::Op::OpImage, sampled_image->result_id());
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t ConvertToSampledImagePass::GetSampledImageTypeForImage(
|
||||
@@ -270,6 +271,9 @@ Instruction* ConvertToSampledImagePass::UpdateImageUses(
|
||||
if (uses_of_load.empty()) return nullptr;
|
||||
|
||||
auto* extracted_image = CreateImageExtraction(sampled_image_load);
|
||||
if (extracted_image == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
for (auto* user : uses_of_load) {
|
||||
user->SetInOperand(0, {extracted_image->result_id()});
|
||||
context()->get_def_use_mgr()->AnalyzeInstUse(user);
|
||||
@@ -306,8 +310,12 @@ void ConvertToSampledImagePass::UpdateSampledImageUses(
|
||||
def_use_mgr->AnalyzeInstUse(image_load);
|
||||
context()->KillInst(sampled_image_inst);
|
||||
} else {
|
||||
if (!image_extraction)
|
||||
if (!image_extraction) {
|
||||
image_extraction = CreateImageExtraction(image_load);
|
||||
if (image_extraction == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
sampled_image_inst->SetInOperand(0, {image_extraction->result_id()});
|
||||
def_use_mgr->AnalyzeInstUse(sampled_image_inst);
|
||||
}
|
||||
@@ -333,6 +341,9 @@ bool ConvertToSampledImagePass::ConvertImageVariableToSampledImage(
|
||||
// reference.
|
||||
uint32_t type_id = context()->get_type_mgr()->FindPointerToType(
|
||||
sampled_image_type_id, storage_class);
|
||||
if (type_id == 0) {
|
||||
return false;
|
||||
}
|
||||
MoveInstructionNextToType(image_variable, type_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -390,6 +390,7 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
|
||||
if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
|
||||
|
||||
uint32_t result_id = context()->TakeNextId();
|
||||
if (result_id == 0) return nullptr;
|
||||
std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
|
||||
context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
|
||||
result_id,
|
||||
|
||||
@@ -74,16 +74,19 @@ uint32_t FixFuncCallArgumentsPass::ReplaceAccessChainFuncCallArguments(
|
||||
op_type->result_id(), spv::StorageClass::Function);
|
||||
// Create new variable
|
||||
builder.SetInsertPoint(variable_insertion_point);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* var =
|
||||
builder.AddVariable(varType, uint32_t(spv::StorageClass::Function));
|
||||
// Load access chain to the new variable before function call
|
||||
builder.SetInsertPoint(func_call_inst);
|
||||
|
||||
uint32_t operand_id = operand_inst->result_id();
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* load = builder.AddLoad(op_type->result_id(), operand_id);
|
||||
builder.AddStore(var->result_id(), load->result_id());
|
||||
// Load return value to the acesschain after function call
|
||||
builder.SetInsertPoint(next_insert_point);
|
||||
// TODO(1841): Handle id overflow.
|
||||
load = builder.AddLoad(op_type->result_id(), var->result_id());
|
||||
builder.AddStore(operand_id, load->result_id());
|
||||
|
||||
|
||||
3
3rdparty/spirv-tools/source/opt/fold.cpp
vendored
3
3rdparty/spirv-tools/source/opt/fold.cpp
vendored
@@ -597,6 +597,9 @@ Instruction* InstructionFolder::FoldInstructionToConstant(
|
||||
const analysis::Constant* folded_const = nullptr;
|
||||
for (auto rule : GetConstantFoldingRules().GetRulesForInstruction(inst)) {
|
||||
folded_const = rule(context_, inst, constants);
|
||||
if (folded_const == nullptr && inst->context()->id_overflow()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (folded_const != nullptr) {
|
||||
Instruction* const_inst =
|
||||
const_mgr->GetDefiningInstruction(folded_const, inst->type_id());
|
||||
|
||||
@@ -105,9 +105,16 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() {
|
||||
// Constants will be added to id_to_const_val_ and const_val_to_id_ so
|
||||
// that we can use the new Normal Constants when folding following Spec
|
||||
// Constants.
|
||||
case spv::Op::OpSpecConstantOp:
|
||||
modified |= ProcessOpSpecConstantOp(&inst_iter);
|
||||
case spv::Op::OpSpecConstantOp: {
|
||||
const auto status = ProcessOpSpecConstantOp(&inst_iter);
|
||||
if (status == Status::Failure) {
|
||||
return Status::Failure;
|
||||
}
|
||||
if (status == Status::SuccessWithChange) {
|
||||
modified = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -115,7 +122,7 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() {
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
|
||||
Pass::Status FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
|
||||
Module::inst_iterator* pos) {
|
||||
Instruction* inst = &**pos;
|
||||
Instruction* folded_inst = nullptr;
|
||||
@@ -125,10 +132,17 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
|
||||
"SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type");
|
||||
|
||||
folded_inst = FoldWithInstructionFolder(pos);
|
||||
if (context()->id_overflow()) {
|
||||
return Status::Failure;
|
||||
}
|
||||
|
||||
if (!folded_inst) {
|
||||
folded_inst = DoComponentWiseOperation(pos);
|
||||
if (context()->id_overflow()) {
|
||||
return Status::Failure;
|
||||
}
|
||||
}
|
||||
if (!folded_inst) return false;
|
||||
if (!folded_inst) return Status::SuccessWithoutChange;
|
||||
|
||||
// Replace the original constant with the new folded constant, kill the
|
||||
// original constant.
|
||||
@@ -136,7 +150,7 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
|
||||
uint32_t old_id = inst->result_id();
|
||||
context()->ReplaceAllUsesWith(old_id, new_id);
|
||||
context()->KillDef(old_id);
|
||||
return true;
|
||||
return Status::SuccessWithChange;
|
||||
}
|
||||
|
||||
Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
|
||||
@@ -195,7 +209,11 @@ Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
|
||||
|
||||
if (need_to_clone) {
|
||||
new_const_inst = new_const_inst->Clone(context());
|
||||
new_const_inst->SetResultId(TakeNextId());
|
||||
uint32_t new_id = TakeNextId();
|
||||
if (new_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
new_const_inst->SetResultId(new_id);
|
||||
new_const_inst->InsertAfter(insert_pos);
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/opt/constants.h"
|
||||
#include "source/opt/def_use_manager.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
@@ -45,14 +46,14 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
|
||||
private:
|
||||
// Processes the OpSpecConstantOp instruction pointed by the given
|
||||
// instruction iterator, folds it to normal constants if possible. Returns
|
||||
// true if the spec constant is folded to normal constants. New instructions
|
||||
// will be inserted before the OpSpecConstantOp instruction pointed by the
|
||||
// instruction iterator. The instruction iterator, which is passed by
|
||||
// pointer, will still point to the original OpSpecConstantOp instruction. If
|
||||
// folding is done successfully, the original OpSpecConstantOp instruction
|
||||
// will be changed to Nop and new folded instruction will be inserted before
|
||||
// it.
|
||||
bool ProcessOpSpecConstantOp(Module::inst_iterator* pos);
|
||||
// kSuccess if the spec constant is folded to normal constants. New
|
||||
// instructions will be inserted before the OpSpecConstantOp instruction
|
||||
// pointed by the instruction iterator. The instruction iterator, which is
|
||||
// passed by pointer, will still point to the original OpSpecConstantOp
|
||||
// instruction. If folding is done successfully, the original OpSpecConstantOp
|
||||
// instruction will be changed to Nop and new folded instruction will be
|
||||
// inserted before it. Returns kFail if an id overflow occurs.
|
||||
Status ProcessOpSpecConstantOp(Module::inst_iterator* pos);
|
||||
|
||||
// Returns the result of folding the OpSpecConstantOp instruction
|
||||
// |inst_iter_ptr| using the instruction folder.
|
||||
@@ -62,7 +63,24 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
|
||||
// pointed by the given instruction iterator to a normal constant defining
|
||||
// instruction. Returns the pointer to the new constant defining instruction
|
||||
// if succeeded, otherwise return nullptr.
|
||||
// instruction if succeeded, otherwise return nullptr.
|
||||
Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr);
|
||||
|
||||
// Returns the next available id, or 0 if the id overflows.
|
||||
uint32_t TakeNextId() {
|
||||
uint32_t next_id = context()->TakeNextId();
|
||||
if (next_id == 0) {
|
||||
Fail() << "ID overflow. Try running compact-ids.";
|
||||
}
|
||||
return next_id;
|
||||
}
|
||||
|
||||
// Records failure for the current module and returns a stream for printing
|
||||
// diagnostics.
|
||||
spvtools::DiagnosticStream Fail() {
|
||||
return spvtools::DiagnosticStream({}, context()->consumer(), "",
|
||||
SPV_ERROR_INTERNAL);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
|
||||
154
3rdparty/spirv-tools/source/opt/folding_rules.cpp
vendored
154
3rdparty/spirv-tools/source/opt/folding_rules.cpp
vendored
@@ -176,12 +176,57 @@ std::vector<uint32_t> GetWordsFromNumericScalarOrVectorConstant(
|
||||
return GetWordsFromScalarIntConstant(int_constant);
|
||||
} else if (const auto* vec_constant = c->AsVectorConstant()) {
|
||||
std::vector<uint32_t> words;
|
||||
// Retrieve all the components as 32bit words.
|
||||
for (const auto* comp : vec_constant->GetComponents()) {
|
||||
auto comp_in_words =
|
||||
GetWordsFromNumericScalarOrVectorConstant(const_mgr, comp);
|
||||
words.insert(words.end(), comp_in_words.begin(), comp_in_words.end());
|
||||
}
|
||||
return words;
|
||||
|
||||
if (ElementWidth(c->type()) >= 32) {
|
||||
return words;
|
||||
}
|
||||
// Check the element width and concactenate if the width is less than 32.
|
||||
if (ElementWidth(c->type()) == 8) {
|
||||
assert(words.size() <= 4);
|
||||
// Each 32-bit word will comprise 4 8-bit integers.
|
||||
// reverse the order when compacting.
|
||||
uint32_t compacted_word = 0;
|
||||
for (int32_t i = static_cast<int32_t>(words.size()) - 1; i >= 0; --i) {
|
||||
compacted_word <<= 8;
|
||||
compacted_word |= words[i];
|
||||
}
|
||||
return {compacted_word};
|
||||
} else if (ElementWidth(c->type()) == 16) {
|
||||
assert(words.size() <= 4);
|
||||
std::vector<uint32_t> compacted_words;
|
||||
// Each 32-bit word will comprise 2 16-bit integers.
|
||||
// reverse the order pair-wise when compacting.
|
||||
for (uint32_t i = 0; i < words.size(); i += 2) {
|
||||
uint32_t word1 = words[i];
|
||||
uint32_t word2 = (i + 1 < words.size()) ? words[i + 1] : 0;
|
||||
uint32_t compacted_word = (word2 << 16) | word1;
|
||||
compacted_words.push_back(compacted_word);
|
||||
}
|
||||
return compacted_words;
|
||||
}
|
||||
assert(false && "Unhandled element width");
|
||||
} else if (c->AsNullConstant()) {
|
||||
uint32_t num_elements = 1;
|
||||
|
||||
if (const auto* vec_type = c->type()->AsVector()) {
|
||||
num_elements = vec_type->element_count();
|
||||
}
|
||||
|
||||
// We need to check the element width to determine how many 32-bit words are
|
||||
// needed.
|
||||
uint32_t element_width = ElementWidth(c->type());
|
||||
if (element_width < 32) {
|
||||
num_elements = (num_elements + 1) / 2;
|
||||
} else if (element_width == 64) {
|
||||
num_elements = num_elements * 2;
|
||||
}
|
||||
return std::vector<uint32_t>(num_elements, 0);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -634,6 +679,15 @@ uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr,
|
||||
case spv::Op::OpISub:
|
||||
FOLD_OP(-);
|
||||
break;
|
||||
case spv::Op::OpBitwiseXor:
|
||||
FOLD_OP(^);
|
||||
break;
|
||||
case spv::Op::OpBitwiseOr:
|
||||
FOLD_OP(|);
|
||||
break;
|
||||
case spv::Op::OpBitwiseAnd:
|
||||
FOLD_OP(&);
|
||||
break;
|
||||
default:
|
||||
assert(false && "Unexpected operation");
|
||||
break;
|
||||
@@ -1488,6 +1542,9 @@ bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
Instruction* new_add_inst = ir_builder.AddBinaryOp(
|
||||
inst->type_id(), inst->opcode(), factor0_1, factor1_1);
|
||||
if (!new_add_inst) {
|
||||
return false;
|
||||
}
|
||||
inst->SetOpcode(inst->opcode() == spv::Op::OpFAdd ? spv::Op::OpFMul
|
||||
: spv::Op::OpIMul);
|
||||
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}},
|
||||
@@ -2239,6 +2296,48 @@ FoldingRule BitCastScalarOrVector() {
|
||||
};
|
||||
}
|
||||
|
||||
FoldingRule BitReverseScalarOrVector() {
|
||||
return [](IRContext* context, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
assert(inst->opcode() == spv::Op::OpBitReverse && constants.size() == 1);
|
||||
if (constants[0] == nullptr) return false;
|
||||
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
assert(!HasFloatingPoint(type) &&
|
||||
"BitReverse cannot be applied to floating point types.");
|
||||
assert((type->AsInteger() || type->AsVector()) &&
|
||||
"BitReverse can only be applied to integer scalars or vectors.");
|
||||
assert((ElementWidth(type) == 32) &&
|
||||
"BitReverse can only be applied to integer types of width 32");
|
||||
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
std::vector<uint32_t> words =
|
||||
GetWordsFromNumericScalarOrVectorConstant(const_mgr, constants[0]);
|
||||
if (words.size() == 0) return false;
|
||||
|
||||
for (uint32_t& word : words) {
|
||||
// Reverse the bits in each word.
|
||||
word = ((word & 0x55555555) << 1) | ((word >> 1) & 0x55555555);
|
||||
word = ((word & 0x33333333) << 2) | ((word >> 2) & 0x33333333);
|
||||
word = ((word & 0x0F0F0F0F) << 4) | ((word >> 4) & 0x0F0F0F0F);
|
||||
word = ((word & 0x00FF00FF) << 8) | ((word >> 8) & 0x00FF00FF);
|
||||
word = (word << 16) | (word >> 16);
|
||||
}
|
||||
|
||||
const analysis::Constant* bitreversed_constant =
|
||||
ConvertWordsToNumericScalarOrVectorConstant(const_mgr, words, type);
|
||||
if (!bitreversed_constant) return false;
|
||||
|
||||
auto new_feeder_id =
|
||||
const_mgr->GetDefiningInstruction(bitreversed_constant, inst->type_id())
|
||||
->result_id();
|
||||
inst->SetOpcode(spv::Op::OpCopyObject);
|
||||
inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {new_feeder_id}}});
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
FoldingRule RedundantSelect() {
|
||||
// An OpSelect instruction where both values are the same or the condition is
|
||||
// constant can be replaced by one of the values
|
||||
@@ -2610,6 +2709,56 @@ FoldingRule RedundantBinaryLhs0To0(spv::Op op) {
|
||||
return RedundantBinaryOpWithZeroOperand(0, 0);
|
||||
}
|
||||
|
||||
FoldingRule ReassociateCommutiveOp() {
|
||||
return [](IRContext* context, Instruction* inst,
|
||||
const std::vector<const analysis::Constant*>& constants) {
|
||||
const analysis::Type* type =
|
||||
context->get_type_mgr()->GetType(inst->type_id());
|
||||
uint32_t width = ElementWidth(type);
|
||||
if (width != 32) return false;
|
||||
|
||||
analysis::ConstantManager* const_mgr = context->get_constant_mgr();
|
||||
const analysis::Constant* const_input1 = ConstInput(constants);
|
||||
if (!const_input1) return false;
|
||||
Instruction* other_inst = NonConstInput(context, constants[0], inst);
|
||||
|
||||
if (other_inst->opcode() == inst->opcode()) {
|
||||
std::vector<const analysis::Constant*> other_constants =
|
||||
const_mgr->GetOperandConstants(other_inst);
|
||||
const analysis::Constant* const_input2 = ConstInput(other_constants);
|
||||
if (!const_input2) return false;
|
||||
|
||||
Instruction* non_const_input =
|
||||
NonConstInput(context, other_constants[0], other_inst);
|
||||
uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(),
|
||||
const_input1, const_input2);
|
||||
|
||||
if (merged_id == 0) return false;
|
||||
inst->SetInOperands(
|
||||
{{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}},
|
||||
{SPV_OPERAND_TYPE_ID, {merged_id}}});
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// A | (b | C) = b | (A | C)
|
||||
// A ^ (b ^ C) = b ^ (A ^ C)
|
||||
// A & (b & C) = b & (A & C)
|
||||
// Where A and C are constants
|
||||
static const constexpr spv::Op ReassociateCommutiveBitwiseOps[] = {
|
||||
spv::Op::OpBitwiseOr, spv::Op::OpBitwiseXor, spv::Op::OpBitwiseAnd};
|
||||
FoldingRule ReassociateCommutiveBitwise(spv::Op op) {
|
||||
assert(std::find(std::begin(ReassociateCommutiveBitwiseOps),
|
||||
std::end(ReassociateCommutiveBitwiseOps),
|
||||
op) != std::end(ReassociateCommutiveBitwiseOps) &&
|
||||
"Wrong opcode.");
|
||||
(void)op;
|
||||
return ReassociateCommutiveOp();
|
||||
}
|
||||
|
||||
// Returns true if all elements in |c| are 1.
|
||||
bool IsAllInt1(const analysis::Constant* c) {
|
||||
if (auto composite = c->AsCompositeConstant()) {
|
||||
@@ -3013,12 +3162,15 @@ void FoldingRules::AddFoldingRules() {
|
||||
rules_[op].push_back(RedundantBinaryLhs0(op));
|
||||
for (auto op : RedundantBinaryLhs0To0Ops)
|
||||
rules_[op].push_back(RedundantBinaryLhs0To0(op));
|
||||
for (auto op : ReassociateCommutiveBitwiseOps)
|
||||
rules_[op].push_back(ReassociateCommutiveBitwise(op));
|
||||
rules_[spv::Op::OpSDiv].push_back(RedundantSUDiv());
|
||||
rules_[spv::Op::OpUDiv].push_back(RedundantSUDiv());
|
||||
rules_[spv::Op::OpSMod].push_back(RedundantSUMod());
|
||||
rules_[spv::Op::OpUMod].push_back(RedundantSUMod());
|
||||
|
||||
rules_[spv::Op::OpBitcast].push_back(BitCastScalarOrVector());
|
||||
rules_[spv::Op::OpBitReverse].push_back(BitReverseScalarOrVector());
|
||||
|
||||
rules_[spv::Op::OpCompositeConstruct].push_back(
|
||||
CompositeExtractFeedingConstruct);
|
||||
|
||||
4
3rdparty/spirv-tools/source/opt/function.cpp
vendored
4
3rdparty/spirv-tools/source/opt/function.cpp
vendored
@@ -40,6 +40,10 @@ Function* Function::Clone(IRContext* ctx) const {
|
||||
clone->blocks_.reserve(blocks_.size());
|
||||
for (const auto& b : blocks_) {
|
||||
std::unique_ptr<BasicBlock> bb(b->Clone(ctx));
|
||||
if (!bb) {
|
||||
delete clone;
|
||||
return nullptr;
|
||||
}
|
||||
clone->AddBasicBlock(std::move(bb));
|
||||
}
|
||||
|
||||
|
||||
2
3rdparty/spirv-tools/source/opt/function.h
vendored
2
3rdparty/spirv-tools/source/opt/function.h
vendored
@@ -48,7 +48,7 @@ class Function {
|
||||
|
||||
explicit Function(const Function& f) = delete;
|
||||
|
||||
// Creates a clone of the instruction in the given |context|
|
||||
// Creates a clone of the function in the given |context|
|
||||
//
|
||||
// The parent module will default to null and needs to be explicitly set by
|
||||
// the user.
|
||||
|
||||
90
3rdparty/spirv-tools/source/opt/graph.cpp
vendored
Normal file
90
3rdparty/spirv-tools/source/opt/graph.cpp
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2022-2025 Arm Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/opt/graph.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Graph* Graph::Clone(IRContext* ctx) const {
|
||||
Graph* clone = new Graph(std::unique_ptr<Instruction>(DefInst().Clone(ctx)));
|
||||
|
||||
clone->inputs_.reserve(inputs_.size());
|
||||
for (const auto& i : inputs()) {
|
||||
clone->AddInput(std::unique_ptr<Instruction>(i->Clone(ctx)));
|
||||
}
|
||||
|
||||
clone->insts_.reserve(insts_.size());
|
||||
for (const auto& i : instructions()) {
|
||||
clone->AddInstruction(std::unique_ptr<Instruction>(i->Clone(ctx)));
|
||||
}
|
||||
|
||||
clone->outputs_.reserve(outputs_.size());
|
||||
for (const auto& i : outputs()) {
|
||||
clone->AddOutput(std::unique_ptr<Instruction>(i->Clone(ctx)));
|
||||
}
|
||||
|
||||
clone->SetGraphEnd(std::unique_ptr<Instruction>(EndInst()->Clone(ctx)));
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
void Graph::ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts,
|
||||
bool run_on_non_semantic_insts) {
|
||||
(void)run_on_debug_line_insts;
|
||||
(void)run_on_non_semantic_insts;
|
||||
|
||||
f(def_inst_.get());
|
||||
|
||||
for (auto& inst : inputs_) {
|
||||
f(inst.get());
|
||||
}
|
||||
|
||||
for (auto& inst : insts_) {
|
||||
f(inst.get());
|
||||
}
|
||||
|
||||
for (auto& inst : outputs_) {
|
||||
f(inst.get());
|
||||
}
|
||||
|
||||
f(end_inst_.get());
|
||||
}
|
||||
|
||||
void Graph::ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts,
|
||||
bool run_on_non_semantic_insts) const {
|
||||
(void)run_on_debug_line_insts;
|
||||
(void)run_on_non_semantic_insts;
|
||||
|
||||
f(def_inst_.get());
|
||||
|
||||
for (auto& inst : inputs_) {
|
||||
f(inst.get());
|
||||
}
|
||||
|
||||
for (auto& inst : insts_) {
|
||||
f(inst.get());
|
||||
}
|
||||
|
||||
for (auto& inst : outputs_) {
|
||||
f(inst.get());
|
||||
}
|
||||
|
||||
f(end_inst_.get());
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
124
3rdparty/spirv-tools/source/opt/graph.h
vendored
Normal file
124
3rdparty/spirv-tools/source/opt/graph.h
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2022-2025 Arm Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef SOURCE_OPT_GRAPH_H_
|
||||
#define SOURCE_OPT_GRAPH_H_
|
||||
|
||||
#include "source/opt/instruction.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
struct Graph {
|
||||
// Creates a graph instance declared by the given OpGraph instruction
|
||||
// |def_inst|.
|
||||
inline explicit Graph(std::unique_ptr<Instruction> def_inst);
|
||||
explicit Graph(const Graph& f) = delete;
|
||||
|
||||
// Creates a clone of the graph in the given |context|
|
||||
//
|
||||
// The parent module will default to null and needs to be explicitly set by
|
||||
// the user.
|
||||
Graph* Clone(IRContext*) const;
|
||||
|
||||
// The OpGraph instruction that begins the definition of this graph.
|
||||
Instruction& DefInst() { return *def_inst_; }
|
||||
const Instruction& DefInst() const { return *def_inst_; }
|
||||
|
||||
// Appends an input to this graph.
|
||||
inline void AddInput(std::unique_ptr<Instruction> inst);
|
||||
|
||||
// Appends an instruction to this graph.
|
||||
inline void AddInstruction(std::unique_ptr<Instruction> inst);
|
||||
|
||||
// Appends an output to this graph.
|
||||
inline void AddOutput(std::unique_ptr<Instruction> inst);
|
||||
|
||||
// Saves the given graph end instruction.
|
||||
void SetGraphEnd(std::unique_ptr<Instruction> end_inst);
|
||||
|
||||
// Returns the given graph end instruction.
|
||||
inline Instruction* EndInst() { return end_inst_.get(); }
|
||||
inline const Instruction* EndInst() const { return end_inst_.get(); }
|
||||
|
||||
// Returns graph's id
|
||||
inline uint32_t result_id() const { return def_inst_->result_id(); }
|
||||
|
||||
// Returns graph's return type id
|
||||
inline uint32_t type_id() const { return def_inst_->type_id(); }
|
||||
|
||||
// Return a read-only reference to the instructions that define the body of
|
||||
// the graph.
|
||||
const std::vector<std::unique_ptr<Instruction>>& instructions() const {
|
||||
return insts_;
|
||||
}
|
||||
|
||||
// Return a read-only reference to the instructions that define the inputs
|
||||
// of the graph.
|
||||
const std::vector<std::unique_ptr<Instruction>>& inputs() const {
|
||||
return inputs_;
|
||||
}
|
||||
|
||||
// Return a read-only reference to the instructions that define the outputs
|
||||
// of the graph.
|
||||
const std::vector<std::unique_ptr<Instruction>>& outputs() const {
|
||||
return outputs_;
|
||||
}
|
||||
|
||||
// Runs the given function |f| on instructions in this graph, in order,
|
||||
// and optionally on debug line instructions that might precede them and
|
||||
// non-semantic instructions that succceed the function.
|
||||
void ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false,
|
||||
bool run_on_non_semantic_insts = false);
|
||||
void ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
bool run_on_debug_line_insts = false,
|
||||
bool run_on_non_semantic_insts = false) const;
|
||||
|
||||
private:
|
||||
// The OpGraph instruction that begins the definition of this graph.
|
||||
std::unique_ptr<Instruction> def_inst_;
|
||||
// All inputs to this graph.
|
||||
std::vector<std::unique_ptr<Instruction>> inputs_;
|
||||
// All instructions describing this graph
|
||||
std::vector<std::unique_ptr<Instruction>> insts_;
|
||||
// All outputs of this graph.
|
||||
std::vector<std::unique_ptr<Instruction>> outputs_;
|
||||
// The OpGraphEnd instruction.
|
||||
std::unique_ptr<Instruction> end_inst_;
|
||||
};
|
||||
|
||||
inline Graph::Graph(std::unique_ptr<Instruction> def_inst)
|
||||
: def_inst_(std::move(def_inst)) {}
|
||||
|
||||
inline void Graph::AddInput(std::unique_ptr<Instruction> inst) {
|
||||
inputs_.emplace_back(std::move(inst));
|
||||
}
|
||||
|
||||
inline void Graph::AddInstruction(std::unique_ptr<Instruction> inst) {
|
||||
insts_.emplace_back(std::move(inst));
|
||||
}
|
||||
|
||||
inline void Graph::AddOutput(std::unique_ptr<Instruction> inst) {
|
||||
outputs_.emplace_back(std::move(inst));
|
||||
}
|
||||
|
||||
inline void Graph::SetGraphEnd(std::unique_ptr<Instruction> end_inst) {
|
||||
end_inst_ = std::move(end_inst);
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_GRAPH_H_
|
||||
@@ -126,6 +126,7 @@ Pass::Status IfConversion::Process() {
|
||||
condition = SplatCondition(vec_data_ty, condition, &builder);
|
||||
}
|
||||
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* select = builder.AddSelect(phi->type_id(), condition,
|
||||
true_value->result_id(),
|
||||
false_value->result_id());
|
||||
@@ -205,6 +206,7 @@ uint32_t IfConversion::SplatCondition(analysis::Vector* vec_data_ty,
|
||||
uint32_t bool_vec_id =
|
||||
context()->get_type_mgr()->GetTypeInstruction(&bool_vec_ty);
|
||||
std::vector<uint32_t> ids(vec_data_ty->element_count(), cond);
|
||||
// TODO(1841): Handle id overflow.
|
||||
return builder->AddCompositeConstruct(bool_vec_id, ids)->result_id();
|
||||
}
|
||||
|
||||
|
||||
36
3rdparty/spirv-tools/source/opt/instruction.cpp
vendored
36
3rdparty/spirv-tools/source/opt/instruction.cpp
vendored
@@ -168,7 +168,13 @@ Instruction* Instruction::Clone(IRContext* c) const {
|
||||
clone->dbg_line_insts_ = dbg_line_insts_;
|
||||
for (auto& i : clone->dbg_line_insts_) {
|
||||
i.unique_id_ = c->TakeNextUniqueId();
|
||||
if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId());
|
||||
if (i.IsDebugLineInst()) {
|
||||
uint32_t new_id = c->TakeNextId();
|
||||
if (new_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
i.SetResultId(new_id);
|
||||
}
|
||||
}
|
||||
clone->dbg_scope_ = dbg_scope_;
|
||||
return clone;
|
||||
@@ -546,27 +552,37 @@ void Instruction::ClearDbgLineInsts() {
|
||||
clear_dbg_line_insts();
|
||||
}
|
||||
|
||||
void Instruction::UpdateDebugInfoFrom(const Instruction* from,
|
||||
bool Instruction::UpdateDebugInfoFrom(const Instruction* from,
|
||||
const Instruction* line) {
|
||||
if (from == nullptr) return;
|
||||
if (from == nullptr) return true;
|
||||
ClearDbgLineInsts();
|
||||
const Instruction* fromLine = line != nullptr ? line : from;
|
||||
if (!fromLine->dbg_line_insts().empty())
|
||||
AddDebugLine(&fromLine->dbg_line_insts().back());
|
||||
if (!fromLine->dbg_line_insts().empty()) {
|
||||
if (!AddDebugLine(&fromLine->dbg_line_insts().back())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SetDebugScope(from->GetDebugScope());
|
||||
if (!IsLineInst() &&
|
||||
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
|
||||
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Instruction::AddDebugLine(const Instruction* inst) {
|
||||
bool Instruction::AddDebugLine(const Instruction* inst) {
|
||||
dbg_line_insts_.push_back(*inst);
|
||||
dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId();
|
||||
if (inst->IsDebugLineInst())
|
||||
dbg_line_insts_.back().SetResultId(context_->TakeNextId());
|
||||
if (inst->IsDebugLineInst()) {
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
if (new_id == 0) {
|
||||
return false;
|
||||
}
|
||||
dbg_line_insts_.back().SetResultId(new_id);
|
||||
}
|
||||
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse))
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Instruction::IsDebugLineInst() const {
|
||||
@@ -770,7 +786,7 @@ bool Instruction::IsFoldableByFoldScalar() const {
|
||||
// Even if the type of the instruction is foldable, its operands may not be
|
||||
// foldable (e.g., comparisons of 64bit types). Check that all operand types
|
||||
// are foldable before accepting the instruction.
|
||||
return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
|
||||
return WhileEachInId([&folder, this](const uint32_t* op_id) {
|
||||
Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
|
||||
Instruction* def_inst_type =
|
||||
context()->get_def_use_mgr()->GetDef(def_inst->type_id());
|
||||
@@ -792,7 +808,7 @@ bool Instruction::IsFoldableByFoldVector() const {
|
||||
// Even if the type of the instruction is foldable, its operands may not be
|
||||
// foldable (e.g., comparisons of 64bit types). Check that all operand types
|
||||
// are foldable before accepting the instruction.
|
||||
return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
|
||||
return WhileEachInId([&folder, this](const uint32_t* op_id) {
|
||||
Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
|
||||
Instruction* def_inst_type =
|
||||
context()->get_def_use_mgr()->GetDef(def_inst->type_id());
|
||||
|
||||
@@ -318,7 +318,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
inline void SetDebugScope(const DebugScope& scope);
|
||||
inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
|
||||
// Add debug line inst. Renew result id if Debug[No]Line
|
||||
void AddDebugLine(const Instruction* inst);
|
||||
bool AddDebugLine(const Instruction* inst);
|
||||
// Updates DebugInlinedAt of DebugScope and OpLine.
|
||||
void UpdateDebugInlinedAt(uint32_t new_inlined_at);
|
||||
// Clear line-related debug instructions attached to this instruction
|
||||
@@ -338,7 +338,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
|
||||
// Updates lexical scope of DebugScope and OpLine.
|
||||
void UpdateLexicalScope(uint32_t scope);
|
||||
// Updates OpLine and DebugScope based on the information of |from|.
|
||||
void UpdateDebugInfoFrom(const Instruction* from,
|
||||
bool UpdateDebugInfoFrom(const Instruction* from,
|
||||
const Instruction* line = nullptr);
|
||||
// Remove the |index|-th operand
|
||||
void RemoveOperand(uint32_t index) {
|
||||
|
||||
98
3rdparty/spirv-tools/source/opt/ir_builder.h
vendored
98
3rdparty/spirv-tools/source/opt/ir_builder.h
vendored
@@ -166,10 +166,14 @@ class InstructionBuilder {
|
||||
for (size_t i = 0; i < operands.size(); i++) {
|
||||
ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}});
|
||||
}
|
||||
// TODO(1841): Handle id overflow.
|
||||
std::unique_ptr<Instruction> new_inst(new Instruction(
|
||||
GetContext(), opcode, type_id,
|
||||
result != 0 ? result : GetContext()->TakeNextId(), ops));
|
||||
if (result == 0) {
|
||||
result = GetContext()->TakeNextId();
|
||||
if (result == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
std::unique_ptr<Instruction> new_inst(
|
||||
new Instruction(GetContext(), opcode, type_id, result, ops));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
@@ -297,9 +301,12 @@ class InstructionBuilder {
|
||||
// The id |op1| is the left hand side of the operation.
|
||||
// The id |op2| is the right hand side of the operation.
|
||||
Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) {
|
||||
// TODO(1841): Handle id overflow.
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> inst(new Instruction(
|
||||
GetContext(), spv::Op::OpIAdd, type, GetContext()->TakeNextId(),
|
||||
GetContext(), spv::Op::OpIAdd, type, result_id,
|
||||
{{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
|
||||
return AddInstruction(std::move(inst));
|
||||
}
|
||||
@@ -311,9 +318,12 @@ class InstructionBuilder {
|
||||
Instruction* AddULessThan(uint32_t op1, uint32_t op2) {
|
||||
analysis::Bool bool_type;
|
||||
uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
|
||||
// TODO(1841): Handle id overflow.
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> inst(new Instruction(
|
||||
GetContext(), spv::Op::OpULessThan, type, GetContext()->TakeNextId(),
|
||||
GetContext(), spv::Op::OpULessThan, type, result_id,
|
||||
{{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
|
||||
return AddInstruction(std::move(inst));
|
||||
}
|
||||
@@ -325,9 +335,12 @@ class InstructionBuilder {
|
||||
Instruction* AddSLessThan(uint32_t op1, uint32_t op2) {
|
||||
analysis::Bool bool_type;
|
||||
uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type);
|
||||
// TODO(1841): Handle id overflow.
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> inst(new Instruction(
|
||||
GetContext(), spv::Op::OpSLessThan, type, GetContext()->TakeNextId(),
|
||||
GetContext(), spv::Op::OpSLessThan, type, result_id,
|
||||
{{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}));
|
||||
return AddInstruction(std::move(inst));
|
||||
}
|
||||
@@ -355,9 +368,12 @@ class InstructionBuilder {
|
||||
// bool) for |type|.
|
||||
Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value,
|
||||
uint32_t false_value) {
|
||||
// TODO(1841): Handle id overflow.
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> select(new Instruction(
|
||||
GetContext(), spv::Op::OpSelect, type, GetContext()->TakeNextId(),
|
||||
GetContext(), spv::Op::OpSelect, type, result_id,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_ID, {cond}},
|
||||
{SPV_OPERAND_TYPE_ID, {true_value}},
|
||||
{SPV_OPERAND_TYPE_ID, {false_value}}}));
|
||||
@@ -381,10 +397,12 @@ class InstructionBuilder {
|
||||
ops.emplace_back(SPV_OPERAND_TYPE_ID,
|
||||
std::initializer_list<uint32_t>{id});
|
||||
}
|
||||
// TODO(1841): Handle id overflow.
|
||||
std::unique_ptr<Instruction> construct(
|
||||
new Instruction(GetContext(), spv::Op::OpCompositeConstruct, type,
|
||||
GetContext()->TakeNextId(), ops));
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> construct(new Instruction(
|
||||
GetContext(), spv::Op::OpCompositeConstruct, type, result_id, ops));
|
||||
return AddInstruction(std::move(construct));
|
||||
}
|
||||
|
||||
@@ -466,10 +484,12 @@ class InstructionBuilder {
|
||||
operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}});
|
||||
}
|
||||
|
||||
// TODO(1841): Handle id overflow.
|
||||
std::unique_ptr<Instruction> new_inst(
|
||||
new Instruction(GetContext(), spv::Op::OpCompositeExtract, type,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> new_inst(new Instruction(
|
||||
GetContext(), spv::Op::OpCompositeExtract, type, result_id, operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
@@ -493,9 +513,12 @@ class InstructionBuilder {
|
||||
operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}});
|
||||
}
|
||||
|
||||
// TODO(1841): Handle id overflow.
|
||||
std::unique_ptr<Instruction> new_inst(new Instruction(
|
||||
GetContext(), opcode, type_id, GetContext()->TakeNextId(), operands));
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> new_inst(
|
||||
new Instruction(GetContext(), opcode, type_id, result_id, operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
@@ -521,29 +544,36 @@ class InstructionBuilder {
|
||||
operands.push_back({SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, {alignment}});
|
||||
}
|
||||
|
||||
// TODO(1841): Handle id overflow.
|
||||
std::unique_ptr<Instruction> new_inst(
|
||||
new Instruction(GetContext(), spv::Op::OpLoad, type_id,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> new_inst(new Instruction(
|
||||
GetContext(), spv::Op::OpLoad, type_id, result_id, operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
Instruction* AddCopyObject(uint32_t type_id, uint32_t value_id) {
|
||||
std::vector<Operand> operands{{SPV_OPERAND_TYPE_ID, {value_id}}};
|
||||
|
||||
// TODO(1841): Handle id overflow.
|
||||
std::unique_ptr<Instruction> new_inst(
|
||||
new Instruction(GetContext(), spv::Op::OpCopyObject, type_id,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> new_inst(new Instruction(
|
||||
GetContext(), spv::Op::OpCopyObject, type_id, result_id, operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
Instruction* AddVariable(uint32_t type_id, uint32_t storage_class) {
|
||||
std::vector<Operand> operands;
|
||||
operands.push_back({SPV_OPERAND_TYPE_STORAGE_CLASS, {storage_class}});
|
||||
std::unique_ptr<Instruction> new_inst(
|
||||
new Instruction(GetContext(), spv::Op::OpVariable, type_id,
|
||||
GetContext()->TakeNextId(), operands));
|
||||
uint32_t result_id = GetContext()->TakeNextId();
|
||||
if (result_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<Instruction> new_inst(new Instruction(
|
||||
GetContext(), spv::Op::OpVariable, type_id, result_id, operands));
|
||||
return AddInstruction(std::move(new_inst));
|
||||
}
|
||||
|
||||
|
||||
14
3rdparty/spirv-tools/source/opt/ir_context.cpp
vendored
14
3rdparty/spirv-tools/source/opt/ir_context.cpp
vendored
@@ -93,6 +93,9 @@ void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) {
|
||||
if (set & kAnalysisLiveness) {
|
||||
BuildLivenessManager();
|
||||
}
|
||||
if (set & kAnalysisIdToGraphMapping) {
|
||||
BuildIdToGraphMapping();
|
||||
}
|
||||
}
|
||||
|
||||
void IRContext::InvalidateAnalysesExceptFor(
|
||||
@@ -164,6 +167,9 @@ void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) {
|
||||
if (analyses_to_invalidate & kAnalysisDebugInfo) {
|
||||
debug_info_mgr_.reset(nullptr);
|
||||
}
|
||||
if (analyses_to_invalidate & kAnalysisIdToGraphMapping) {
|
||||
id_to_graph_.clear();
|
||||
}
|
||||
|
||||
valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate);
|
||||
}
|
||||
@@ -393,6 +399,14 @@ bool IRContext::IsConsistent() {
|
||||
}
|
||||
}
|
||||
|
||||
if (AreAnalysesValid(kAnalysisIdToGraphMapping)) {
|
||||
for (auto& g : module_->graphs()) {
|
||||
if (id_to_graph_[g->DefInst().result_id()] != g.get()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) {
|
||||
for (auto& func : *module()) {
|
||||
for (auto& block : func) {
|
||||
|
||||
53
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
53
3rdparty/spirv-tools/source/opt/ir_context.h
vendored
@@ -84,7 +84,8 @@ class IRContext {
|
||||
kAnalysisTypes = 1 << 15,
|
||||
kAnalysisDebugInfo = 1 << 16,
|
||||
kAnalysisLiveness = 1 << 17,
|
||||
kAnalysisEnd = 1 << 18
|
||||
kAnalysisIdToGraphMapping = 1 << 18,
|
||||
kAnalysisEnd = 1 << 19
|
||||
};
|
||||
|
||||
using ProcessFunction = std::function<bool(Function*)>;
|
||||
@@ -109,7 +110,8 @@ class IRContext {
|
||||
id_to_name_(nullptr),
|
||||
max_id_bound_(kDefaultMaxIdBound),
|
||||
preserve_bindings_(false),
|
||||
preserve_spec_constants_(false) {
|
||||
preserve_spec_constants_(false),
|
||||
id_overflow_(false) {
|
||||
SetContextMessageConsumer(syntax_context_, consumer_);
|
||||
module_->SetContext(this);
|
||||
}
|
||||
@@ -127,7 +129,8 @@ class IRContext {
|
||||
id_to_name_(nullptr),
|
||||
max_id_bound_(kDefaultMaxIdBound),
|
||||
preserve_bindings_(false),
|
||||
preserve_spec_constants_(false) {
|
||||
preserve_spec_constants_(false),
|
||||
id_overflow_(false) {
|
||||
SetContextMessageConsumer(syntax_context_, consumer_);
|
||||
module_->SetContext(this);
|
||||
InitializeCombinators();
|
||||
@@ -563,6 +566,7 @@ class IRContext {
|
||||
inline uint32_t TakeNextId() {
|
||||
uint32_t next_id = module()->TakeNextIdBound();
|
||||
if (next_id == 0) {
|
||||
id_overflow_ = true;
|
||||
if (consumer()) {
|
||||
std::string message = "ID overflow. Try running compact-ids.";
|
||||
consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
|
||||
@@ -583,6 +587,13 @@ class IRContext {
|
||||
return next_id;
|
||||
}
|
||||
|
||||
// Returns true if an ID overflow has occurred since the last time the flag
|
||||
// was cleared.
|
||||
bool id_overflow() const { return id_overflow_; }
|
||||
|
||||
// Clears the ID overflow flag.
|
||||
void clear_id_overflow() { id_overflow_ = false; }
|
||||
|
||||
FeatureManager* get_feature_mgr() {
|
||||
if (!feature_mgr_.get()) {
|
||||
AnalyzeFeatures();
|
||||
@@ -641,6 +652,23 @@ class IRContext {
|
||||
return GetFunction(inst->result_id());
|
||||
}
|
||||
|
||||
// Returns the graph whose id is |id|, if one exists. Returns |nullptr|
|
||||
// otherwise.
|
||||
Graph* GetGraph(uint32_t id) {
|
||||
if (!AreAnalysesValid(kAnalysisIdToGraphMapping)) {
|
||||
BuildIdToGraphMapping();
|
||||
}
|
||||
auto entry = id_to_graph_.find(id);
|
||||
return (entry != id_to_graph_.end()) ? entry->second : nullptr;
|
||||
}
|
||||
|
||||
Graph* GetGraph(Instruction* inst) {
|
||||
if (inst->opcode() != spv::Op::OpGraphARM) {
|
||||
return nullptr;
|
||||
}
|
||||
return GetGraph(inst->result_id());
|
||||
}
|
||||
|
||||
// Add to |todo| all ids of functions called directly from |func|.
|
||||
void AddCalls(const Function* func, std::queue<uint32_t>* todo);
|
||||
|
||||
@@ -719,6 +747,15 @@ class IRContext {
|
||||
valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping;
|
||||
}
|
||||
|
||||
// Builds the instruction-graph map for the whole module.
|
||||
void BuildIdToGraphMapping() {
|
||||
id_to_graph_.clear();
|
||||
for (auto& g : module_->graphs()) {
|
||||
id_to_graph_[g->DefInst().result_id()] = g.get();
|
||||
}
|
||||
valid_analyses_ = valid_analyses_ | kAnalysisIdToGraphMapping;
|
||||
}
|
||||
|
||||
void BuildDecorationManager() {
|
||||
decoration_mgr_ = MakeUnique<analysis::DecorationManager>(module());
|
||||
valid_analyses_ = valid_analyses_ | kAnalysisDecorations;
|
||||
@@ -872,6 +909,13 @@ class IRContext {
|
||||
// iterators to traverse instructions.
|
||||
std::unordered_map<uint32_t, Function*> id_to_func_;
|
||||
|
||||
// A map from ids to the graph they define. This mapping is
|
||||
// built on-demand when GetGraph() is called.
|
||||
//
|
||||
// NOTE: Do not traverse this map. Ever. Use the graph iterators to
|
||||
// traverse instructions.
|
||||
std::unordered_map<uint32_t, Graph*> id_to_graph_;
|
||||
|
||||
// A bitset indicating which analyzes are currently valid.
|
||||
Analysis valid_analyses_;
|
||||
|
||||
@@ -930,6 +974,9 @@ class IRContext {
|
||||
// Whether all specialization constants within |module_|
|
||||
// should be preserved.
|
||||
bool preserve_spec_constants_;
|
||||
|
||||
// Set to true if TakeNextId() fails.
|
||||
bool id_overflow_;
|
||||
};
|
||||
|
||||
inline IRContext::Analysis operator|(IRContext::Analysis lhs,
|
||||
|
||||
36
3rdparty/spirv-tools/source/opt/ir_loader.cpp
vendored
36
3rdparty/spirv-tools/source/opt/ir_loader.cpp
vendored
@@ -178,6 +178,40 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
|
||||
last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt);
|
||||
last_line_inst_.reset();
|
||||
dbg_line_info_.clear();
|
||||
} else if (opcode == spv::Op::OpGraphARM) {
|
||||
if (graph_ != nullptr) {
|
||||
Error(consumer_, src, loc, "graph inside graph");
|
||||
return false;
|
||||
}
|
||||
graph_ = MakeUnique<Graph>(std::move(spv_inst));
|
||||
} else if (opcode == spv::Op::OpGraphEndARM) {
|
||||
if (graph_ == nullptr) {
|
||||
Error(consumer_, src, loc,
|
||||
"OpGraphEndARM without corresponding OpGraphARM");
|
||||
return false;
|
||||
}
|
||||
graph_->SetGraphEnd(std::move(spv_inst));
|
||||
module_->AddGraph(std::move(graph_));
|
||||
graph_ = nullptr;
|
||||
} else if (opcode == spv::Op::OpGraphConstantARM) {
|
||||
module_->AddGlobalValue(std::move(spv_inst));
|
||||
} else if (graph_ != nullptr) {
|
||||
if (opcode == spv::Op::OpGraphInputARM) {
|
||||
graph_->AddInput(std::move(spv_inst));
|
||||
} else if (opcode == spv::Op::OpGraphSetOutputARM) {
|
||||
graph_->AddOutput(std::move(spv_inst));
|
||||
} else {
|
||||
switch (opcode) {
|
||||
case spv::Op::OpExtInst:
|
||||
case spv::Op::OpCompositeExtract:
|
||||
graph_->AddInstruction(std::move(spv_inst));
|
||||
break;
|
||||
default:
|
||||
Errorf(consumer_, src, loc,
|
||||
"unhandled instruction (opcode %d) inside graph", opcode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (function_ == nullptr) { // Outside function definition
|
||||
SPIRV_ASSERT(consumer_, block_ == nullptr);
|
||||
@@ -195,6 +229,8 @@ bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) {
|
||||
module_->SetSampledImageAddressMode(std::move(spv_inst));
|
||||
} else if (opcode == spv::Op::OpEntryPoint) {
|
||||
module_->AddEntryPoint(std::move(spv_inst));
|
||||
} else if (opcode == spv::Op::OpGraphEntryPointARM) {
|
||||
module_->AddGraphEntryPoint(std::move(spv_inst));
|
||||
} else if (opcode == spv::Op::OpExecutionMode ||
|
||||
opcode == spv::Op::OpExecutionModeId) {
|
||||
module_->AddExecutionMode(std::move(spv_inst));
|
||||
|
||||
3
3rdparty/spirv-tools/source/opt/ir_loader.h
vendored
3
3rdparty/spirv-tools/source/opt/ir_loader.h
vendored
@@ -20,6 +20,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "source/opt/basic_block.h"
|
||||
#include "source/opt/graph.h"
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
@@ -80,6 +81,8 @@ class IrLoader {
|
||||
std::unique_ptr<Function> function_;
|
||||
// The current BasicBlock under construction.
|
||||
std::unique_ptr<BasicBlock> block_;
|
||||
// The current Graph under construction.
|
||||
std::unique_ptr<Graph> graph_;
|
||||
// Line related debug instructions accumulated thus far.
|
||||
std::vector<Instruction> dbg_line_info_;
|
||||
// If doing extra line tracking, this is the line instruction that should be
|
||||
|
||||
@@ -118,7 +118,6 @@ bool LICMPass::IsImmediatelyContainedInLoop(Loop* loop, Function* f,
|
||||
}
|
||||
|
||||
bool LICMPass::HoistInstruction(Loop* loop, Instruction* inst) {
|
||||
// TODO(1841): Handle failure to create pre-header.
|
||||
BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock();
|
||||
if (!pre_header_bb) {
|
||||
return false;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "source/opt/dominator_tree.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/iterator.h"
|
||||
#include "source/opt/pass.h"
|
||||
#include "source/opt/tree_iterator.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
@@ -278,6 +279,9 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock() {
|
||||
|
||||
CFG* cfg = context_->cfg();
|
||||
loop_header_ = cfg->SplitLoopHeader(loop_header_);
|
||||
if (loop_header_ == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return loop_preheader_;
|
||||
}
|
||||
|
||||
@@ -920,18 +924,19 @@ Instruction* Loop::FindConditionVariable(
|
||||
return induction;
|
||||
}
|
||||
|
||||
bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
|
||||
auto modified = false;
|
||||
LoopDescriptor::Status LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
|
||||
bool modified = false;
|
||||
|
||||
for (auto& loop : *this) {
|
||||
if (!loop.GetPreHeaderBlock()) {
|
||||
if (!loop.GetOrCreatePreHeaderBlock()) {
|
||||
return Status::kFailure;
|
||||
}
|
||||
modified = true;
|
||||
// TODO(1841): Handle failure to create pre-header.
|
||||
loop.GetOrCreatePreHeaderBlock();
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
return modified ? Status::kSuccessWithChange : Status::kSuccessWithoutChange;
|
||||
}
|
||||
|
||||
// Add and remove loops which have been marked for addition and removal to
|
||||
|
||||
@@ -425,6 +425,9 @@ class LoopDescriptor {
|
||||
using pre_iterator = TreeDFIterator<Loop>;
|
||||
using const_pre_iterator = TreeDFIterator<const Loop>;
|
||||
|
||||
// The status of processing a module.
|
||||
enum class Status { kSuccessWithChange, kSuccessWithoutChange, kFailure };
|
||||
|
||||
// Creates a loop object for all loops found in |f|.
|
||||
LoopDescriptor(IRContext* context, const Function* f);
|
||||
|
||||
@@ -506,9 +509,11 @@ class LoopDescriptor {
|
||||
loops_to_add_.emplace_back(std::make_pair(parent, std::move(loop_to_add)));
|
||||
}
|
||||
|
||||
// Checks all loops in |this| and will create pre-headers for all loops
|
||||
// that don't have one. Returns |true| if any blocks were created.
|
||||
bool CreatePreHeaderBlocksIfMissing();
|
||||
// Creates pre-header blocks for all loops in the function that do not have
|
||||
// one. Returns `LoopDescriptor::Status::kSuccessWithChange` if any change is
|
||||
// made, `LoopDescriptor::Status::kSuccessWithoutChange` if no change is made,
|
||||
// and `LoopDescriptor::Status::kFailure` if it fails to create a pre-header.
|
||||
Status CreatePreHeaderBlocksIfMissing();
|
||||
|
||||
// Should be called to preserve the LoopAnalysis after loops have been marked
|
||||
// for addition with AddLoop or MarkLoopForRemoval.
|
||||
|
||||
@@ -22,23 +22,27 @@ namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status LoopFusionPass::Process() {
|
||||
bool modified = false;
|
||||
Status status = Status::SuccessWithoutChange;
|
||||
Module* module = context()->module();
|
||||
|
||||
// Process each function in the module
|
||||
for (Function& f : *module) {
|
||||
modified |= ProcessFunction(&f);
|
||||
status = CombineStatus(status, ProcessFunction(&f));
|
||||
if (status == Status::Failure) return Status::Failure;
|
||||
}
|
||||
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
return status;
|
||||
}
|
||||
|
||||
bool LoopFusionPass::ProcessFunction(Function* function) {
|
||||
Pass::Status LoopFusionPass::ProcessFunction(Function* function) {
|
||||
LoopDescriptor& ld = *context()->GetLoopDescriptor(function);
|
||||
|
||||
// If a loop doesn't have a preheader needs then it needs to be created. Make
|
||||
// sure to return Status::SuccessWithChange in that case.
|
||||
auto modified = ld.CreatePreHeaderBlocksIfMissing();
|
||||
bool modified = false;
|
||||
auto status = ld.CreatePreHeaderBlocksIfMissing();
|
||||
if (status == LoopDescriptor::Status::kFailure) return Status::Failure;
|
||||
modified = status == LoopDescriptor::Status::kSuccessWithChange;
|
||||
|
||||
// TODO(tremmelg): Could the only loop that |loop| could possibly be fused be
|
||||
// picked out so don't have to check every loop
|
||||
@@ -55,13 +59,13 @@ bool LoopFusionPass::ProcessFunction(Function* function) {
|
||||
fusion.Fuse();
|
||||
// Recurse, as the current iterators will have been invalidated.
|
||||
ProcessFunction(function);
|
||||
return true;
|
||||
return Status::SuccessWithChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
|
||||
@@ -39,7 +39,7 @@ class LoopFusionPass : public Pass {
|
||||
private:
|
||||
// Fuse loops in |function| if compatible, legal and the fused loop won't use
|
||||
// too many registers.
|
||||
bool ProcessFunction(Function* function);
|
||||
Status ProcessFunction(Function* function);
|
||||
|
||||
// The maximum number of registers a fused loop is allowed to use.
|
||||
size_t max_registers_per_loop_;
|
||||
|
||||
14
3rdparty/spirv-tools/source/opt/loop_peeling.cpp
vendored
14
3rdparty/spirv-tools/source/opt/loop_peeling.cpp
vendored
@@ -564,11 +564,15 @@ bool LoopPeeling::PeelAfter(uint32_t peel_factor) {
|
||||
// factor < loop_iteration_count_.
|
||||
|
||||
// The original loop's pre-header was the cloned loop merge block.
|
||||
GetClonedLoop()->SetMergeBlock(
|
||||
CreateBlockBefore(GetOriginalLoop()->GetPreHeaderBlock()));
|
||||
if (!GetClonedLoop()->GetMergeBlock()) {
|
||||
BasicBlock* pre_header = GetOriginalLoop()->GetPreHeaderBlock();
|
||||
if (!pre_header) {
|
||||
return false;
|
||||
}
|
||||
BasicBlock* new_merge_block = CreateBlockBefore(pre_header);
|
||||
if (!new_merge_block) {
|
||||
return false;
|
||||
}
|
||||
GetClonedLoop()->SetMergeBlock(new_merge_block);
|
||||
// Use the second loop preheader as if merge block.
|
||||
|
||||
// Prevent the first loop if only the peeled loop needs it.
|
||||
@@ -665,7 +669,9 @@ Pass::Status LoopPeelingPass::ProcessFunction(Function* f) {
|
||||
auto try_peel = [&loop_size, &modified, this](
|
||||
Loop* loop_to_peel) -> std::pair<Pass::Status, Loop*> {
|
||||
if (!loop_to_peel->IsLCSSA()) {
|
||||
LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA();
|
||||
if (!LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA()) {
|
||||
return {Pass::Status::Failure, nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
Pass::Status status;
|
||||
|
||||
152
3rdparty/spirv-tools/source/opt/loop_unroller.cpp
vendored
152
3rdparty/spirv-tools/source/opt/loop_unroller.cpp
vendored
@@ -178,7 +178,7 @@ class LoopUnrollerUtilsImpl {
|
||||
|
||||
// Unroll the |loop| by given |factor| by copying the whole body |factor|
|
||||
// times. The resulting basicblock structure will remain a loop.
|
||||
void PartiallyUnroll(Loop*, size_t factor);
|
||||
bool PartiallyUnroll(Loop*, size_t factor);
|
||||
|
||||
// If partially unrolling the |loop| would leave the loop with too many bodies
|
||||
// for its number of iterations then this method should be used. This method
|
||||
@@ -186,12 +186,12 @@ class LoopUnrollerUtilsImpl {
|
||||
// successor of the original's merge block. The original loop will have its
|
||||
// condition changed to loop over the residual part and the duplicate will be
|
||||
// partially unrolled. The resulting structure will be two loops.
|
||||
void PartiallyUnrollResidualFactor(Loop* loop, size_t factor);
|
||||
bool PartiallyUnrollResidualFactor(Loop* loop, size_t factor);
|
||||
|
||||
// Fully unroll the |loop| by copying the full body by the total number of
|
||||
// loop iterations, folding all conditions, and removing the backedge from the
|
||||
// continue block to the header.
|
||||
void FullyUnroll(Loop* loop);
|
||||
bool FullyUnroll(Loop* loop);
|
||||
|
||||
// Get the ID of the variable in the |phi| paired with |label|.
|
||||
uint32_t GetPhiDefID(const Instruction* phi, uint32_t label) const;
|
||||
@@ -203,7 +203,7 @@ class LoopUnrollerUtilsImpl {
|
||||
// Remove the OpConditionalBranch instruction inside |conditional_block| used
|
||||
// to branch to either exit or continue the loop and replace it with an
|
||||
// unconditional OpBranch to block |new_target|.
|
||||
void FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target);
|
||||
bool FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target);
|
||||
|
||||
// Add all blocks_to_add_ to function_ at the |insert_point|.
|
||||
void AddBlocksToFunction(const BasicBlock* insert_point);
|
||||
@@ -211,7 +211,7 @@ class LoopUnrollerUtilsImpl {
|
||||
// Duplicates the |old_loop|, cloning each body and remapping the ids without
|
||||
// removing instructions or changing relative structure. Result will be stored
|
||||
// in |new_loop|.
|
||||
void DuplicateLoop(Loop* old_loop, Loop* new_loop);
|
||||
bool DuplicateLoop(Loop* old_loop, Loop* new_loop);
|
||||
|
||||
inline size_t GetLoopIterationCount() const {
|
||||
return number_of_loop_iterations_;
|
||||
@@ -241,7 +241,7 @@ class LoopUnrollerUtilsImpl {
|
||||
// to old
|
||||
// ids. |loop| is used to identify special loop blocks (header, continue,
|
||||
// etc).
|
||||
void AssignNewResultIds(BasicBlock* basic_block);
|
||||
bool AssignNewResultIds(BasicBlock* basic_block);
|
||||
|
||||
// Using the map built by AssignNewResultIds, replace the uses in |inst|
|
||||
// by the id that the use maps to.
|
||||
@@ -258,18 +258,18 @@ class LoopUnrollerUtilsImpl {
|
||||
// the old |loop| continue block and the new body will link to the |loop|
|
||||
// header via the new continue block. |eliminate_conditions| is used to decide
|
||||
// whether or not to fold all the condition blocks other than the last one.
|
||||
void CopyBody(Loop* loop, bool eliminate_conditions);
|
||||
bool CopyBody(Loop* loop, bool eliminate_conditions);
|
||||
|
||||
// Copy a given |block_to_copy| in the |loop| and record the mapping of the
|
||||
// old/new ids. |preserve_instructions| determines whether or not the method
|
||||
// will modify (other than result_id) instructions which are copied.
|
||||
void CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy,
|
||||
bool CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy,
|
||||
bool preserve_instructions);
|
||||
|
||||
// The actual implementation of the unroll step. Unrolls |loop| by given
|
||||
// |factor| by copying the body by |factor| times. Also propagates the
|
||||
// induction variable value throughout the copies.
|
||||
void Unroll(Loop* loop, size_t factor);
|
||||
bool Unroll(Loop* loop, size_t factor);
|
||||
|
||||
// Fills the loop_blocks_inorder_ field with the ordered list of basic blocks
|
||||
// as computed by the method ComputeLoopOrderedBlocks.
|
||||
@@ -376,11 +376,12 @@ void LoopUnrollerUtilsImpl::Init(Loop* loop) {
|
||||
// loop it creates two loops and unrolls one and adjusts the condition on the
|
||||
// other. The end result being that the new loop pair iterates over the correct
|
||||
// number of bodies.
|
||||
void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
|
||||
bool LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
|
||||
size_t factor) {
|
||||
// TODO(1841): Handle id overflow.
|
||||
std::unique_ptr<Instruction> new_label{new Instruction(
|
||||
context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})};
|
||||
uint32_t new_label_id = context_->TakeNextId();
|
||||
if (new_label_id == 0) return false;
|
||||
std::unique_ptr<Instruction> new_label{
|
||||
new Instruction(context_, spv::Op::OpLabel, 0, new_label_id, {})};
|
||||
std::unique_ptr<BasicBlock> new_exit_bb{new BasicBlock(std::move(new_label))};
|
||||
new_exit_bb->SetParent(&function_);
|
||||
|
||||
@@ -401,7 +402,9 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
|
||||
// Clear the basic blocks of the new loop.
|
||||
new_loop->ClearBlocks();
|
||||
|
||||
DuplicateLoop(loop, new_loop.get());
|
||||
if (!DuplicateLoop(loop, new_loop.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the blocks to the function.
|
||||
AddBlocksToFunction(loop->GetMergeBlock());
|
||||
@@ -416,7 +419,9 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
|
||||
loop_induction_variable_ = state_.new_phi;
|
||||
// Unroll the new loop by the factor with the usual -1 to account for the
|
||||
// existing block iteration.
|
||||
Unroll(new_loop.get(), factor);
|
||||
if (!Unroll(new_loop.get(), factor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LinkLastPhisToStart(new_loop.get());
|
||||
AddBlocksToLoop(new_loop.get());
|
||||
@@ -460,6 +465,10 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
|
||||
new_constant = builder.GetUintConstant(static_cast<int32_t>(remainder));
|
||||
}
|
||||
|
||||
if (!new_constant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t constant_id = new_constant->result_id();
|
||||
|
||||
// Update the condition check.
|
||||
@@ -477,6 +486,9 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
|
||||
for (size_t index = 0; index < new_inductions.size(); ++index) {
|
||||
Instruction* new_induction = new_inductions[index];
|
||||
Instruction* old_induction = old_inductions[index];
|
||||
if (!new_induction || !old_induction) {
|
||||
return false;
|
||||
}
|
||||
// Get the index of the loop initalizer, the value coming in from the
|
||||
// preheader.
|
||||
uint32_t initalizer_index =
|
||||
@@ -512,6 +524,7 @@ void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop,
|
||||
loop_descriptor.AddLoop(std::move(new_loop), loop->GetParent());
|
||||
|
||||
RemoveDeadInstructions();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mark this loop as DontUnroll as it will already be unrolled and it may not
|
||||
@@ -528,7 +541,7 @@ void LoopUnrollerUtilsImpl::MarkLoopControlAsDontUnroll(Loop* loop) const {
|
||||
// Duplicate the |loop| body |factor| - 1 number of times while keeping the loop
|
||||
// backedge intact. This will leave the loop with |factor| number of bodies
|
||||
// after accounting for the initial body.
|
||||
void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
|
||||
bool LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
|
||||
// If we unroll a loop partially it will not be safe to unroll it further.
|
||||
// This is due to the current method of calculating the number of loop
|
||||
// iterations.
|
||||
@@ -539,8 +552,11 @@ void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) {
|
||||
state_ = LoopUnrollState{loop_induction_variable_, loop->GetLatchBlock(),
|
||||
loop_condition_block_, std::move(inductions)};
|
||||
for (size_t i = 0; i < factor - 1; ++i) {
|
||||
CopyBody(loop, true);
|
||||
if (!CopyBody(loop, true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::RemoveDeadInstructions() {
|
||||
@@ -573,12 +589,16 @@ void LoopUnrollerUtilsImpl::ReplaceInductionUseWithFinalValue(Loop* loop) {
|
||||
|
||||
// Fully unroll the loop by partially unrolling it by the number of loop
|
||||
// iterations minus one for the body already accounted for.
|
||||
void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
|
||||
bool LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
|
||||
// We unroll the loop by number of iterations in the loop.
|
||||
Unroll(loop, number_of_loop_iterations_);
|
||||
if (!Unroll(loop, number_of_loop_iterations_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first condition block is preserved until now so it can be copied.
|
||||
FoldConditionBlock(loop_condition_block_, 1);
|
||||
if (!FoldConditionBlock(loop_condition_block_, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete the OpLoopMerge and remove the backedge to the header.
|
||||
CloseUnrolledLoop(loop);
|
||||
@@ -602,6 +622,7 @@ void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) {
|
||||
context_->InvalidateAnalysesExceptFor(
|
||||
IRContext::Analysis::kAnalysisLoopAnalysis |
|
||||
IRContext::Analysis::kAnalysisDefUse);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
|
||||
@@ -622,10 +643,11 @@ void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) {
|
||||
// and the id mapping in the state. |preserve_instructions| is used to determine
|
||||
// whether or not this function should edit instructions other than the
|
||||
// |result_id|.
|
||||
void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
|
||||
bool LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
|
||||
bool preserve_instructions) {
|
||||
// Clone the block exactly, including the IDs.
|
||||
BasicBlock* basic_block = itr->Clone(context_);
|
||||
if (!basic_block) return false;
|
||||
basic_block->SetParent(itr->GetParent());
|
||||
|
||||
// We do not want to duplicate DebugDeclare.
|
||||
@@ -633,7 +655,9 @@ void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
|
||||
|
||||
// Assign each result a new unique ID and keep a mapping of the old ids to
|
||||
// the new ones.
|
||||
AssignNewResultIds(basic_block);
|
||||
if (!AssignNewResultIds(basic_block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is the continue block we are copying.
|
||||
if (itr == loop->GetContinueBlock()) {
|
||||
@@ -672,13 +696,16 @@ void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr,
|
||||
|
||||
// Keep tracking the old block via a map.
|
||||
state_.new_blocks[itr->id()] = basic_block;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
|
||||
bool LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
|
||||
// Copy each basic block in the loop, give them new ids, and save state
|
||||
// information.
|
||||
for (const BasicBlock* itr : loop_blocks_inorder_) {
|
||||
CopyBasicBlock(loop, itr, false);
|
||||
if (!CopyBasicBlock(loop, itr, false)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the previous latch block to point to the new header.
|
||||
@@ -717,7 +744,9 @@ void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
|
||||
|
||||
if (eliminate_conditions &&
|
||||
state_.new_condition_block != loop_condition_block_) {
|
||||
FoldConditionBlock(state_.new_condition_block, 1);
|
||||
if (!FoldConditionBlock(state_.new_condition_block, 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Only reference to the header block is the backedge in the latch block,
|
||||
@@ -733,6 +762,7 @@ void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) {
|
||||
|
||||
// Swap the state so the new is now the previous.
|
||||
state_.NextIterationState();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi,
|
||||
@@ -746,7 +776,7 @@ uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
|
||||
bool LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
|
||||
uint32_t operand_label) {
|
||||
// Remove the old conditional branch to the merge and continue blocks.
|
||||
Instruction& old_branch = *condition_block->tail();
|
||||
@@ -763,8 +793,13 @@ void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block,
|
||||
IRContext::Analysis::kAnalysisInstrToBlockMapping);
|
||||
Instruction* new_branch = builder.AddBranch(new_target);
|
||||
|
||||
if (!lines.empty()) new_branch->AddDebugLine(&lines.back());
|
||||
if (!lines.empty()) {
|
||||
if (!new_branch->AddDebugLine(&lines.back())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
new_branch->SetDebugScope(scope);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {
|
||||
@@ -811,19 +846,24 @@ void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) {
|
||||
}
|
||||
|
||||
// Uses the first loop to create a copy of the loop with new IDs.
|
||||
void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
|
||||
bool LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
|
||||
std::vector<BasicBlock*> new_block_order;
|
||||
|
||||
// Copy every block in the old loop.
|
||||
for (const BasicBlock* itr : loop_blocks_inorder_) {
|
||||
CopyBasicBlock(old_loop, itr, true);
|
||||
if (!CopyBasicBlock(old_loop, itr, true)) {
|
||||
return false;
|
||||
}
|
||||
new_block_order.push_back(blocks_to_add_.back().get());
|
||||
}
|
||||
|
||||
// Clone the merge block, give it a new id and record it in the state.
|
||||
BasicBlock* new_merge = old_loop->GetMergeBlock()->Clone(context_);
|
||||
if (!new_merge) return false;
|
||||
new_merge->SetParent(old_loop->GetMergeBlock()->GetParent());
|
||||
AssignNewResultIds(new_merge);
|
||||
if (!AssignNewResultIds(new_merge)) {
|
||||
return false;
|
||||
}
|
||||
state_.new_blocks[old_loop->GetMergeBlock()->id()] = new_merge;
|
||||
|
||||
// Remap the operands of every instruction in the loop to point to the new
|
||||
@@ -840,6 +880,7 @@ void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) {
|
||||
new_loop->SetContinueBlock(state_.new_continue_block);
|
||||
new_loop->SetLatchBlock(state_.new_latch_block);
|
||||
new_loop->SetMergeBlock(new_merge);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Whenever the utility copies a block it stores it in a temporary buffer, this
|
||||
@@ -862,13 +903,15 @@ void LoopUnrollerUtilsImpl::AddBlocksToFunction(
|
||||
|
||||
// Assign all result_ids in |basic_block| instructions to new IDs and preserve
|
||||
// the mapping of new ids to old ones.
|
||||
void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
|
||||
bool LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
|
||||
analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr();
|
||||
|
||||
// Label instructions aren't covered by normal traversal of the
|
||||
// instructions.
|
||||
// TODO(1841): Handle id overflow.
|
||||
uint32_t new_label_id = context_->TakeNextId();
|
||||
if (new_label_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assign a new id to the label.
|
||||
state_.new_inst[basic_block->GetLabelInst()->result_id()] = new_label_id;
|
||||
@@ -888,8 +931,11 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
|
||||
}
|
||||
|
||||
// Give the instruction a new id.
|
||||
// TODO(1841): Handle id overflow.
|
||||
inst.SetResultId(context_->TakeNextId());
|
||||
uint32_t new_id = context_->TakeNextId();
|
||||
if (new_id == 0) {
|
||||
return false;
|
||||
}
|
||||
inst.SetResultId(new_id);
|
||||
def_use_mgr->AnalyzeInstDef(&inst);
|
||||
|
||||
// Save the mapping of old_id -> new_id.
|
||||
@@ -901,6 +947,7 @@ void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) {
|
||||
}
|
||||
state_.ids_to_new_inst[inst.result_id()] = &inst;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoopUnrollerUtilsImpl::RemapOperands(Instruction* inst) {
|
||||
@@ -961,12 +1008,13 @@ void LoopUnrollerUtilsImpl::LinkLastPhisToStart(Loop* loop) const {
|
||||
|
||||
// Duplicate the |loop| body |factor| number of times while keeping the loop
|
||||
// backedge intact.
|
||||
void LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) {
|
||||
Unroll(loop, factor);
|
||||
bool LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) {
|
||||
if (!Unroll(loop, factor)) return false;
|
||||
LinkLastPhisToStart(loop);
|
||||
AddBlocksToLoop(loop);
|
||||
AddBlocksToFunction(loop->GetMergeBlock());
|
||||
RemoveDeadInstructions();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1071,7 +1119,9 @@ bool LoopUtils::PartiallyUnroll(size_t factor) {
|
||||
// If the unrolling factor is larger than or the same size as the loop just
|
||||
// fully unroll the loop.
|
||||
if (factor >= unroller.GetLoopIterationCount()) {
|
||||
unroller.FullyUnroll(loop_);
|
||||
if (!unroller.FullyUnroll(loop_)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1080,9 +1130,13 @@ bool LoopUtils::PartiallyUnroll(size_t factor) {
|
||||
// remaining part. We add one when calucating the remainder to take into
|
||||
// account the one iteration already in the loop.
|
||||
if (unroller.GetLoopIterationCount() % factor != 0) {
|
||||
unroller.PartiallyUnrollResidualFactor(loop_, factor);
|
||||
if (!unroller.PartiallyUnrollResidualFactor(loop_, factor)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
unroller.PartiallyUnroll(loop_, factor);
|
||||
if (!unroller.PartiallyUnroll(loop_, factor)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1098,7 +1152,9 @@ bool LoopUtils::FullyUnroll() {
|
||||
loop_->GetHeaderBlock()->GetParent()};
|
||||
|
||||
unroller.Init(loop_);
|
||||
unroller.FullyUnroll(loop_);
|
||||
if (!unroller.FullyUnroll(loop_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1131,15 +1187,25 @@ Pass::Status LoopUnroller::Process() {
|
||||
}
|
||||
|
||||
if (fully_unroll_) {
|
||||
loop_utils.FullyUnroll();
|
||||
if (!loop_utils.FullyUnroll()) {
|
||||
return Status::Failure;
|
||||
}
|
||||
changed = true;
|
||||
} else {
|
||||
loop_utils.PartiallyUnroll(unroll_factor_);
|
||||
if (!loop_utils.PartiallyUnroll(unroll_factor_)) {
|
||||
return Status::Failure;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
LD->PostModificationCleanup();
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
context()->InvalidateAnalysesExceptFor(
|
||||
IRContext::Analysis::kAnalysisLoopAnalysis);
|
||||
}
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
|
||||
@@ -623,7 +623,9 @@ Pass::Status LoopUnswitchPass::ProcessFunction(Function* f) {
|
||||
LoopUnswitch unswitcher(context(), f, &loop, &loop_descriptor);
|
||||
while (unswitcher.CanUnswitchLoop()) {
|
||||
if (!loop.IsLCSSA()) {
|
||||
LoopUtils(context(), &loop).MakeLoopClosedSSA();
|
||||
if (!LoopUtils(context(), &loop).MakeLoopClosedSSA()) {
|
||||
return Status::Failure;
|
||||
}
|
||||
}
|
||||
if (!unswitcher.PerformUnswitch()) {
|
||||
return Status::Failure;
|
||||
|
||||
67
3rdparty/spirv-tools/source/opt/loop_utils.cpp
vendored
67
3rdparty/spirv-tools/source/opt/loop_utils.cpp
vendored
@@ -68,7 +68,7 @@ class LCSSARewriter {
|
||||
// block. This operation does not update the def/use manager, instead it
|
||||
// records what needs to be updated. The actual update is performed by
|
||||
// UpdateManagers.
|
||||
void RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
|
||||
bool RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) {
|
||||
assert(
|
||||
(user->opcode() != spv::Op::OpPhi || bb != GetParent(user)) &&
|
||||
"The root basic block must be the incoming edge if |user| is a phi "
|
||||
@@ -79,9 +79,13 @@ class LCSSARewriter {
|
||||
"phi instruction");
|
||||
|
||||
Instruction* new_def = GetOrBuildIncoming(bb->id());
|
||||
if (!new_def) {
|
||||
return false;
|
||||
}
|
||||
|
||||
user->SetOperand(operand_index, {new_def->result_id()});
|
||||
rewritten_.insert(user);
|
||||
return true;
|
||||
}
|
||||
|
||||
// In-place update of some managers (avoid full invalidation).
|
||||
@@ -120,6 +124,9 @@ class LCSSARewriter {
|
||||
IRContext::kAnalysisInstrToBlockMapping);
|
||||
Instruction* incoming_phi =
|
||||
builder.AddPhi(def_insn_.type_id(), incomings);
|
||||
if (!incoming_phi) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rewritten_.insert(incoming_phi);
|
||||
return incoming_phi;
|
||||
@@ -139,6 +146,9 @@ class LCSSARewriter {
|
||||
IRContext::kAnalysisInstrToBlockMapping);
|
||||
Instruction* incoming_phi =
|
||||
builder.AddPhi(def_insn_.type_id(), incomings);
|
||||
if (!incoming_phi) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rewritten_.insert(incoming_phi);
|
||||
return incoming_phi;
|
||||
@@ -270,7 +280,7 @@ class LCSSARewriter {
|
||||
// Make the set |blocks| closed SSA. The set is closed SSA if all the uses
|
||||
// outside the set are phi instructions in exiting basic block set (hold by
|
||||
// |lcssa_rewriter|).
|
||||
inline void MakeSetClosedSSA(IRContext* context, Function* function,
|
||||
inline bool MakeSetClosedSSA(IRContext* context, Function* function,
|
||||
const std::unordered_set<uint32_t>& blocks,
|
||||
const std::unordered_set<BasicBlock*>& exit_bb,
|
||||
LCSSARewriter* lcssa_rewriter) {
|
||||
@@ -285,18 +295,18 @@ inline void MakeSetClosedSSA(IRContext* context, Function* function,
|
||||
if (!DominatesAnExit(bb, exit_bb, dom_tree)) continue;
|
||||
for (Instruction& inst : *bb) {
|
||||
LCSSARewriter::UseRewriter rewriter(lcssa_rewriter, inst);
|
||||
def_use_manager->ForEachUse(
|
||||
bool success = def_use_manager->WhileEachUse(
|
||||
&inst, [&blocks, &rewriter, &exit_bb, context](
|
||||
Instruction* use, uint32_t operand_index) {
|
||||
BasicBlock* use_parent = context->get_instr_block(use);
|
||||
assert(use_parent);
|
||||
if (blocks.count(use_parent->id())) return;
|
||||
if (blocks.count(use_parent->id())) return true;
|
||||
|
||||
if (use->opcode() == spv::Op::OpPhi) {
|
||||
// If the use is a Phi instruction and the incoming block is
|
||||
// coming from the loop, then that's consistent with LCSSA form.
|
||||
if (exit_bb.count(use_parent)) {
|
||||
return;
|
||||
return true;
|
||||
} else {
|
||||
// That's not an exit block, but the user is a phi instruction.
|
||||
// Consider the incoming branch only.
|
||||
@@ -306,16 +316,20 @@ inline void MakeSetClosedSSA(IRContext* context, Function* function,
|
||||
}
|
||||
// Rewrite the use. Note that this call does not invalidate the
|
||||
// def/use manager. So this operation is safe.
|
||||
rewriter.RewriteUse(use_parent, use, operand_index);
|
||||
return rewriter.RewriteUse(use_parent, use, operand_index);
|
||||
});
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
rewriter.UpdateManagers();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void LoopUtils::CreateLoopDedicatedExits() {
|
||||
bool LoopUtils::CreateLoopDedicatedExits() {
|
||||
Function* function = loop_->GetHeaderBlock()->GetParent();
|
||||
LoopDescriptor& loop_desc = *context_->GetLoopDescriptor(function);
|
||||
CFG& cfg = *context_->cfg();
|
||||
@@ -351,10 +365,13 @@ void LoopUtils::CreateLoopDedicatedExits() {
|
||||
assert(insert_pt != function->end() && "Basic Block not found");
|
||||
|
||||
// Create the dedicate exit basic block.
|
||||
// TODO(1841): Handle id overflow.
|
||||
BasicBlock& exit = *insert_pt.InsertBefore(std::unique_ptr<BasicBlock>(
|
||||
new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
|
||||
context_, spv::Op::OpLabel, 0, context_->TakeNextId(), {})))));
|
||||
uint32_t exit_id = context_->TakeNextId();
|
||||
if (exit_id == 0) {
|
||||
return false;
|
||||
}
|
||||
BasicBlock& exit = *insert_pt.InsertBefore(
|
||||
std::unique_ptr<BasicBlock>(new BasicBlock(std::unique_ptr<Instruction>(
|
||||
new Instruction(context_, spv::Op::OpLabel, 0, exit_id, {})))));
|
||||
exit.SetParent(function);
|
||||
|
||||
// Redirect in loop predecessors to |exit| block.
|
||||
@@ -380,7 +397,7 @@ void LoopUtils::CreateLoopDedicatedExits() {
|
||||
// We also reset the insert point so all instructions are inserted before
|
||||
// the branch.
|
||||
builder.SetInsertPoint(builder.AddBranch(non_dedicate->id()));
|
||||
non_dedicate->ForEachPhiInst(
|
||||
bool succeeded = non_dedicate->WhileEachPhiInst(
|
||||
[&builder, &exit, def_use_mgr, this](Instruction* phi) {
|
||||
// New phi operands for this instruction.
|
||||
std::vector<uint32_t> new_phi_op;
|
||||
@@ -400,6 +417,9 @@ void LoopUtils::CreateLoopDedicatedExits() {
|
||||
|
||||
// Build the new phi instruction dedicated exit block.
|
||||
Instruction* exit_phi = builder.AddPhi(phi->type_id(), exit_phi_op);
|
||||
if (!exit_phi) {
|
||||
return false;
|
||||
}
|
||||
// Build the new incoming branch.
|
||||
new_phi_op.push_back(exit_phi->result_id());
|
||||
new_phi_op.push_back(exit.id());
|
||||
@@ -412,7 +432,9 @@ void LoopUtils::CreateLoopDedicatedExits() {
|
||||
phi->RemoveInOperand(j);
|
||||
// Update the def/use manager for this |phi|.
|
||||
def_use_mgr->AnalyzeInstUse(phi);
|
||||
return true;
|
||||
});
|
||||
if (!succeeded) return false;
|
||||
// Update the CFG.
|
||||
cfg.RegisterBlock(&exit);
|
||||
cfg.RemoveNonExistingEdges(non_dedicate->id());
|
||||
@@ -431,10 +453,13 @@ void LoopUtils::CreateLoopDedicatedExits() {
|
||||
PreservedAnalyses | IRContext::kAnalysisCFG |
|
||||
IRContext::Analysis::kAnalysisLoopAnalysis);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoopUtils::MakeLoopClosedSSA() {
|
||||
CreateLoopDedicatedExits();
|
||||
bool LoopUtils::MakeLoopClosedSSA() {
|
||||
if (!CreateLoopDedicatedExits()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Function* function = loop_->GetHeaderBlock()->GetParent();
|
||||
CFG& cfg = *context_->cfg();
|
||||
@@ -452,8 +477,10 @@ void LoopUtils::MakeLoopClosedSSA() {
|
||||
|
||||
LCSSARewriter lcssa_rewriter(context_, dom_tree, exit_bb,
|
||||
loop_->GetMergeBlock());
|
||||
MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb,
|
||||
&lcssa_rewriter);
|
||||
if (!MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb,
|
||||
&lcssa_rewriter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure all defs post-dominated by the merge block have their last use no
|
||||
// further than the merge block.
|
||||
@@ -466,14 +493,17 @@ void LoopUtils::MakeLoopClosedSSA() {
|
||||
exit_bb.insert(loop_->GetMergeBlock());
|
||||
// LCSSARewriter is reusable here only because it forces the creation of a
|
||||
// phi instruction in the merge block.
|
||||
MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb,
|
||||
&lcssa_rewriter);
|
||||
if (!MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb,
|
||||
&lcssa_rewriter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
context_->InvalidateAnalysesExceptFor(
|
||||
IRContext::Analysis::kAnalysisCFG |
|
||||
IRContext::Analysis::kAnalysisDominatorAnalysis |
|
||||
IRContext::Analysis::kAnalysisLoopAnalysis);
|
||||
return true;
|
||||
}
|
||||
|
||||
Loop* LoopUtils::CloneLoop(LoopCloningResult* cloning_result) const {
|
||||
@@ -569,6 +599,7 @@ Loop* LoopUtils::CloneLoop(
|
||||
// For each basic block in the loop, we clone it and register the mapping
|
||||
// between old and new ids.
|
||||
BasicBlock* new_bb = old_bb->Clone(context_);
|
||||
if (!new_bb) return nullptr;
|
||||
new_bb->SetParent(&function_);
|
||||
uint32_t new_label_id = context_->TakeNextId();
|
||||
if (new_label_id == 0) {
|
||||
|
||||
4
3rdparty/spirv-tools/source/opt/loop_utils.h
vendored
4
3rdparty/spirv-tools/source/opt/loop_utils.h
vendored
@@ -95,14 +95,14 @@ class LoopUtils {
|
||||
//
|
||||
// This makes some loop transformations (such as loop unswitch) simpler
|
||||
// (removes the needs to take care of exiting variables).
|
||||
void MakeLoopClosedSSA();
|
||||
bool MakeLoopClosedSSA();
|
||||
|
||||
// Create dedicate exit basic block. This ensure all exit basic blocks has the
|
||||
// loop as sole predecessors.
|
||||
// By construction, structured control flow already has a dedicated exit
|
||||
// block.
|
||||
// Preserves: CFG, def/use and instruction to block mapping.
|
||||
void CreateLoopDedicatedExits();
|
||||
bool CreateLoopDedicatedExits();
|
||||
|
||||
// Clone |loop_| and remap its instructions. Newly created blocks
|
||||
// will be added to the |cloning_result.cloned_bb_| list, correctly ordered to
|
||||
|
||||
@@ -134,7 +134,9 @@ bool MergeReturnPass::ProcessStructured(
|
||||
state_.pop_back();
|
||||
}
|
||||
|
||||
ProcessStructuredBlock(block);
|
||||
if (!ProcessStructuredBlock(block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate state for next block if warranted
|
||||
GenerateState(block);
|
||||
@@ -169,7 +171,9 @@ bool MergeReturnPass::ProcessStructured(
|
||||
// We have not kept the dominator tree up-to-date.
|
||||
// Invalidate it at this point to make sure it will be rebuilt.
|
||||
context()->RemoveDominatorAnalysis(function);
|
||||
AddNewPhiNodes();
|
||||
if (!AddNewPhiNodes()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -196,7 +200,9 @@ bool MergeReturnPass::CreateReturnBlock() {
|
||||
}
|
||||
|
||||
bool MergeReturnPass::CreateReturn(BasicBlock* block) {
|
||||
AddReturnValue();
|
||||
if (!AddReturnValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (return_value_) {
|
||||
// Load and return the final return value
|
||||
@@ -229,12 +235,18 @@ bool MergeReturnPass::CreateReturn(BasicBlock* block) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
|
||||
bool MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
|
||||
if (block->tail() == block->end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
spv::Op tail_opcode = block->tail()->opcode();
|
||||
if (tail_opcode == spv::Op::OpReturn ||
|
||||
tail_opcode == spv::Op::OpReturnValue) {
|
||||
if (!return_flag_) {
|
||||
AddReturnFlag();
|
||||
if (!AddReturnFlag()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,15 +255,20 @@ void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) {
|
||||
tail_opcode == spv::Op::OpUnreachable) {
|
||||
assert(CurrentState().InBreakable() &&
|
||||
"Should be in the placeholder construct.");
|
||||
BranchToBlock(block, CurrentState().BreakMergeId());
|
||||
if (!BranchToBlock(block, CurrentState().BreakMergeId())) {
|
||||
return false;
|
||||
}
|
||||
return_blocks_.insert(block->id());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
|
||||
bool MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
|
||||
if (block->tail()->opcode() == spv::Op::OpReturn ||
|
||||
block->tail()->opcode() == spv::Op::OpReturnValue) {
|
||||
RecordReturned(block);
|
||||
if (!RecordReturned(block)) {
|
||||
return false;
|
||||
}
|
||||
RecordReturnValue(block);
|
||||
}
|
||||
|
||||
@@ -259,7 +276,9 @@ void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
|
||||
if (target_block->GetLoopMergeInst()) {
|
||||
cfg()->SplitLoopHeader(target_block);
|
||||
}
|
||||
UpdatePhiNodes(block, target_block);
|
||||
if (!UpdatePhiNodes(block, target_block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Instruction* return_inst = block->terminator();
|
||||
return_inst->SetOpcode(spv::Op::OpBranch);
|
||||
@@ -267,19 +286,26 @@ void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) {
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst);
|
||||
new_edges_[target_block].insert(block->id());
|
||||
cfg()->AddEdge(block->id(), target);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
|
||||
bool MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source,
|
||||
BasicBlock* target) {
|
||||
target->ForEachPhiInst([this, new_source](Instruction* inst) {
|
||||
bool succeeded = true;
|
||||
target->ForEachPhiInst([this, new_source, &succeeded](Instruction* inst) {
|
||||
uint32_t undefId = Type2Undef(inst->type_id());
|
||||
if (undefId == 0) {
|
||||
succeeded = false;
|
||||
return;
|
||||
}
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}});
|
||||
inst->AddOperand({SPV_OPERAND_TYPE_ID, {new_source->id()}});
|
||||
context()->UpdateDefUse(inst);
|
||||
});
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
bool MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
Instruction& inst) {
|
||||
DominatorAnalysis* dom_tree =
|
||||
context()->GetDominatorAnalysis(merge_block->GetParent());
|
||||
@@ -313,7 +339,7 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
});
|
||||
|
||||
if (users_to_update.empty()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// There is at least one values that needs to be replaced.
|
||||
@@ -357,6 +383,9 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
if (regenerateInstruction) {
|
||||
std::unique_ptr<Instruction> regen_inst(inst.Clone(context()));
|
||||
uint32_t new_id = TakeNextId();
|
||||
if (new_id == 0) {
|
||||
return false;
|
||||
}
|
||||
regen_inst->SetResultId(new_id);
|
||||
Instruction* insert_pos = &*merge_block->begin();
|
||||
while (insert_pos->opcode() == spv::Op::OpPhi) {
|
||||
@@ -366,18 +395,31 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_phi);
|
||||
context()->set_instr_block(new_phi, merge_block);
|
||||
|
||||
new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) {
|
||||
bool succeeded = true;
|
||||
new_phi->ForEachInId([dom_tree, merge_block, this,
|
||||
&succeeded](uint32_t* use_id) {
|
||||
if (!succeeded) {
|
||||
return;
|
||||
}
|
||||
Instruction* use = get_def_use_mgr()->GetDef(*use_id);
|
||||
BasicBlock* use_bb = context()->get_instr_block(use);
|
||||
if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) {
|
||||
CreatePhiNodesForInst(merge_block, *use);
|
||||
if (!CreatePhiNodesForInst(merge_block, *use)) {
|
||||
succeeded = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!succeeded) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
InstructionBuilder builder(
|
||||
context(), &*merge_block->begin(),
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
new_phi = builder.AddPhi(inst.type_id(), phi_operands);
|
||||
if (new_phi == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint32_t result_of_phi = new_phi->result_id();
|
||||
|
||||
@@ -391,6 +433,7 @@ void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block,
|
||||
context()->AnalyzeUses(user);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MergeReturnPass::PredicateBlocks(
|
||||
@@ -483,6 +526,9 @@ bool MergeReturnPass::BreakFromConstruct(
|
||||
cfg()->RemoveSuccessorEdges(block);
|
||||
|
||||
auto old_body_id = TakeNextId();
|
||||
if (old_body_id == 0) {
|
||||
return false;
|
||||
}
|
||||
BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter);
|
||||
predicated->insert(old_body);
|
||||
|
||||
@@ -519,8 +565,11 @@ bool MergeReturnPass::BreakFromConstruct(
|
||||
analysis::Bool bool_type;
|
||||
uint32_t bool_id = context()->get_type_mgr()->GetId(&bool_type);
|
||||
assert(bool_id != 0);
|
||||
uint32_t load_id =
|
||||
builder.AddLoad(bool_id, return_flag_->result_id())->result_id();
|
||||
Instruction* load_inst = builder.AddLoad(bool_id, return_flag_->result_id());
|
||||
if (load_inst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
uint32_t load_id = load_inst->result_id();
|
||||
|
||||
// 2. Branch to |merge_block| (true) or |old_body| (false)
|
||||
builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(),
|
||||
@@ -533,7 +582,9 @@ bool MergeReturnPass::BreakFromConstruct(
|
||||
}
|
||||
|
||||
// 3. Update OpPhi instructions in |merge_block|.
|
||||
UpdatePhiNodes(block, merge_block);
|
||||
if (!UpdatePhiNodes(block, merge_block)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Update the CFG. We do this after updating the OpPhi instructions
|
||||
// because |UpdatePhiNodes| assumes the edge from |block| has not been added
|
||||
@@ -546,10 +597,10 @@ bool MergeReturnPass::BreakFromConstruct(
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::RecordReturned(BasicBlock* block) {
|
||||
bool MergeReturnPass::RecordReturned(BasicBlock* block) {
|
||||
if (block->tail()->opcode() != spv::Op::OpReturn &&
|
||||
block->tail()->opcode() != spv::Op::OpReturnValue)
|
||||
return;
|
||||
return true;
|
||||
|
||||
assert(return_flag_ && "Did not generate the return flag variable.");
|
||||
|
||||
@@ -562,6 +613,9 @@ void MergeReturnPass::RecordReturned(BasicBlock* block) {
|
||||
const analysis::Constant* true_const =
|
||||
const_mgr->GetConstant(bool_type, {true});
|
||||
constant_true_ = const_mgr->GetDefiningInstruction(true_const);
|
||||
if (!constant_true_) {
|
||||
return false;
|
||||
}
|
||||
context()->UpdateDefUse(constant_true_);
|
||||
}
|
||||
|
||||
@@ -575,6 +629,7 @@ void MergeReturnPass::RecordReturned(BasicBlock* block) {
|
||||
&*block->tail().InsertBefore(std::move(return_store));
|
||||
context()->set_instr_block(store_inst, block);
|
||||
context()->AnalyzeDefUse(store_inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::RecordReturnValue(BasicBlock* block) {
|
||||
@@ -598,18 +653,21 @@ void MergeReturnPass::RecordReturnValue(BasicBlock* block) {
|
||||
context()->AnalyzeDefUse(store_inst);
|
||||
}
|
||||
|
||||
void MergeReturnPass::AddReturnValue() {
|
||||
if (return_value_) return;
|
||||
bool MergeReturnPass::AddReturnValue() {
|
||||
if (return_value_) return true;
|
||||
|
||||
uint32_t return_type_id = function_->type_id();
|
||||
if (get_def_use_mgr()->GetDef(return_type_id)->opcode() ==
|
||||
spv::Op::OpTypeVoid)
|
||||
return;
|
||||
return true;
|
||||
|
||||
uint32_t return_ptr_type = context()->get_type_mgr()->FindPointerToType(
|
||||
return_type_id, spv::StorageClass::Function);
|
||||
|
||||
uint32_t var_id = TakeNextId();
|
||||
if (var_id == 0) {
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<Instruction> returnValue(
|
||||
new Instruction(context(), spv::Op::OpVariable, return_ptr_type, var_id,
|
||||
std::initializer_list<Operand>{
|
||||
@@ -625,27 +683,44 @@ void MergeReturnPass::AddReturnValue() {
|
||||
|
||||
context()->get_decoration_mgr()->CloneDecorations(
|
||||
function_->result_id(), var_id, {spv::Decoration::RelaxedPrecision});
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::AddReturnFlag() {
|
||||
if (return_flag_) return;
|
||||
bool MergeReturnPass::AddReturnFlag() {
|
||||
if (return_flag_) return true;
|
||||
|
||||
analysis::TypeManager* type_mgr = context()->get_type_mgr();
|
||||
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
|
||||
|
||||
analysis::Bool temp;
|
||||
uint32_t bool_id = type_mgr->GetTypeInstruction(&temp);
|
||||
if (bool_id == 0) {
|
||||
return false;
|
||||
}
|
||||
analysis::Bool* bool_type = type_mgr->GetType(bool_id)->AsBool();
|
||||
|
||||
const analysis::Constant* false_const =
|
||||
const_mgr->GetConstant(bool_type, {false});
|
||||
uint32_t const_false_id =
|
||||
const_mgr->GetDefiningInstruction(false_const)->result_id();
|
||||
Instruction* false_inst = const_mgr->GetDefiningInstruction(false_const);
|
||||
if (false_inst == nullptr) {
|
||||
return false;
|
||||
}
|
||||
uint32_t const_false_id = false_inst->result_id();
|
||||
|
||||
uint32_t bool_ptr_id =
|
||||
type_mgr->FindPointerToType(bool_id, spv::StorageClass::Function);
|
||||
|
||||
if (bool_ptr_id == 0) {
|
||||
return false;
|
||||
;
|
||||
}
|
||||
|
||||
uint32_t var_id = TakeNextId();
|
||||
|
||||
if (var_id == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Instruction> returnFlag(new Instruction(
|
||||
context(), spv::Op::OpVariable, bool_ptr_id, var_id,
|
||||
std::initializer_list<Operand>{{SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
@@ -659,6 +734,7 @@ void MergeReturnPass::AddReturnFlag() {
|
||||
return_flag_ = &*entry_block->begin();
|
||||
context()->AnalyzeDefUse(return_flag_);
|
||||
context()->set_instr_block(return_flag_, entry_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<BasicBlock*> MergeReturnPass::CollectReturnBlocks(
|
||||
@@ -737,16 +813,19 @@ bool MergeReturnPass::MergeReturnBlocks(
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::AddNewPhiNodes() {
|
||||
bool MergeReturnPass::AddNewPhiNodes() {
|
||||
std::list<BasicBlock*> order;
|
||||
cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order);
|
||||
|
||||
for (BasicBlock* bb : order) {
|
||||
AddNewPhiNodes(bb);
|
||||
if (!AddNewPhiNodes(bb)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
|
||||
bool MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
|
||||
// New phi nodes are needed for any id whose definition used to dominate |bb|,
|
||||
// but no longer dominates |bb|. These are found by walking the dominator
|
||||
// tree starting at the original immediate dominator of |bb| and ending at its
|
||||
@@ -764,16 +843,19 @@ void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) {
|
||||
|
||||
BasicBlock* dominator = dom_tree->ImmediateDominator(bb);
|
||||
if (dominator == nullptr) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
BasicBlock* current_bb = context()->get_instr_block(original_dominator_[bb]);
|
||||
while (current_bb != nullptr && current_bb != dominator) {
|
||||
for (Instruction& inst : *current_bb) {
|
||||
CreatePhiNodesForInst(bb, inst);
|
||||
if (!CreatePhiNodesForInst(bb, inst)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
current_bb = dom_tree->ImmediateDominator(current_bb);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeReturnPass::RecordImmediateDominators(Function* function) {
|
||||
@@ -857,8 +939,12 @@ bool MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) {
|
||||
++split_pos;
|
||||
}
|
||||
|
||||
uint32_t new_block_id = TakeNextId();
|
||||
if (new_block_id == 0) {
|
||||
return false;
|
||||
}
|
||||
BasicBlock* old_block =
|
||||
start_block->SplitBasicBlock(context(), TakeNextId(), split_pos);
|
||||
start_block->SplitBasicBlock(context(), new_block_id, split_pos);
|
||||
|
||||
// Find DebugFunctionDefinition inst in the old block, and if we can find it,
|
||||
// move it to the entry block. Since DebugFunctionDefinition is not necessary
|
||||
|
||||
@@ -173,21 +173,22 @@ class MergeReturnPass : public MemPass {
|
||||
//
|
||||
// Note this will break the semantics. To fix this, PredicateBlock will have
|
||||
// to be called on the merge block the branch targets.
|
||||
void ProcessStructuredBlock(BasicBlock* block);
|
||||
bool ProcessStructuredBlock(BasicBlock* block);
|
||||
|
||||
// Creates a variable used to store whether or not the control flow has
|
||||
// traversed a block that used to have a return. A pointer to the instruction
|
||||
// declaring the variable is stored in |return_flag_|.
|
||||
void AddReturnFlag();
|
||||
// declaring the variable is stored in |return_flag_|. Returns true if it
|
||||
// succeeds.
|
||||
bool AddReturnFlag();
|
||||
|
||||
// Creates the variable used to store the return value when passing through
|
||||
// a block that use to contain an OpReturnValue.
|
||||
void AddReturnValue();
|
||||
// a block that use to contain an OpReturnValue. Returns true if it succeeds.
|
||||
bool AddReturnValue();
|
||||
|
||||
// Adds a store that stores true to |return_flag_| immediately before the
|
||||
// terminator of |block|. It is assumed that |AddReturnFlag| has already been
|
||||
// called.
|
||||
void RecordReturned(BasicBlock* block);
|
||||
// Records that |block| used to be a return. This is done by adding an
|
||||
// instruction to store true to the |return_flag_|. Returns true if it
|
||||
// succeeds.
|
||||
bool RecordReturned(BasicBlock* block);
|
||||
|
||||
// Adds an instruction that stores the value being returned in the
|
||||
// OpReturnValue in |block|. The value is stored to |return_value_|, and the
|
||||
@@ -198,10 +199,10 @@ class MergeReturnPass : public MemPass {
|
||||
// have already been called to create the variable to store to.
|
||||
void RecordReturnValue(BasicBlock* block);
|
||||
|
||||
// Adds an unconditional branch in |block| that branches to |target|. It also
|
||||
// adds stores to |return_flag_| and |return_value_| as needed.
|
||||
// |AddReturnFlag| and |AddReturnValue| must have already been called.
|
||||
void BranchToBlock(BasicBlock* block, uint32_t target);
|
||||
// Replaces the terminator of |block| with a branch to |target|. If the
|
||||
// terminator was a return, it will first call RecordReturned and
|
||||
// RecordReturnValue. Returns true if it succeeds.
|
||||
bool BranchToBlock(BasicBlock* block, uint32_t target);
|
||||
|
||||
// For every basic block that is reachable from |return_block|, extra code is
|
||||
// added to jump around any code that should not be executed because the
|
||||
@@ -239,32 +240,28 @@ class MergeReturnPass : public MemPass {
|
||||
// return block at the end of the pass.
|
||||
bool CreateReturnBlock();
|
||||
|
||||
// Creates a Phi node in |merge_block| for the result of |inst|.
|
||||
// Any uses of the result of |inst| that are no longer
|
||||
// dominated by |inst|, are replaced with the result of the new |OpPhi|
|
||||
// instruction.
|
||||
void CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst);
|
||||
// For each use of |inst| that is no longer dominated by |inst|, a phi node
|
||||
// is created in |merge_block|. The original use is replaced by the result
|
||||
// of the phi node. Returns true if it succeeds.
|
||||
bool CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst);
|
||||
|
||||
// Add new phi nodes for any id that no longer dominate all of it uses. A phi
|
||||
// node is added to a block |bb| for an id if the id is defined between the
|
||||
// original immediate dominator of |bb| and its new immediate dominator. It
|
||||
// is assumed that at this point there are no unreachable blocks in the
|
||||
// control flow graph.
|
||||
void AddNewPhiNodes();
|
||||
// Adds new phi nodes as needed to the function. This is necessary because
|
||||
// adding the predication code can change the dominator tree. Returns false
|
||||
// if there is a failure.
|
||||
bool AddNewPhiNodes();
|
||||
|
||||
// Creates any new phi nodes that are needed in |bb|. |AddNewPhiNodes| must
|
||||
// have already been called on the original dominators of |bb|.
|
||||
void AddNewPhiNodes(BasicBlock* bb);
|
||||
// Adds new phi nodes to |bb| as needed. This is necessary because adding
|
||||
// the predication code can change the dominator tree. Returns false if
|
||||
// there is a failure.
|
||||
bool AddNewPhiNodes(BasicBlock* bb);
|
||||
|
||||
// Records the terminator of immediate dominator for every basic block in
|
||||
// |function|.
|
||||
void RecordImmediateDominators(Function* function);
|
||||
|
||||
// Modifies existing OpPhi instruction in |target| block to account for the
|
||||
// new edge from |new_source|. The value for that edge will be an Undef.
|
||||
//
|
||||
// The CFG must not include the edge from |new_source| to |target| yet.
|
||||
void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
|
||||
// For each OpPhi instruction in |target|, this function adds an operand for
|
||||
// |new_source|. The value will be OpUndef. Returns true if it succeeds.
|
||||
bool UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target);
|
||||
|
||||
StructuredControlState& CurrentState() { return state_.back(); }
|
||||
|
||||
@@ -334,4 +331,4 @@ class MergeReturnPass : public MemPass {
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_MERGE_RETURN_PASS_H_
|
||||
#endif // SOURCE_OPT_MERGE_RETURN_PASS_H_
|
||||
|
||||
11
3rdparty/spirv-tools/source/opt/module.cpp
vendored
11
3rdparty/spirv-tools/source/opt/module.cpp
vendored
@@ -93,6 +93,7 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
if (sampled_image_address_mode_)
|
||||
sampled_image_address_mode_->ForEachInst(f, run_on_debug_line_insts);
|
||||
DELEGATE(entry_points_);
|
||||
DELEGATE(graph_entry_points_);
|
||||
DELEGATE(execution_modes_);
|
||||
DELEGATE(debugs1_);
|
||||
DELEGATE(debugs2_);
|
||||
@@ -104,6 +105,10 @@ void Module::ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
i->ForEachInst(f, run_on_debug_line_insts,
|
||||
/* run_on_non_semantic_insts = */ true);
|
||||
}
|
||||
for (auto& g : graphs_) {
|
||||
g->ForEachInst(f, run_on_debug_line_insts,
|
||||
/* run_on_non_semantic_insts = */ true);
|
||||
}
|
||||
#undef DELEGATE
|
||||
}
|
||||
|
||||
@@ -132,6 +137,12 @@ void Module::ForEachInst(const std::function<void(const Instruction*)>& f,
|
||||
f, run_on_debug_line_insts,
|
||||
/* run_on_non_semantic_insts = */ true);
|
||||
}
|
||||
for (auto& i : graph_entry_points_) DELEGATE(i);
|
||||
for (auto& i : graphs_) {
|
||||
static_cast<const Graph*>(i.get())->ForEachInst(
|
||||
f, run_on_debug_line_insts,
|
||||
/* run_on_non_semantic_insts = */ true);
|
||||
}
|
||||
if (run_on_debug_line_insts) {
|
||||
for (auto& i : trailing_dbg_line_info_) DELEGATE(i);
|
||||
}
|
||||
|
||||
39
3rdparty/spirv-tools/source/opt/module.h
vendored
39
3rdparty/spirv-tools/source/opt/module.h
vendored
@@ -23,6 +23,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "source/opt/function.h"
|
||||
#include "source/opt/graph.h"
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/opt/iterator.h"
|
||||
|
||||
@@ -48,6 +49,8 @@ class Module {
|
||||
using const_iterator = UptrVectorIterator<Function, true>;
|
||||
using inst_iterator = InstructionList::iterator;
|
||||
using const_inst_iterator = InstructionList::const_iterator;
|
||||
using graph_iterator = UptrVectorIterator<Graph>;
|
||||
using const_graph_iterator = UptrVectorIterator<Graph, true>;
|
||||
|
||||
// Creates an empty module with zero'd header.
|
||||
Module() : header_({}), contains_debug_info_(false) {}
|
||||
@@ -90,6 +93,9 @@ class Module {
|
||||
// Appends an entry point instruction to this module.
|
||||
inline void AddEntryPoint(std::unique_ptr<Instruction> e);
|
||||
|
||||
// Appends a graph entry point instruction to this module.
|
||||
inline void AddGraphEntryPoint(std::unique_ptr<Instruction> e);
|
||||
|
||||
// Appends an execution mode instruction to this module.
|
||||
inline void AddExecutionMode(std::unique_ptr<Instruction> e);
|
||||
|
||||
@@ -126,6 +132,9 @@ class Module {
|
||||
// Appends a function to this module.
|
||||
inline void AddFunction(std::unique_ptr<Function> f);
|
||||
|
||||
// Appends a graph to this module.
|
||||
inline void AddGraph(std::unique_ptr<Graph> g);
|
||||
|
||||
// Sets |contains_debug_info_| as true.
|
||||
inline void SetContainsDebugInfo();
|
||||
inline bool ContainsDebugInfo() { return contains_debug_info_; }
|
||||
@@ -220,6 +229,10 @@ class Module {
|
||||
inline IteratorRange<inst_iterator> entry_points();
|
||||
inline IteratorRange<const_inst_iterator> entry_points() const;
|
||||
|
||||
// Iterators for graph entry point instructions contained in this module
|
||||
inline IteratorRange<inst_iterator> graph_entry_points();
|
||||
inline IteratorRange<const_inst_iterator> graph_entry_points() const;
|
||||
|
||||
// Iterators for execution_modes instructions contained in this module.
|
||||
inline inst_iterator execution_mode_begin();
|
||||
inline inst_iterator execution_mode_end();
|
||||
@@ -252,6 +265,9 @@ class Module {
|
||||
inline const_iterator cbegin() const;
|
||||
inline const_iterator cend() const;
|
||||
|
||||
// Iterators for graphs contained in this module.
|
||||
inline const std::vector<std::unique_ptr<Graph>>& graphs() const;
|
||||
|
||||
// Invokes function |f| on all instructions in this module, and optionally on
|
||||
// the debug line instructions that precede them.
|
||||
void ForEachInst(const std::function<void(Instruction*)>& f,
|
||||
@@ -306,6 +322,7 @@ class Module {
|
||||
// A module can only have one optional sampled image addressing mode
|
||||
std::unique_ptr<Instruction> sampled_image_address_mode_;
|
||||
InstructionList entry_points_;
|
||||
InstructionList graph_entry_points_;
|
||||
InstructionList execution_modes_;
|
||||
InstructionList debugs1_;
|
||||
InstructionList debugs2_;
|
||||
@@ -315,6 +332,7 @@ class Module {
|
||||
// Type declarations, constants, and global variable declarations.
|
||||
InstructionList types_values_;
|
||||
std::vector<std::unique_ptr<Function>> functions_;
|
||||
std::vector<std::unique_ptr<Graph>> graphs_;
|
||||
|
||||
// If the module ends with Op*Line instruction, they will not be attached to
|
||||
// any instruction. We record them here, so they will not be lost.
|
||||
@@ -351,6 +369,10 @@ inline void Module::AddEntryPoint(std::unique_ptr<Instruction> e) {
|
||||
entry_points_.push_back(std::move(e));
|
||||
}
|
||||
|
||||
inline void Module::AddGraphEntryPoint(std::unique_ptr<Instruction> e) {
|
||||
graph_entry_points_.push_back(std::move(e));
|
||||
}
|
||||
|
||||
inline void Module::AddExecutionMode(std::unique_ptr<Instruction> e) {
|
||||
execution_modes_.push_back(std::move(e));
|
||||
}
|
||||
@@ -392,6 +414,10 @@ inline void Module::AddFunction(std::unique_ptr<Function> f) {
|
||||
functions_.emplace_back(std::move(f));
|
||||
}
|
||||
|
||||
inline void Module::AddGraph(std::unique_ptr<Graph> g) {
|
||||
graphs_.emplace_back(std::move(g));
|
||||
}
|
||||
|
||||
inline void Module::SetContainsDebugInfo() { contains_debug_info_ = true; }
|
||||
|
||||
inline Module::inst_iterator Module::capability_begin() {
|
||||
@@ -482,6 +508,15 @@ inline IteratorRange<Module::const_inst_iterator> Module::entry_points() const {
|
||||
return make_range(entry_points_.begin(), entry_points_.end());
|
||||
}
|
||||
|
||||
inline IteratorRange<Module::inst_iterator> Module::graph_entry_points() {
|
||||
return make_range(graph_entry_points_.begin(), graph_entry_points_.end());
|
||||
}
|
||||
|
||||
inline IteratorRange<Module::const_inst_iterator> Module::graph_entry_points()
|
||||
const {
|
||||
return make_range(graph_entry_points_.begin(), graph_entry_points_.end());
|
||||
}
|
||||
|
||||
inline Module::inst_iterator Module::execution_mode_begin() {
|
||||
return execution_modes_.begin();
|
||||
}
|
||||
@@ -544,6 +579,10 @@ inline IteratorRange<Module::const_inst_iterator> Module::types_values() const {
|
||||
return make_range(types_values_.begin(), types_values_.end());
|
||||
}
|
||||
|
||||
inline const std::vector<std::unique_ptr<Graph>>& Module::graphs() const {
|
||||
return graphs_;
|
||||
}
|
||||
|
||||
inline Module::const_iterator Module::cbegin() const {
|
||||
return const_iterator(&functions_, functions_.cbegin());
|
||||
}
|
||||
|
||||
2
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
2
3rdparty/spirv-tools/source/opt/pass.cpp
vendored
@@ -114,6 +114,7 @@ uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
|
||||
assert(length_const->AsIntConstant());
|
||||
uint32_t array_length = length_const->AsIntConstant()->GetU32();
|
||||
for (uint32_t i = 0; i < array_length; i++) {
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
original_element_type_id, object_to_copy->result_id(), {i});
|
||||
uint32_t new_id =
|
||||
@@ -132,6 +133,7 @@ uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
|
||||
for (uint32_t i = 0; i < original_type->NumInOperands(); i++) {
|
||||
uint32_t orig_member_type_id = original_type->GetSingleWordInOperand(i);
|
||||
uint32_t new_member_type_id = new_type->GetSingleWordInOperand(i);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* extract = ir_builder.AddCompositeExtract(
|
||||
orig_member_type_id, object_to_copy->result_id(), {i});
|
||||
uint32_t new_id =
|
||||
|
||||
55
3rdparty/spirv-tools/source/opt/propagator.cpp
vendored
55
3rdparty/spirv-tools/source/opt/propagator.cpp
vendored
@@ -84,16 +84,19 @@ bool SSAPropagator::SetStatus(Instruction* inst, PropStatus status) {
|
||||
return status_changed;
|
||||
}
|
||||
|
||||
bool SSAPropagator::Simulate(Instruction* instr) {
|
||||
Pass::Status SSAPropagator::Simulate(Instruction* instr) {
|
||||
bool changed = false;
|
||||
|
||||
// Don't bother visiting instructions that should not be simulated again.
|
||||
if (!ShouldSimulateAgain(instr)) {
|
||||
return changed;
|
||||
return Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
BasicBlock* dest_bb = nullptr;
|
||||
PropStatus status = visit_fn_(instr, &dest_bb);
|
||||
if (status == SSAPropagator::kFailed) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
bool status_changed = SetStatus(instr, status);
|
||||
|
||||
if (status == kVarying) {
|
||||
@@ -112,7 +115,7 @@ bool SSAPropagator::Simulate(Instruction* instr) {
|
||||
AddControlEdge(e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return Pass::Status::SuccessWithoutChange;
|
||||
} else if (status == kInteresting) {
|
||||
// Add the SSA edges coming out of this instruction if the propagation
|
||||
// status has changed.
|
||||
@@ -169,12 +172,13 @@ bool SSAPropagator::Simulate(Instruction* instr) {
|
||||
DontSimulateAgain(instr);
|
||||
}
|
||||
|
||||
return changed;
|
||||
return changed ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
bool SSAPropagator::Simulate(BasicBlock* block) {
|
||||
Pass::Status SSAPropagator::Simulate(BasicBlock* block) {
|
||||
if (block == ctx_->cfg()->pseudo_exit_block()) {
|
||||
return false;
|
||||
return Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
// Always simulate Phi instructions, even if we have simulated this block
|
||||
@@ -182,17 +186,29 @@ bool SSAPropagator::Simulate(BasicBlock* block) {
|
||||
// incoming edges. When those edges are marked executable, the corresponding
|
||||
// operand can be simulated.
|
||||
bool changed = false;
|
||||
block->ForEachPhiInst(
|
||||
[&changed, this](Instruction* instr) { changed |= Simulate(instr); });
|
||||
bool succeeded =
|
||||
block->WhileEachPhiInst([&changed, this](Instruction* instr) {
|
||||
auto Status = Simulate(instr);
|
||||
if (Status == Pass::Status::Failure) return false;
|
||||
changed |= Status == Pass::Status::SuccessWithChange;
|
||||
return true;
|
||||
});
|
||||
if (!succeeded) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
|
||||
// If this is the first time this block is being simulated, simulate every
|
||||
// statement in it.
|
||||
if (!BlockHasBeenSimulated(block)) {
|
||||
block->ForEachInst([this, &changed](Instruction* instr) {
|
||||
if (instr->opcode() != spv::Op::OpPhi) {
|
||||
changed |= Simulate(instr);
|
||||
}
|
||||
succeeded = block->WhileEachInst([&changed, this](Instruction* instr) {
|
||||
auto Status = Simulate(instr);
|
||||
if (Status == Pass::Status::Failure) return false;
|
||||
changed |= Status == Pass::Status::SuccessWithChange;
|
||||
return true;
|
||||
});
|
||||
if (!succeeded) {
|
||||
return Pass::Status::Failure;
|
||||
}
|
||||
|
||||
MarkBlockSimulated(block);
|
||||
|
||||
@@ -203,7 +219,8 @@ bool SSAPropagator::Simulate(BasicBlock* block) {
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
return changed ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
void SSAPropagator::Initialize(Function* fn) {
|
||||
@@ -245,7 +262,11 @@ bool SSAPropagator::Run(Function* fn) {
|
||||
// follow after all the blocks have been simulated.
|
||||
if (!blocks_.empty()) {
|
||||
auto block = blocks_.front();
|
||||
changed |= Simulate(block);
|
||||
Pass::Status status = Simulate(block);
|
||||
if (status == Pass::Status::Failure) {
|
||||
return false;
|
||||
}
|
||||
changed |= status == Pass::Status::SuccessWithChange;
|
||||
blocks_.pop();
|
||||
continue;
|
||||
}
|
||||
@@ -253,7 +274,11 @@ bool SSAPropagator::Run(Function* fn) {
|
||||
// Simulate edges from the SSA queue.
|
||||
if (!ssa_edge_uses_.empty()) {
|
||||
Instruction* instr = ssa_edge_uses_.front();
|
||||
changed |= Simulate(instr);
|
||||
Pass::Status status = Simulate(instr);
|
||||
if (status == Pass::Status::Failure) {
|
||||
return changed;
|
||||
}
|
||||
changed |= status == Pass::Status::SuccessWithChange;
|
||||
ssa_edge_uses_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
11
3rdparty/spirv-tools/source/opt/propagator.h
vendored
11
3rdparty/spirv-tools/source/opt/propagator.h
vendored
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@@ -182,7 +183,7 @@ class SSAPropagator {
|
||||
public:
|
||||
// Lattice values used for propagation. See class documentation for
|
||||
// a description.
|
||||
enum PropStatus { kNotInteresting, kInteresting, kVarying };
|
||||
enum PropStatus { kNotInteresting, kInteresting, kVarying, kFailed };
|
||||
|
||||
using VisitFunction = std::function<PropStatus(Instruction*, BasicBlock**)>;
|
||||
|
||||
@@ -190,7 +191,9 @@ class SSAPropagator {
|
||||
: ctx_(context), visit_fn_(visit_fn) {}
|
||||
|
||||
// Runs the propagator on function |fn|. Returns true if changes were made to
|
||||
// the function. Otherwise, it returns false.
|
||||
// the function. Otherwise, it returns false. The user should check
|
||||
// IRContext::id_overflow() to see if there was an error caused by reaching
|
||||
// the max id.
|
||||
bool Run(Function* fn);
|
||||
|
||||
// Returns true if the |i|th argument for |phi| comes through a CFG edge that
|
||||
@@ -218,13 +221,13 @@ class SSAPropagator {
|
||||
|
||||
// Simulate the execution |block| by calling |visit_fn_| on every instruction
|
||||
// in it.
|
||||
bool Simulate(BasicBlock* block);
|
||||
Pass::Status Simulate(BasicBlock* block);
|
||||
|
||||
// Simulate the execution of |instr| by replacing all the known values in
|
||||
// every operand and determining whether the result is interesting for
|
||||
// propagation. This invokes the callback function |visit_fn_| to determine
|
||||
// the value computed by |instr|.
|
||||
bool Simulate(Instruction* instr);
|
||||
Pass::Status Simulate(Instruction* instr);
|
||||
|
||||
// Returns true if |instr| should be simulated again.
|
||||
bool ShouldSimulateAgain(Instruction* instr) const {
|
||||
|
||||
@@ -108,6 +108,7 @@ bool ReduceLoadSize::ReplaceExtract(Instruction* inst) {
|
||||
Instruction* new_access_chain = ir_builder.AddAccessChain(
|
||||
pointer_to_result_type_id,
|
||||
composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* new_load =
|
||||
ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id());
|
||||
|
||||
|
||||
@@ -41,14 +41,15 @@ Pass::Status ReplaceDescArrayAccessUsingVarIndex::Process() {
|
||||
Status status = Status::SuccessWithoutChange;
|
||||
for (Instruction& var : context()->types_values()) {
|
||||
if (descsroautil::IsDescriptorArray(context(), &var)) {
|
||||
if (ReplaceVariableAccessesWithConstantElements(&var))
|
||||
status = Status::SuccessWithChange;
|
||||
Status s = ReplaceVariableAccessesWithConstantElements(&var);
|
||||
if (s == Status::Failure) return Status::Failure;
|
||||
if (s == Status::SuccessWithChange) status = Status::SuccessWithChange;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::
|
||||
Pass::Status ReplaceDescArrayAccessUsingVarIndex::
|
||||
ReplaceVariableAccessesWithConstantElements(Instruction* var) const {
|
||||
std::vector<Instruction*> work_list;
|
||||
get_def_use_mgr()->ForEachUser(var, [&work_list](Instruction* use) {
|
||||
@@ -66,16 +67,16 @@ bool ReplaceDescArrayAccessUsingVarIndex::
|
||||
for (Instruction* access_chain : work_list) {
|
||||
if (descsroautil::GetAccessChainIndexAsConst(context(), access_chain) ==
|
||||
nullptr) {
|
||||
ReplaceAccessChain(var, access_chain);
|
||||
if (!ReplaceAccessChain(var, access_chain)) return Status::Failure;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
// Note that we do not consider OpLoad and OpCompositeExtract because
|
||||
// OpCompositeExtract always has constant literals for indices.
|
||||
return updated;
|
||||
return updated ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
|
||||
Instruction* var, Instruction* access_chain) const {
|
||||
uint32_t number_of_elements =
|
||||
descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
|
||||
@@ -83,21 +84,23 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceAccessChain(
|
||||
if (number_of_elements == 1) {
|
||||
UseConstIndexForAccessChain(access_chain, 0);
|
||||
get_def_use_mgr()->AnalyzeInstUse(access_chain);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
ReplaceUsersOfAccessChain(access_chain, number_of_elements);
|
||||
return ReplaceUsersOfAccessChain(access_chain, number_of_elements);
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::ReplaceUsersOfAccessChain(
|
||||
Instruction* access_chain, uint32_t number_of_elements) const {
|
||||
std::vector<Instruction*> final_users;
|
||||
CollectRecursiveUsersWithConcreteType(access_chain, &final_users);
|
||||
for (auto* inst : final_users) {
|
||||
std::deque<Instruction*> insts_to_be_cloned =
|
||||
CollectRequiredImageAndAccessInsts(inst);
|
||||
ReplaceNonUniformAccessWithSwitchCase(
|
||||
inst, access_chain, number_of_elements, insts_to_be_cloned);
|
||||
if (!ReplaceNonUniformAccessWithSwitchCase(
|
||||
inst, access_chain, number_of_elements, insts_to_be_cloned))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::CollectRecursiveUsersWithConcreteType(
|
||||
@@ -208,17 +211,23 @@ BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateCaseBlock(
|
||||
const std::deque<Instruction*>& insts_to_be_cloned,
|
||||
uint32_t branch_target_id,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
|
||||
auto* case_block = CreateNewBlock();
|
||||
AddConstElementAccessToCaseBlock(case_block, access_chain, element_index,
|
||||
old_ids_to_new_ids);
|
||||
CloneInstsToBlock(case_block, access_chain, insts_to_be_cloned,
|
||||
old_ids_to_new_ids);
|
||||
AddBranchToBlock(case_block, branch_target_id);
|
||||
UseNewIdsInBlock(case_block, *old_ids_to_new_ids);
|
||||
return case_block;
|
||||
std::unique_ptr<BasicBlock> case_block(CreateNewBlock());
|
||||
if (!case_block) return nullptr;
|
||||
|
||||
if (!AddConstElementAccessToCaseBlock(case_block.get(), access_chain,
|
||||
element_index, old_ids_to_new_ids)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!CloneInstsToBlock(case_block.get(), access_chain, insts_to_be_cloned,
|
||||
old_ids_to_new_ids)) {
|
||||
return nullptr;
|
||||
}
|
||||
AddBranchToBlock(case_block.get(), branch_target_id);
|
||||
UseNewIdsInBlock(case_block.get(), *old_ids_to_new_ids);
|
||||
return case_block.release();
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
|
||||
BasicBlock* block, Instruction* inst_to_skip_cloning,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
|
||||
@@ -227,6 +236,7 @@ void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
|
||||
std::unique_ptr<Instruction> clone(inst_to_be_cloned->Clone(context()));
|
||||
if (inst_to_be_cloned->HasResultId()) {
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
if (new_id == 0) return false;
|
||||
clone->SetResultId(new_id);
|
||||
(*old_ids_to_new_ids)[inst_to_be_cloned->result_id()] = new_id;
|
||||
}
|
||||
@@ -234,6 +244,7 @@ void ReplaceDescArrayAccessUsingVarIndex::CloneInstsToBlock(
|
||||
context()->set_instr_block(clone.get(), block);
|
||||
block->AddInstruction(std::move(clone));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
|
||||
@@ -250,18 +261,19 @@ void ReplaceDescArrayAccessUsingVarIndex::UseNewIdsInBlock(
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
|
||||
Instruction* access_chain_final_user, Instruction* access_chain,
|
||||
uint32_t number_of_elements,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned) const {
|
||||
auto* block = context()->get_instr_block(access_chain_final_user);
|
||||
// If the instruction does not belong to a block (i.e. in the case of
|
||||
// OpDecorate), no replacement is needed.
|
||||
if (!block) return;
|
||||
if (!block) return true;
|
||||
|
||||
// Create merge block and add terminator
|
||||
auto* merge_block = SeparateInstructionsIntoNewBlock(
|
||||
block, access_chain_final_user->NextNode());
|
||||
if (!merge_block) return false;
|
||||
|
||||
auto* function = block->GetParent();
|
||||
|
||||
@@ -273,6 +285,7 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
|
||||
std::unique_ptr<BasicBlock> case_block(CreateCaseBlock(
|
||||
access_chain, idx, insts_to_be_cloned, merge_block->id(),
|
||||
&old_ids_to_new_ids_for_cloned_insts));
|
||||
if (!case_block) return false;
|
||||
case_block_ids.push_back(case_block->id());
|
||||
function->InsertBasicBlockBefore(std::move(case_block), merge_block);
|
||||
|
||||
@@ -288,6 +301,7 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
|
||||
std::unique_ptr<BasicBlock> default_block(
|
||||
CreateDefaultBlock(access_chain_final_user->HasResultId(), &phi_operands,
|
||||
merge_block->id()));
|
||||
if (!default_block) return false;
|
||||
uint32_t default_block_id = default_block->id();
|
||||
function->InsertBasicBlockBefore(std::move(default_block), merge_block);
|
||||
|
||||
@@ -301,11 +315,13 @@ void ReplaceDescArrayAccessUsingVarIndex::ReplaceNonUniformAccessWithSwitchCase(
|
||||
if (!phi_operands.empty()) {
|
||||
uint32_t phi_id = CreatePhiInstruction(merge_block, phi_operands,
|
||||
case_block_ids, default_block_id);
|
||||
if (phi_id == 0) return false;
|
||||
context()->ReplaceAllUsesWith(access_chain_final_user->result_id(), phi_id);
|
||||
}
|
||||
|
||||
// Replace OpPhi incoming block operand that uses |block| with |merge_block|
|
||||
ReplacePhiIncomingBlock(block->id(), merge_block->id());
|
||||
return true;
|
||||
}
|
||||
|
||||
BasicBlock*
|
||||
@@ -316,13 +332,16 @@ ReplaceDescArrayAccessUsingVarIndex::SeparateInstructionsIntoNewBlock(
|
||||
&*separation_begin != separation_begin_inst) {
|
||||
++separation_begin;
|
||||
}
|
||||
return block->SplitBasicBlock(context(), context()->TakeNextId(),
|
||||
separation_begin);
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
if (new_id == 0) return nullptr;
|
||||
return block->SplitBasicBlock(context(), new_id, separation_begin);
|
||||
}
|
||||
|
||||
BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateNewBlock() const {
|
||||
auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(new Instruction(
|
||||
context(), spv::Op::OpLabel, 0, context()->TakeNextId(), {})));
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
if (new_id == 0) return nullptr;
|
||||
auto* new_block = new BasicBlock(std::unique_ptr<Instruction>(
|
||||
new Instruction(context(), spv::Op::OpLabel, 0, new_id, {})));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_block->GetLabelInst());
|
||||
context()->set_instr_block(new_block->GetLabelInst(), new_block);
|
||||
return new_block;
|
||||
@@ -336,7 +355,7 @@ void ReplaceDescArrayAccessUsingVarIndex::UseConstIndexForAccessChain(
|
||||
{const_element_idx_id});
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
|
||||
bool ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
|
||||
BasicBlock* case_block, Instruction* access_chain,
|
||||
uint32_t const_element_idx,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const {
|
||||
@@ -344,12 +363,14 @@ void ReplaceDescArrayAccessUsingVarIndex::AddConstElementAccessToCaseBlock(
|
||||
UseConstIndexForAccessChain(access_clone.get(), const_element_idx);
|
||||
|
||||
uint32_t new_access_id = context()->TakeNextId();
|
||||
if (new_access_id == 0) return false;
|
||||
(*old_ids_to_new_ids)[access_clone->result_id()] = new_access_id;
|
||||
access_clone->SetResultId(new_access_id);
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(access_clone.get());
|
||||
|
||||
context()->set_instr_block(access_clone.get(), case_block);
|
||||
case_block->AddInstruction(std::move(access_clone));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReplaceDescArrayAccessUsingVarIndex::AddBranchToBlock(
|
||||
@@ -363,6 +384,7 @@ BasicBlock* ReplaceDescArrayAccessUsingVarIndex::CreateDefaultBlock(
|
||||
bool null_const_for_phi_is_needed, std::vector<uint32_t>* phi_operands,
|
||||
uint32_t merge_block_id) const {
|
||||
auto* default_block = CreateNewBlock();
|
||||
if (!default_block) return nullptr;
|
||||
AddBranchToBlock(default_block, merge_block_id);
|
||||
if (!null_const_for_phi_is_needed) return default_block;
|
||||
|
||||
@@ -413,7 +435,11 @@ uint32_t ReplaceDescArrayAccessUsingVarIndex::CreatePhiInstruction(
|
||||
kAnalysisDefUseAndInstrToBlockMapping};
|
||||
uint32_t phi_result_type_id =
|
||||
context()->get_def_use_mgr()->GetDef(phi_operands[0])->type_id();
|
||||
auto* phi = builder.AddPhi(phi_result_type_id, incomings);
|
||||
Instruction* phi = builder.AddPhi(phi_result_type_id, incomings);
|
||||
if (!phi) {
|
||||
return 0;
|
||||
}
|
||||
context()->get_def_use_mgr()->AnalyzeInstDefUse(phi);
|
||||
return phi->result_id();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,14 +49,13 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
|
||||
private:
|
||||
// Replaces all accesses to |var| using variable indices with constant
|
||||
// elements of the array |var|. Creates switch-case statements to determine
|
||||
// the value of the variable index for all the possible cases. Returns
|
||||
// whether replacement is done or not.
|
||||
bool ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
|
||||
// the value of the variable index for all the possible cases.
|
||||
Status ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
|
||||
|
||||
// Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that
|
||||
// uses the descriptor variable |var| with the OpAccessChain or
|
||||
// OpInBoundsAccessChain instruction with a constant Indexes operand.
|
||||
void ReplaceAccessChain(Instruction* var, Instruction* use) const;
|
||||
bool ReplaceAccessChain(Instruction* var, Instruction* use) const;
|
||||
|
||||
// Updates the first Indexes operand of the OpAccessChain or
|
||||
// OpInBoundsAccessChain instruction |access_chain| to let it use a constant
|
||||
@@ -68,7 +67,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
|
||||
// |access_chain| that accesses an array descriptor variable using variable
|
||||
// indices with constant elements. |number_of_elements| is the number
|
||||
// of array elements.
|
||||
void ReplaceUsersOfAccessChain(Instruction* access_chain,
|
||||
bool ReplaceUsersOfAccessChain(Instruction* access_chain,
|
||||
uint32_t number_of_elements) const;
|
||||
|
||||
// Puts all the recursive users of |access_chain| with concrete result types
|
||||
@@ -102,7 +101,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
|
||||
// OpInBoundsAccessChain) will have a constant index for its first index. The
|
||||
// OpSwitch instruction will have the cases for the variable index of
|
||||
// |access_chain| from 0 to |number_of_elements| - 1.
|
||||
void ReplaceNonUniformAccessWithSwitchCase(
|
||||
bool ReplaceNonUniformAccessWithSwitchCase(
|
||||
Instruction* access_chain_final_user, Instruction* access_chain,
|
||||
uint32_t number_of_elements,
|
||||
const std::deque<Instruction*>& non_uniform_accesses_to_clone) const;
|
||||
@@ -124,7 +123,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
|
||||
// |access_chain| to |case_block|. The clone of |access_chain| will use
|
||||
// |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the
|
||||
// mapping from the result id of |access_chain| to the result of its clone.
|
||||
void AddConstElementAccessToCaseBlock(
|
||||
bool AddConstElementAccessToCaseBlock(
|
||||
BasicBlock* case_block, Instruction* access_chain,
|
||||
uint32_t const_element_idx,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
|
||||
@@ -132,7 +131,7 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
|
||||
// Clones all instructions in |insts_to_be_cloned| and put them to |block|.
|
||||
// |old_ids_to_new_ids| keeps the mapping from the result id of each
|
||||
// instruction of |insts_to_be_cloned| to the result of their clones.
|
||||
void CloneInstsToBlock(
|
||||
bool CloneInstsToBlock(
|
||||
BasicBlock* block, Instruction* inst_to_skip_cloning,
|
||||
const std::deque<Instruction*>& insts_to_be_cloned,
|
||||
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
|
||||
@@ -183,7 +182,8 @@ class ReplaceDescArrayAccessUsingVarIndex : public Pass {
|
||||
// |case_block_ids| and |default_block_id| as incoming blocks. The size of
|
||||
// |phi_operands| must be exactly 1 larger than the size of |case_block_ids|.
|
||||
// The last element of |phi_operands| will be used for |default_block_id|. It
|
||||
// adds the phi instruction to the beginning of |parent_block|.
|
||||
// adds the phi instruction to the beginning of |parent_block|. Returns 0 if
|
||||
// it fails to create the Phi instruction.
|
||||
uint32_t CreatePhiInstruction(BasicBlock* parent_block,
|
||||
const std::vector<uint32_t>& phi_operands,
|
||||
const std::vector<uint32_t>& case_block_ids,
|
||||
|
||||
@@ -275,8 +275,10 @@ spv_result_t SplitCombinedImageSamplerPass::RemapVar(
|
||||
auto [ptr_image_ty, ptr_sampler_ty] = SplitType(*combined_var_type);
|
||||
assert(ptr_image_ty);
|
||||
assert(ptr_sampler_ty);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* sampler_var = builder.AddVariable(
|
||||
ptr_sampler_ty->result_id(), SpvStorageClassUniformConstant);
|
||||
// TODO(1841): Handle id overflow.
|
||||
Instruction* image_var = builder.AddVariable(ptr_image_ty->result_id(),
|
||||
SpvStorageClassUniformConstant);
|
||||
modified_ = true;
|
||||
@@ -354,8 +356,10 @@ spv_result_t SplitCombinedImageSamplerPass::RemapUses(
|
||||
|
||||
// Create loads for the image part and sampler part.
|
||||
builder.SetInsertPoint(load);
|
||||
// TODO(1841): Handle id overflow.
|
||||
auto* image = builder.AddLoad(PointeeTypeId(use.image_part),
|
||||
use.image_part->result_id());
|
||||
// TODO(1841): Handle id overflow.
|
||||
auto* sampler = builder.AddLoad(PointeeTypeId(use.sampler_part),
|
||||
use.sampler_part->result_id());
|
||||
|
||||
@@ -459,9 +463,11 @@ spv_result_t SplitCombinedImageSamplerPass::RemapUses(
|
||||
|
||||
auto [result_image_part_ty, result_sampler_part_ty] =
|
||||
SplitType(*def_use_mgr_->GetDef(original_access_chain->type_id()));
|
||||
// TODO(1841): Handle id overflow.
|
||||
auto* result_image_part = builder.AddOpcodeAccessChain(
|
||||
use.user->opcode(), result_image_part_ty->result_id(),
|
||||
use.image_part->result_id(), indices);
|
||||
// TODO(1841): Handle id overflow.
|
||||
auto* result_sampler_part = builder.AddOpcodeAccessChain(
|
||||
use.user->opcode(), result_sampler_part_ty->result_id(),
|
||||
use.sampler_part->result_id(), indices);
|
||||
|
||||
@@ -804,11 +804,13 @@ void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) {
|
||||
InstructionBuilder builder(
|
||||
context(), where,
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||
// TODO(1841): Handle id overflow.
|
||||
auto extract_0 =
|
||||
builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0});
|
||||
context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id());
|
||||
// The extract's input was just changed to itself, so fix that.
|
||||
extract_0->SetInOperand(0u, {ext_inst->result_id()});
|
||||
// TODO(1841): Handle id overflow.
|
||||
auto extract_1 =
|
||||
builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1});
|
||||
builder.AddStore(ptr_id, extract_1->result_id());
|
||||
|
||||
@@ -143,8 +143,31 @@ uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement a normal form for opcodes that commute like integer
|
||||
// addition. This will let us know that a+b is the same value as b+a.
|
||||
// Apply normal form, so a+b == b+a
|
||||
switch (value_ins.opcode()) {
|
||||
case spv::Op::OpIAdd:
|
||||
case spv::Op::OpFAdd:
|
||||
case spv::Op::OpIMul:
|
||||
case spv::Op::OpFMul:
|
||||
case spv::Op::OpDot:
|
||||
case spv::Op::OpLogicalEqual:
|
||||
case spv::Op::OpLogicalNotEqual:
|
||||
case spv::Op::OpLogicalOr:
|
||||
case spv::Op::OpLogicalAnd:
|
||||
case spv::Op::OpIEqual:
|
||||
case spv::Op::OpINotEqual:
|
||||
case spv::Op::OpBitwiseOr:
|
||||
case spv::Op::OpBitwiseXor:
|
||||
case spv::Op::OpBitwiseAnd:
|
||||
if (value_ins.GetSingleWordInOperand(0) >
|
||||
value_ins.GetSingleWordInOperand(1)) {
|
||||
value_ins.SetInOperands(
|
||||
{{SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(1)}},
|
||||
{SPV_OPERAND_TYPE_ID, {value_ins.GetSingleWordInOperand(0)}}});
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, we check if this value has been computed before.
|
||||
auto value_iterator = instruction_to_value_.find(value_ins);
|
||||
|
||||
65
3rdparty/spirv-tools/source/table2.cpp
vendored
65
3rdparty/spirv-tools/source/table2.cpp
vendored
@@ -372,4 +372,69 @@ bool GetExtensionFromString(const char* name, Extension* extension) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is dirty copy of the spirv.hpp11 function
|
||||
// TODO - Use a generated version of this function
|
||||
const char* StorageClassToString(spv::StorageClass value) {
|
||||
switch (value) {
|
||||
case spv::StorageClass::UniformConstant:
|
||||
return "UniformConstant";
|
||||
case spv::StorageClass::Input:
|
||||
return "Input";
|
||||
case spv::StorageClass::Uniform:
|
||||
return "Uniform";
|
||||
case spv::StorageClass::Output:
|
||||
return "Output";
|
||||
case spv::StorageClass::Workgroup:
|
||||
return "Workgroup";
|
||||
case spv::StorageClass::CrossWorkgroup:
|
||||
return "CrossWorkgroup";
|
||||
case spv::StorageClass::Private:
|
||||
return "Private";
|
||||
case spv::StorageClass::Function:
|
||||
return "Function";
|
||||
case spv::StorageClass::Generic:
|
||||
return "Generic";
|
||||
case spv::StorageClass::PushConstant:
|
||||
return "PushConstant";
|
||||
case spv::StorageClass::AtomicCounter:
|
||||
return "AtomicCounter";
|
||||
case spv::StorageClass::Image:
|
||||
return "Image";
|
||||
case spv::StorageClass::StorageBuffer:
|
||||
return "StorageBuffer";
|
||||
case spv::StorageClass::TileImageEXT:
|
||||
return "TileImageEXT";
|
||||
case spv::StorageClass::TileAttachmentQCOM:
|
||||
return "TileAttachmentQCOM";
|
||||
case spv::StorageClass::NodePayloadAMDX:
|
||||
return "NodePayloadAMDX";
|
||||
case spv::StorageClass::CallableDataKHR:
|
||||
return "CallableDataKHR";
|
||||
case spv::StorageClass::IncomingCallableDataKHR:
|
||||
return "IncomingCallableDataKHR";
|
||||
case spv::StorageClass::RayPayloadKHR:
|
||||
return "RayPayloadKHR";
|
||||
case spv::StorageClass::HitAttributeKHR:
|
||||
return "HitAttributeKHR";
|
||||
case spv::StorageClass::IncomingRayPayloadKHR:
|
||||
return "IncomingRayPayloadKHR";
|
||||
case spv::StorageClass::ShaderRecordBufferKHR:
|
||||
return "ShaderRecordBufferKHR";
|
||||
case spv::StorageClass::PhysicalStorageBuffer:
|
||||
return "PhysicalStorageBuffer";
|
||||
case spv::StorageClass::HitObjectAttributeNV:
|
||||
return "HitObjectAttributeNV";
|
||||
case spv::StorageClass::TaskPayloadWorkgroupEXT:
|
||||
return "TaskPayloadWorkgroupEXT";
|
||||
case spv::StorageClass::CodeSectionINTEL:
|
||||
return "CodeSectionINTEL";
|
||||
case spv::StorageClass::DeviceOnlyINTEL:
|
||||
return "DeviceOnlyINTEL";
|
||||
case spv::StorageClass::HostOnlyINTEL:
|
||||
return "HostOnlyINTEL";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
3
3rdparty/spirv-tools/source/table2.h
vendored
3
3rdparty/spirv-tools/source/table2.h
vendored
@@ -256,5 +256,8 @@ bool GetExtensionFromString(const char* str, Extension* extension);
|
||||
// Returns text string corresponding to |extension|.
|
||||
const char* ExtensionToString(Extension extension);
|
||||
|
||||
/// Used to provide better error message
|
||||
const char* StorageClassToString(spv::StorageClass value);
|
||||
|
||||
} // namespace spvtools
|
||||
#endif // SOURCE_TABLE2_H_
|
||||
|
||||
@@ -2176,17 +2176,19 @@ bool AllowsLayout(ValidationState_t& vstate, const spv::StorageClass sc) {
|
||||
}
|
||||
}
|
||||
|
||||
bool UsesExplicitLayout(ValidationState_t& vstate, uint32_t type_id,
|
||||
std::unordered_map<uint32_t, bool>& cache) {
|
||||
// Returns a decoration used to make it explicit
|
||||
spv::Decoration UsesExplicitLayout(
|
||||
ValidationState_t& vstate, uint32_t type_id,
|
||||
std::unordered_map<uint32_t, spv::Decoration>& cache) {
|
||||
if (type_id == 0) {
|
||||
return false;
|
||||
return spv::Decoration::Max;
|
||||
}
|
||||
|
||||
if (cache.count(type_id)) {
|
||||
return cache[type_id];
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
spv::Decoration res = spv::Decoration::Max;
|
||||
const auto type_inst = vstate.FindDef(type_id);
|
||||
if (type_inst->opcode() == spv::Op::OpTypeStruct ||
|
||||
type_inst->opcode() == spv::Op::OpTypeArray ||
|
||||
@@ -2202,21 +2204,26 @@ bool UsesExplicitLayout(ValidationState_t& vstate, uint32_t type_id,
|
||||
allowLayoutDecorations = AllowsLayout(vstate, sc);
|
||||
}
|
||||
if (!allowLayoutDecorations) {
|
||||
res = std::any_of(
|
||||
iter->second.begin(), iter->second.end(), [](const Decoration& d) {
|
||||
return d.dec_type() == spv::Decoration::Block ||
|
||||
d.dec_type() == spv::Decoration::BufferBlock ||
|
||||
d.dec_type() == spv::Decoration::Offset ||
|
||||
d.dec_type() == spv::Decoration::ArrayStride ||
|
||||
d.dec_type() == spv::Decoration::MatrixStride;
|
||||
});
|
||||
for (const auto& d : iter->second) {
|
||||
const spv::Decoration dec = d.dec_type();
|
||||
if (dec == spv::Decoration::Block ||
|
||||
dec == spv::Decoration::BufferBlock ||
|
||||
dec == spv::Decoration::Offset ||
|
||||
dec == spv::Decoration::ArrayStride ||
|
||||
dec == spv::Decoration::MatrixStride) {
|
||||
res = dec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
if (res == spv::Decoration::Max) {
|
||||
switch (type_inst->opcode()) {
|
||||
case spv::Op::OpTypeStruct:
|
||||
for (uint32_t i = 1; !res && i < type_inst->operands().size(); i++) {
|
||||
for (uint32_t i = 1;
|
||||
res == spv::Decoration::Max && i < type_inst->operands().size();
|
||||
i++) {
|
||||
res = UsesExplicitLayout(
|
||||
vstate, type_inst->GetOperandAs<uint32_t>(i), cache);
|
||||
}
|
||||
@@ -2248,10 +2255,13 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, bool> cache;
|
||||
std::unordered_map<uint32_t, spv::Decoration> cache;
|
||||
for (const auto& inst : vstate.ordered_instructions()) {
|
||||
const auto type_id = inst.type_id();
|
||||
const auto type_inst = vstate.FindDef(type_id);
|
||||
|
||||
spv::StorageClass sc = spv::StorageClass::Max;
|
||||
spv::Decoration layout_dec = spv::Decoration::Max;
|
||||
uint32_t fail_id = 0;
|
||||
// Variables are the main place to check for improper decorations, but some
|
||||
// untyped pointer instructions must also be checked since those types may
|
||||
@@ -2261,16 +2271,18 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
|
||||
switch (inst.opcode()) {
|
||||
case spv::Op::OpVariable:
|
||||
case spv::Op::OpUntypedVariableKHR: {
|
||||
const auto sc = inst.GetOperandAs<spv::StorageClass>(2);
|
||||
sc = inst.GetOperandAs<spv::StorageClass>(2);
|
||||
auto check_id = type_id;
|
||||
if (inst.opcode() == spv::Op::OpUntypedVariableKHR) {
|
||||
if (inst.operands().size() > 3) {
|
||||
check_id = inst.GetOperandAs<uint32_t>(3);
|
||||
}
|
||||
}
|
||||
if (!AllowsLayout(vstate, sc) &&
|
||||
UsesExplicitLayout(vstate, check_id, cache)) {
|
||||
fail_id = check_id;
|
||||
if (!AllowsLayout(vstate, sc)) {
|
||||
layout_dec = UsesExplicitLayout(vstate, check_id, cache);
|
||||
if (layout_dec != spv::Decoration::Max) {
|
||||
fail_id = check_id;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2280,13 +2292,17 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
|
||||
case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: {
|
||||
// Check both the base type and return type. The return type may have an
|
||||
// invalid array stride.
|
||||
const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
|
||||
const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
|
||||
sc = type_inst->GetOperandAs<spv::StorageClass>(1);
|
||||
if (!AllowsLayout(vstate, sc)) {
|
||||
if (UsesExplicitLayout(vstate, base_type_id, cache)) {
|
||||
const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
|
||||
layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
|
||||
if (layout_dec != spv::Decoration::Max) {
|
||||
fail_id = base_type_id;
|
||||
} else if (UsesExplicitLayout(vstate, type_id, cache)) {
|
||||
fail_id = type_id;
|
||||
} else {
|
||||
layout_dec = UsesExplicitLayout(vstate, type_id, cache);
|
||||
if (layout_dec != spv::Decoration::Max) {
|
||||
fail_id = type_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2296,11 +2312,13 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
|
||||
const auto ptr_ty_id =
|
||||
vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
|
||||
const auto ptr_ty = vstate.FindDef(ptr_ty_id);
|
||||
const auto sc = ptr_ty->GetOperandAs<spv::StorageClass>(1);
|
||||
const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
|
||||
if (!AllowsLayout(vstate, sc) &&
|
||||
UsesExplicitLayout(vstate, base_type_id, cache)) {
|
||||
fail_id = base_type_id;
|
||||
sc = ptr_ty->GetOperandAs<spv::StorageClass>(1);
|
||||
if (!AllowsLayout(vstate, sc)) {
|
||||
const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
|
||||
layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
|
||||
if (layout_dec != spv::Decoration::Max) {
|
||||
fail_id = base_type_id;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2309,10 +2327,12 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
|
||||
const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
|
||||
if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
|
||||
// For untyped pointers check the return type for an invalid layout.
|
||||
const auto sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
|
||||
if (!AllowsLayout(vstate, sc) &&
|
||||
UsesExplicitLayout(vstate, type_id, cache)) {
|
||||
fail_id = type_id;
|
||||
sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
|
||||
if (!AllowsLayout(vstate, sc)) {
|
||||
layout_dec = UsesExplicitLayout(vstate, type_id, cache);
|
||||
if (layout_dec != spv::Decoration::Max) {
|
||||
fail_id = type_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2323,11 +2343,13 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
|
||||
if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
|
||||
// For untyped pointers, check the type of the data operand for an
|
||||
// invalid layout.
|
||||
const auto sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
|
||||
const auto data_type_id = vstate.GetOperandTypeId(&inst, 1);
|
||||
if (!AllowsLayout(vstate, sc) &&
|
||||
UsesExplicitLayout(vstate, data_type_id, cache)) {
|
||||
fail_id = inst.GetOperandAs<uint32_t>(2);
|
||||
sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
|
||||
if (!AllowsLayout(vstate, sc)) {
|
||||
const auto data_type_id = vstate.GetOperandTypeId(&inst, 1);
|
||||
layout_dec = UsesExplicitLayout(vstate, data_type_id, cache);
|
||||
if (layout_dec != spv::Decoration::Max) {
|
||||
fail_id = inst.GetOperandAs<uint32_t>(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2339,7 +2361,10 @@ spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< vstate.VkErrorID(10684)
|
||||
<< "Invalid explicit layout decorations on type for operand "
|
||||
<< vstate.getIdName(fail_id);
|
||||
<< vstate.getIdName(fail_id) << ", the "
|
||||
<< spvtools::StorageClassToString(sc)
|
||||
<< " storage class has a explicit layout from the "
|
||||
<< vstate.SpvDecorationString(layout_dec) << " decoration.";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5383
3rdparty/spirv-tools/source/val/validate_extensions.cpp
vendored
5383
3rdparty/spirv-tools/source/val/validate_extensions.cpp
vendored
File diff suppressed because it is too large
Load Diff
@@ -399,10 +399,19 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
|
||||
uint32_t largest_scalar = 0;
|
||||
if (dst_sc == spv::StorageClass::PhysicalStorageBuffer) {
|
||||
largest_scalar =
|
||||
_.GetLargestScalarType(dst_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
if (dst_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
|
||||
largest_scalar =
|
||||
_.GetLargestScalarType(dst_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
} else if (inst->type_id() != 0) {
|
||||
largest_scalar = _.GetLargestScalarType(inst->type_id());
|
||||
} else {
|
||||
// TODO need to handle cases like OpStore and OpCopyMemorySized which
|
||||
// don't have a result type
|
||||
}
|
||||
}
|
||||
if (src_sc == spv::StorageClass::PhysicalStorageBuffer) {
|
||||
// TODO - Handle Untyped in OpCopyMemory
|
||||
if (src_sc == spv::StorageClass::PhysicalStorageBuffer &&
|
||||
src_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
|
||||
largest_scalar = std::max(
|
||||
largest_scalar,
|
||||
_.GetLargestScalarType(src_pointer_type->GetOperandAs<uint32_t>(2)));
|
||||
|
||||
Reference in New Issue
Block a user