2
0

liveness.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // Copyright (c) 2022 The Khronos Group Inc.
  2. // Copyright (c) 2022 LunarG Inc.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #include "source/opt/liveness.h"
  16. #include "source/opt/ir_context.h"
  17. namespace spvtools {
  18. namespace opt {
  19. namespace analysis {
  20. namespace {
  21. constexpr uint32_t kDecorationLocationInIdx = 2;
  22. constexpr uint32_t kOpDecorateMemberMemberInIdx = 1;
  23. constexpr uint32_t kOpDecorateMemberLocationInIdx = 3;
  24. constexpr uint32_t kOpDecorateBuiltInLiteralInIdx = 2;
  25. constexpr uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3;
  26. } // namespace
  27. LivenessManager::LivenessManager(IRContext* ctx) : ctx_(ctx), computed_(false) {
  28. // Liveness sets computed when queried
  29. }
  30. void LivenessManager::InitializeAnalysis() {
  31. live_locs_.clear();
  32. live_builtins_.clear();
  33. // Mark all builtins live for frag shader.
  34. if (context()->GetStage() == spv::ExecutionModel::Fragment) {
  35. live_builtins_.insert(uint32_t(spv::BuiltIn::PointSize));
  36. live_builtins_.insert(uint32_t(spv::BuiltIn::ClipDistance));
  37. live_builtins_.insert(uint32_t(spv::BuiltIn::CullDistance));
  38. }
  39. }
  40. bool LivenessManager::IsAnalyzedBuiltin(uint32_t bi) {
  41. // There are only three builtins that can be analyzed and removed between
  42. // two stages: PointSize, ClipDistance and CullDistance. All others are
  43. // always consumed implicitly by the downstream stage.
  44. const auto builtin = spv::BuiltIn(bi);
  45. return builtin == spv::BuiltIn::PointSize ||
  46. builtin == spv::BuiltIn::ClipDistance ||
  47. builtin == spv::BuiltIn::CullDistance;
  48. }
  49. bool LivenessManager::AnalyzeBuiltIn(uint32_t id) {
  50. auto deco_mgr = context()->get_decoration_mgr();
  51. bool saw_builtin = false;
  52. // Analyze all builtin decorations of |id|.
  53. (void)deco_mgr->ForEachDecoration(
  54. id, uint32_t(spv::Decoration::BuiltIn),
  55. [this, &saw_builtin](const Instruction& deco_inst) {
  56. saw_builtin = true;
  57. // No need to process builtins in frag shader. All assumed used.
  58. if (context()->GetStage() == spv::ExecutionModel::Fragment) return;
  59. uint32_t builtin = uint32_t(spv::BuiltIn::Max);
  60. if (deco_inst.opcode() == spv::Op::OpDecorate)
  61. builtin =
  62. deco_inst.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx);
  63. else if (deco_inst.opcode() == spv::Op::OpMemberDecorate)
  64. builtin = deco_inst.GetSingleWordInOperand(
  65. kOpDecorateMemberBuiltInLiteralInIdx);
  66. else
  67. assert(false && "unexpected decoration");
  68. if (IsAnalyzedBuiltin(builtin)) live_builtins_.insert(builtin);
  69. });
  70. return saw_builtin;
  71. }
  72. void LivenessManager::MarkLocsLive(uint32_t start, uint32_t count) {
  73. auto finish = start + count;
  74. for (uint32_t u = start; u < finish; ++u) {
  75. live_locs_.insert(u);
  76. }
  77. }
  78. uint32_t LivenessManager::GetLocSize(const analysis::Type* type) const {
  79. auto arr_type = type->AsArray();
  80. if (arr_type) {
  81. auto comp_type = arr_type->element_type();
  82. auto len_info = arr_type->length_info();
  83. assert(len_info.words[0] == analysis::Array::LengthInfo::kConstant &&
  84. "unexpected array length");
  85. auto comp_len = len_info.words[1];
  86. return comp_len * GetLocSize(comp_type);
  87. }
  88. auto struct_type = type->AsStruct();
  89. if (struct_type) {
  90. uint32_t size = 0u;
  91. for (auto& el_type : struct_type->element_types())
  92. size += GetLocSize(el_type);
  93. return size;
  94. }
  95. auto mat_type = type->AsMatrix();
  96. if (mat_type) {
  97. auto cnt = mat_type->element_count();
  98. auto comp_type = mat_type->element_type();
  99. return cnt * GetLocSize(comp_type);
  100. }
  101. auto vec_type = type->AsVector();
  102. if (vec_type) {
  103. auto comp_type = vec_type->element_type();
  104. if (comp_type->AsInteger()) return 1;
  105. auto float_type = comp_type->AsFloat();
  106. assert(float_type && "unexpected vector component type");
  107. auto width = float_type->width();
  108. if (width == 32 || width == 16) return 1;
  109. assert(width == 64 && "unexpected float type width");
  110. auto comp_cnt = vec_type->element_count();
  111. return (comp_cnt > 2) ? 2 : 1;
  112. }
  113. assert((type->AsInteger() || type->AsFloat()) && "unexpected input type");
  114. return 1;
  115. }
  116. const analysis::Type* LivenessManager::GetComponentType(
  117. uint32_t index, const analysis::Type* agg_type) const {
  118. auto arr_type = agg_type->AsArray();
  119. if (arr_type) return arr_type->element_type();
  120. auto struct_type = agg_type->AsStruct();
  121. if (struct_type) return struct_type->element_types()[index];
  122. auto mat_type = agg_type->AsMatrix();
  123. if (mat_type) return mat_type->element_type();
  124. auto vec_type = agg_type->AsVector();
  125. assert(vec_type && "unexpected non-aggregate type");
  126. return vec_type->element_type();
  127. }
  128. uint32_t LivenessManager::GetLocOffset(uint32_t index,
  129. const analysis::Type* agg_type) const {
  130. auto arr_type = agg_type->AsArray();
  131. if (arr_type) return index * GetLocSize(arr_type->element_type());
  132. auto struct_type = agg_type->AsStruct();
  133. if (struct_type) {
  134. uint32_t offset = 0u;
  135. uint32_t cnt = 0u;
  136. for (auto& el_type : struct_type->element_types()) {
  137. if (cnt == index) break;
  138. offset += GetLocSize(el_type);
  139. ++cnt;
  140. }
  141. return offset;
  142. }
  143. auto mat_type = agg_type->AsMatrix();
  144. if (mat_type) return index * GetLocSize(mat_type->element_type());
  145. auto vec_type = agg_type->AsVector();
  146. assert(vec_type && "unexpected non-aggregate type");
  147. auto comp_type = vec_type->element_type();
  148. auto flt_type = comp_type->AsFloat();
  149. if (flt_type && flt_type->width() == 64u && index >= 2u) return 1;
  150. return 0;
  151. }
  152. void LivenessManager::AnalyzeAccessChainLoc(const Instruction* ac,
  153. const analysis::Type** curr_type,
  154. uint32_t* offset, bool* no_loc,
  155. bool is_patch, bool input) {
  156. analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
  157. analysis::TypeManager* type_mgr = context()->get_type_mgr();
  158. analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
  159. // For tesc, tese and geom input variables, and tesc output variables,
  160. // first array index does not contribute to offset.
  161. auto stage = context()->GetStage();
  162. bool skip_first_index = false;
  163. if ((input && (stage == spv::ExecutionModel::TessellationControl ||
  164. stage == spv::ExecutionModel::TessellationEvaluation ||
  165. stage == spv::ExecutionModel::Geometry)) ||
  166. (!input && stage == spv::ExecutionModel::TessellationControl))
  167. skip_first_index = !is_patch;
  168. uint32_t ocnt = 0;
  169. ac->WhileEachInOperand([this, &ocnt, def_use_mgr, type_mgr, deco_mgr,
  170. curr_type, offset, no_loc,
  171. skip_first_index](const uint32_t* opnd) {
  172. if (ocnt >= 1) {
  173. // Skip first index's contribution to offset if indicated
  174. if (ocnt == 1 && skip_first_index) {
  175. auto arr_type = (*curr_type)->AsArray();
  176. assert(arr_type && "unexpected wrapper type");
  177. *curr_type = arr_type->element_type();
  178. ocnt++;
  179. return true;
  180. }
  181. // If any non-constant index, mark the entire current object and return.
  182. auto idx_inst = def_use_mgr->GetDef(*opnd);
  183. if (idx_inst->opcode() != spv::Op::OpConstant) return false;
  184. // If current type is struct, look for location decoration on member and
  185. // reset offset if found.
  186. auto index = idx_inst->GetSingleWordInOperand(0);
  187. auto str_type = (*curr_type)->AsStruct();
  188. if (str_type) {
  189. uint32_t loc = 0;
  190. auto str_type_id = type_mgr->GetId(str_type);
  191. bool no_mem_loc = deco_mgr->WhileEachDecoration(
  192. str_type_id, uint32_t(spv::Decoration::Location),
  193. [&loc, index, no_loc](const Instruction& deco) {
  194. assert(deco.opcode() == spv::Op::OpMemberDecorate &&
  195. "unexpected decoration");
  196. if (deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx) ==
  197. index) {
  198. loc =
  199. deco.GetSingleWordInOperand(kOpDecorateMemberLocationInIdx);
  200. *no_loc = false;
  201. return false;
  202. }
  203. return true;
  204. });
  205. if (!no_mem_loc) {
  206. *offset = loc;
  207. *curr_type = GetComponentType(index, *curr_type);
  208. ocnt++;
  209. return true;
  210. }
  211. }
  212. // Update offset and current type based on constant index.
  213. *offset += GetLocOffset(index, *curr_type);
  214. *curr_type = GetComponentType(index, *curr_type);
  215. }
  216. ocnt++;
  217. return true;
  218. });
  219. }
  220. void LivenessManager::MarkRefLive(const Instruction* ref, Instruction* var) {
  221. analysis::TypeManager* type_mgr = context()->get_type_mgr();
  222. analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
  223. // Find variable location if present.
  224. uint32_t loc = 0;
  225. auto var_id = var->result_id();
  226. bool no_loc = deco_mgr->WhileEachDecoration(
  227. var_id, uint32_t(spv::Decoration::Location),
  228. [&loc](const Instruction& deco) {
  229. assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
  230. loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx);
  231. return false;
  232. });
  233. // Find patch decoration if present
  234. bool is_patch = !deco_mgr->WhileEachDecoration(
  235. var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) {
  236. if (deco.opcode() != spv::Op::OpDecorate)
  237. assert(false && "unexpected decoration");
  238. return false;
  239. });
  240. // If use is a load, mark all locations of var
  241. auto ptr_type = type_mgr->GetType(var->type_id())->AsPointer();
  242. assert(ptr_type && "unexpected var type");
  243. auto var_type = ptr_type->pointee_type();
  244. if (ref->opcode() == spv::Op::OpLoad) {
  245. assert(!no_loc && "missing input variable location");
  246. MarkLocsLive(loc, GetLocSize(var_type));
  247. return;
  248. }
  249. // Mark just those locations indicated by access chain
  250. assert((ref->opcode() == spv::Op::OpAccessChain ||
  251. ref->opcode() == spv::Op::OpInBoundsAccessChain) &&
  252. "unexpected use of input variable");
  253. // Traverse access chain, compute location offset and type of reference
  254. // through constant indices and mark those locs live. Assert if no location
  255. // found.
  256. uint32_t offset = loc;
  257. auto curr_type = var_type;
  258. AnalyzeAccessChainLoc(ref, &curr_type, &offset, &no_loc, is_patch);
  259. assert(!no_loc && "missing input variable location");
  260. MarkLocsLive(offset, GetLocSize(curr_type));
  261. }
  262. void LivenessManager::ComputeLiveness() {
  263. InitializeAnalysis();
  264. analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
  265. analysis::TypeManager* type_mgr = context()->get_type_mgr();
  266. // Process all input variables
  267. for (auto& var : context()->types_values()) {
  268. if (var.opcode() != spv::Op::OpVariable) {
  269. continue;
  270. }
  271. analysis::Type* var_type = type_mgr->GetType(var.type_id());
  272. analysis::Pointer* ptr_type = var_type->AsPointer();
  273. if (ptr_type->storage_class() != spv::StorageClass::Input) {
  274. continue;
  275. }
  276. // If var is builtin, mark live if analyzed and continue to next variable
  277. auto var_id = var.result_id();
  278. if (AnalyzeBuiltIn(var_id)) continue;
  279. // If interface block with builtin members, mark live if analyzed and
  280. // continue to next variable. Input interface blocks will only appear
  281. // in tesc, tese and geom shaders. Will need to strip off one level of
  282. // arrayness to get to block type.
  283. auto pte_type = ptr_type->pointee_type();
  284. auto arr_type = pte_type->AsArray();
  285. if (arr_type) {
  286. auto elt_type = arr_type->element_type();
  287. auto str_type = elt_type->AsStruct();
  288. if (str_type) {
  289. auto str_type_id = type_mgr->GetId(str_type);
  290. if (AnalyzeBuiltIn(str_type_id)) continue;
  291. }
  292. }
  293. // Mark all used locations of var live
  294. def_use_mgr->ForEachUser(var_id, [this, &var](Instruction* user) {
  295. auto op = user->opcode();
  296. if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
  297. op == spv::Op::OpDecorate) {
  298. return;
  299. }
  300. MarkRefLive(user, &var);
  301. });
  302. }
  303. }
  304. void LivenessManager::GetLiveness(std::unordered_set<uint32_t>* live_locs,
  305. std::unordered_set<uint32_t>* live_builtins) {
  306. if (!computed_) {
  307. ComputeLiveness();
  308. computed_ = true;
  309. }
  310. *live_locs = live_locs_;
  311. *live_builtins = live_builtins_;
  312. }
  313. } // namespace analysis
  314. } // namespace opt
  315. } // namespace spvtools