Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/KhronosGroup/SPIRV-Tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/opt/liveness.cpp')
-rw-r--r--source/opt/liveness.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/source/opt/liveness.cpp b/source/opt/liveness.cpp
new file mode 100644
index 000000000..08b1c6ebc
--- /dev/null
+++ b/source/opt/liveness.cpp
@@ -0,0 +1,330 @@
+// Copyright (c) 2022 The Khronos Group Inc.
+// Copyright (c) 2022 LunarG Inc.
+//
+// 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/liveness.h"
+
+#include "source/opt/ir_context.h"
+
+namespace {
+
+const uint32_t kDecorationLocationInIdx = 2;
+const uint32_t kOpDecorateMemberMemberInIdx = 1;
+const uint32_t kOpDecorateMemberLocationInIdx = 3;
+const uint32_t kOpDecorateBuiltInLiteralInIdx = 2;
+const uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3;
+
+} // namespace
+
+namespace spvtools {
+namespace opt {
+namespace analysis {
+
+LivenessManager::LivenessManager(IRContext* ctx) : ctx_(ctx), computed_(false) {
+ // Liveness sets computed when queried
+}
+
+void LivenessManager::InitializeAnalysis() {
+ live_locs_.clear();
+ live_builtins_.clear();
+ // Mark all builtins live for frag shader.
+ if (context()->GetStage() == SpvExecutionModelFragment) {
+ live_builtins_.insert(SpvBuiltInPointSize);
+ live_builtins_.insert(SpvBuiltInClipDistance);
+ live_builtins_.insert(SpvBuiltInCullDistance);
+ }
+}
+
+bool LivenessManager::IsAnalyzedBuiltin(uint32_t bi) {
+ // There are only three builtins that can be analyzed and removed between
+ // two stages: PointSize, ClipDistance and CullDistance. All others are
+ // always consumed implicitly by the downstream stage.
+ return bi == SpvBuiltInPointSize || bi == SpvBuiltInClipDistance ||
+ bi == SpvBuiltInCullDistance;
+}
+
+bool LivenessManager::AnalyzeBuiltIn(uint32_t id) {
+ auto deco_mgr = context()->get_decoration_mgr();
+ bool saw_builtin = false;
+ // Analyze all builtin decorations of |id|.
+ (void)deco_mgr->ForEachDecoration(
+ id, SpvDecorationBuiltIn,
+ [this, &saw_builtin](const Instruction& deco_inst) {
+ saw_builtin = true;
+ // No need to process builtins in frag shader. All assumed used.
+ if (context()->GetStage() == SpvExecutionModelFragment) return;
+ uint32_t builtin = SpvBuiltInMax;
+ if (deco_inst.opcode() == SpvOpDecorate)
+ builtin =
+ deco_inst.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx);
+ else if (deco_inst.opcode() == SpvOpMemberDecorate)
+ builtin = deco_inst.GetSingleWordInOperand(
+ kOpDecorateMemberBuiltInLiteralInIdx);
+ else
+ assert(false && "unexpected decoration");
+ if (IsAnalyzedBuiltin(builtin)) live_builtins_.insert(builtin);
+ });
+ return saw_builtin;
+}
+
+void LivenessManager::MarkLocsLive(uint32_t start, uint32_t count) {
+ auto finish = start + count;
+ for (uint32_t u = start; u < finish; ++u) {
+ live_locs_.insert(u);
+ }
+}
+
+uint32_t LivenessManager::GetLocSize(const analysis::Type* type) const {
+ auto arr_type = type->AsArray();
+ if (arr_type) {
+ auto comp_type = arr_type->element_type();
+ auto len_info = arr_type->length_info();
+ assert(len_info.words[0] == analysis::Array::LengthInfo::kConstant &&
+ "unexpected array length");
+ auto comp_len = len_info.words[1];
+ return comp_len * GetLocSize(comp_type);
+ }
+ auto struct_type = type->AsStruct();
+ if (struct_type) {
+ uint32_t size = 0u;
+ for (auto& el_type : struct_type->element_types())
+ size += GetLocSize(el_type);
+ return size;
+ }
+ auto mat_type = type->AsMatrix();
+ if (mat_type) {
+ auto cnt = mat_type->element_count();
+ auto comp_type = mat_type->element_type();
+ return cnt * GetLocSize(comp_type);
+ }
+ auto vec_type = type->AsVector();
+ if (vec_type) {
+ auto comp_type = vec_type->element_type();
+ if (comp_type->AsInteger()) return 1;
+ auto float_type = comp_type->AsFloat();
+ assert(float_type && "unexpected vector component type");
+ auto width = float_type->width();
+ if (width == 32 || width == 16) return 1;
+ assert(width == 64 && "unexpected float type width");
+ auto comp_cnt = vec_type->element_count();
+ return (comp_cnt > 2) ? 2 : 1;
+ }
+ assert((type->AsInteger() || type->AsFloat()) && "unexpected input type");
+ return 1;
+}
+
+const analysis::Type* LivenessManager::GetComponentType(
+ uint32_t index, const analysis::Type* agg_type) const {
+ auto arr_type = agg_type->AsArray();
+ if (arr_type) return arr_type->element_type();
+ auto struct_type = agg_type->AsStruct();
+ if (struct_type) return struct_type->element_types()[index];
+ auto mat_type = agg_type->AsMatrix();
+ if (mat_type) return mat_type->element_type();
+ auto vec_type = agg_type->AsVector();
+ assert(vec_type && "unexpected non-aggregate type");
+ return vec_type->element_type();
+}
+
+uint32_t LivenessManager::GetLocOffset(uint32_t index,
+ const analysis::Type* agg_type) const {
+ auto arr_type = agg_type->AsArray();
+ if (arr_type) return index * GetLocSize(arr_type->element_type());
+ auto struct_type = agg_type->AsStruct();
+ if (struct_type) {
+ uint32_t offset = 0u;
+ uint32_t cnt = 0u;
+ for (auto& el_type : struct_type->element_types()) {
+ if (cnt == index) break;
+ offset += GetLocSize(el_type);
+ ++cnt;
+ }
+ return offset;
+ }
+ auto mat_type = agg_type->AsMatrix();
+ if (mat_type) return index * GetLocSize(mat_type->element_type());
+ auto vec_type = agg_type->AsVector();
+ assert(vec_type && "unexpected non-aggregate type");
+ auto comp_type = vec_type->element_type();
+ auto flt_type = comp_type->AsFloat();
+ if (flt_type && flt_type->width() == 64u && index >= 2u) return 1;
+ return 0;
+}
+
+void LivenessManager::AnalyzeAccessChainLoc(const Instruction* ac,
+ const analysis::Type** curr_type,
+ uint32_t* offset, bool* no_loc,
+ bool is_patch, bool input) {
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
+ // For tesc, tese and geom input variables, and tesc output variables,
+ // first array index does not contribute to offset.
+ auto stage = context()->GetStage();
+ bool skip_first_index = false;
+ if ((input && (stage == SpvExecutionModelTessellationControl ||
+ stage == SpvExecutionModelTessellationEvaluation ||
+ stage == SpvExecutionModelGeometry)) ||
+ (!input && stage == SpvExecutionModelTessellationControl))
+ skip_first_index = !is_patch;
+ uint32_t ocnt = 0;
+ ac->WhileEachInOperand([this, &ocnt, def_use_mgr, type_mgr, deco_mgr,
+ curr_type, offset, no_loc,
+ skip_first_index](const uint32_t* opnd) {
+ if (ocnt >= 1) {
+ // Skip first index's contribution to offset if indicated
+ if (ocnt == 1 && skip_first_index) {
+ auto arr_type = (*curr_type)->AsArray();
+ assert(arr_type && "unexpected wrapper type");
+ *curr_type = arr_type->element_type();
+ ocnt++;
+ return true;
+ }
+ // If any non-constant index, mark the entire current object and return.
+ auto idx_inst = def_use_mgr->GetDef(*opnd);
+ if (idx_inst->opcode() != SpvOpConstant) return false;
+ // If current type is struct, look for location decoration on member and
+ // reset offset if found.
+ auto index = idx_inst->GetSingleWordInOperand(0);
+ auto str_type = (*curr_type)->AsStruct();
+ if (str_type) {
+ uint32_t loc = 0;
+ auto str_type_id = type_mgr->GetId(str_type);
+ bool no_mem_loc = deco_mgr->WhileEachDecoration(
+ str_type_id, SpvDecorationLocation,
+ [&loc, index, no_loc](const Instruction& deco) {
+ assert(deco.opcode() == SpvOpMemberDecorate &&
+ "unexpected decoration");
+ if (deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx) ==
+ index) {
+ loc =
+ deco.GetSingleWordInOperand(kOpDecorateMemberLocationInIdx);
+ *no_loc = false;
+ return false;
+ }
+ return true;
+ });
+ if (!no_mem_loc) {
+ *offset = loc;
+ *curr_type = GetComponentType(index, *curr_type);
+ ocnt++;
+ return true;
+ }
+ }
+
+ // Update offset and current type based on constant index.
+ *offset += GetLocOffset(index, *curr_type);
+ *curr_type = GetComponentType(index, *curr_type);
+ }
+ ocnt++;
+ return true;
+ });
+}
+
+void LivenessManager::MarkRefLive(const Instruction* ref, Instruction* var) {
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
+ // Find variable location if present.
+ uint32_t loc = 0;
+ auto var_id = var->result_id();
+ bool no_loc = deco_mgr->WhileEachDecoration(
+ var_id, SpvDecorationLocation, [&loc](const Instruction& deco) {
+ assert(deco.opcode() == SpvOpDecorate && "unexpected decoration");
+ loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx);
+ return false;
+ });
+ // Find patch decoration if present
+ bool is_patch = !deco_mgr->WhileEachDecoration(
+ var_id, SpvDecorationPatch, [](const Instruction& deco) {
+ if (deco.opcode() != SpvOpDecorate)
+ assert(false && "unexpected decoration");
+ return false;
+ });
+ // If use is a load, mark all locations of var
+ auto ptr_type = type_mgr->GetType(var->type_id())->AsPointer();
+ assert(ptr_type && "unexpected var type");
+ auto var_type = ptr_type->pointee_type();
+ if (ref->opcode() == SpvOpLoad) {
+ assert(!no_loc && "missing input variable location");
+ MarkLocsLive(loc, GetLocSize(var_type));
+ return;
+ }
+ // Mark just those locations indicated by access chain
+ assert((ref->opcode() == SpvOpAccessChain ||
+ ref->opcode() == SpvOpInBoundsAccessChain) &&
+ "unexpected use of input variable");
+ // Traverse access chain, compute location offset and type of reference
+ // through constant indices and mark those locs live. Assert if no location
+ // found.
+ uint32_t offset = loc;
+ auto curr_type = var_type;
+ AnalyzeAccessChainLoc(ref, &curr_type, &offset, &no_loc, is_patch);
+ assert(!no_loc && "missing input variable location");
+ MarkLocsLive(offset, GetLocSize(curr_type));
+}
+
+void LivenessManager::ComputeLiveness() {
+ InitializeAnalysis();
+ analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
+ analysis::TypeManager* type_mgr = context()->get_type_mgr();
+ // Process all input variables
+ for (auto& var : context()->types_values()) {
+ if (var.opcode() != SpvOpVariable) {
+ continue;
+ }
+ analysis::Type* var_type = type_mgr->GetType(var.type_id());
+ analysis::Pointer* ptr_type = var_type->AsPointer();
+ if (ptr_type->storage_class() != SpvStorageClassInput) {
+ continue;
+ }
+ // If var is builtin, mark live if analyzed and continue to next variable
+ auto var_id = var.result_id();
+ if (AnalyzeBuiltIn(var_id)) continue;
+ // If interface block with builtin members, mark live if analyzed and
+ // continue to next variable. Input interface blocks will only appear
+ // in tesc, tese and geom shaders. Will need to strip off one level of
+ // arrayness to get to block type.
+ auto pte_type = ptr_type->pointee_type();
+ auto arr_type = pte_type->AsArray();
+ if (arr_type) {
+ auto elt_type = arr_type->element_type();
+ auto str_type = elt_type->AsStruct();
+ if (str_type) {
+ auto str_type_id = type_mgr->GetId(str_type);
+ if (AnalyzeBuiltIn(str_type_id)) continue;
+ }
+ }
+ // Mark all used locations of var live
+ def_use_mgr->ForEachUser(var_id, [this, &var](Instruction* user) {
+ auto op = user->opcode();
+ if (op == SpvOpEntryPoint || op == SpvOpName || op == SpvOpDecorate)
+ return;
+ MarkRefLive(user, &var);
+ });
+ }
+}
+
+void LivenessManager::GetLiveness(std::unordered_set<uint32_t>* live_locs,
+ std::unordered_set<uint32_t>* live_builtins) {
+ if (!computed_) {
+ ComputeLiveness();
+ computed_ = true;
+ }
+ *live_locs = live_locs_;
+ *live_builtins = live_builtins_;
+}
+
+} // namespace analysis
+} // namespace opt
+} // namespace spvtools