liveness.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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. uint32_t LivenessManager::GetComponentType(uint32_t index,
  117. uint32_t agg_type_id) const {
  118. analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
  119. Instruction* agg_type_inst = def_use_mgr->GetDef(agg_type_id);
  120. const uint32_t kArrayElementInIdx = 0;
  121. switch (agg_type_inst->opcode()) {
  122. case spv::Op::OpTypeArray:
  123. case spv::Op::OpTypeMatrix:
  124. case spv::Op::OpTypeVector:
  125. return agg_type_inst->GetSingleWordInOperand(kArrayElementInIdx);
  126. case spv::Op::OpTypeStruct:
  127. return agg_type_inst->GetSingleWordInOperand(index);
  128. default:
  129. assert(false && "unexpected aggregate type");
  130. return 0;
  131. }
  132. }
  133. uint32_t LivenessManager::GetLocOffset(uint32_t index,
  134. uint32_t agg_type_id) const {
  135. analysis::TypeManager* type_mgr = context()->get_type_mgr();
  136. const analysis::Type* agg_type = type_mgr->GetType(agg_type_id);
  137. auto arr_type = agg_type->AsArray();
  138. if (arr_type) return index * GetLocSize(arr_type->element_type());
  139. auto struct_type = agg_type->AsStruct();
  140. if (struct_type) {
  141. uint32_t offset = 0u;
  142. uint32_t cnt = 0u;
  143. for (auto& el_type : struct_type->element_types()) {
  144. if (cnt == index) break;
  145. offset += GetLocSize(el_type);
  146. ++cnt;
  147. }
  148. return offset;
  149. }
  150. auto mat_type = agg_type->AsMatrix();
  151. if (mat_type) return index * GetLocSize(mat_type->element_type());
  152. auto vec_type = agg_type->AsVector();
  153. assert(vec_type && "unexpected non-aggregate type");
  154. auto comp_type = vec_type->element_type();
  155. auto flt_type = comp_type->AsFloat();
  156. if (flt_type && flt_type->width() == 64u && index >= 2u) return 1;
  157. return 0;
  158. }
  159. uint32_t LivenessManager::AnalyzeAccessChainLoc(const Instruction* ac,
  160. uint32_t curr_type_id,
  161. uint32_t* offset, bool* no_loc,
  162. bool is_patch, bool input) {
  163. analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
  164. analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
  165. // For tesc, tese and geom input variables, and tesc output variables,
  166. // first array index does not contribute to offset.
  167. auto stage = context()->GetStage();
  168. bool skip_first_index = false;
  169. if ((input && (stage == spv::ExecutionModel::TessellationControl ||
  170. stage == spv::ExecutionModel::TessellationEvaluation ||
  171. stage == spv::ExecutionModel::Geometry)) ||
  172. (!input && stage == spv::ExecutionModel::TessellationControl))
  173. skip_first_index = !is_patch;
  174. uint32_t ocnt = 0;
  175. ac->WhileEachInOperand([this, &ocnt, def_use_mgr, deco_mgr, &curr_type_id,
  176. offset, no_loc,
  177. skip_first_index](const uint32_t* opnd) {
  178. if (ocnt >= 1) {
  179. // Skip first index's contribution to offset if indicated
  180. Instruction* curr_type_inst = def_use_mgr->GetDef(curr_type_id);
  181. if (ocnt == 1 && skip_first_index) {
  182. assert(curr_type_inst->opcode() == spv::Op::OpTypeArray &&
  183. "unexpected wrapper type");
  184. const uint32_t kArrayElementTypeInIdx = 0;
  185. curr_type_id =
  186. curr_type_inst->GetSingleWordInOperand(kArrayElementTypeInIdx);
  187. ocnt++;
  188. return true;
  189. }
  190. // If any non-constant index, mark the entire current object and return.
  191. auto idx_inst = def_use_mgr->GetDef(*opnd);
  192. if (idx_inst->opcode() != spv::Op::OpConstant) return false;
  193. // If current type is struct, look for location decoration on member and
  194. // reset offset if found.
  195. auto index = idx_inst->GetSingleWordInOperand(0);
  196. if (curr_type_inst->opcode() == spv::Op::OpTypeStruct) {
  197. uint32_t loc = 0;
  198. bool no_mem_loc = deco_mgr->WhileEachDecoration(
  199. curr_type_id, uint32_t(spv::Decoration::Location),
  200. [&loc, index, no_loc](const Instruction& deco) {
  201. assert(deco.opcode() == spv::Op::OpMemberDecorate &&
  202. "unexpected decoration");
  203. if (deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx) ==
  204. index) {
  205. loc =
  206. deco.GetSingleWordInOperand(kOpDecorateMemberLocationInIdx);
  207. *no_loc = false;
  208. return false;
  209. }
  210. return true;
  211. });
  212. if (!no_mem_loc) {
  213. *offset = loc;
  214. curr_type_id = curr_type_inst->GetSingleWordInOperand(index);
  215. ocnt++;
  216. return true;
  217. }
  218. }
  219. // Update offset and current type based on constant index.
  220. *offset += GetLocOffset(index, curr_type_id);
  221. curr_type_id = GetComponentType(index, curr_type_id);
  222. }
  223. ocnt++;
  224. return true;
  225. });
  226. return curr_type_id;
  227. }
  228. void LivenessManager::MarkRefLive(const Instruction* ref, Instruction* var) {
  229. analysis::TypeManager* type_mgr = context()->get_type_mgr();
  230. analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
  231. // Find variable location if present.
  232. uint32_t loc = 0;
  233. auto var_id = var->result_id();
  234. bool no_loc = deco_mgr->WhileEachDecoration(
  235. var_id, uint32_t(spv::Decoration::Location),
  236. [&loc](const Instruction& deco) {
  237. assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
  238. loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx);
  239. return false;
  240. });
  241. // Find patch decoration if present
  242. bool is_patch = !deco_mgr->WhileEachDecoration(
  243. var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) {
  244. if (deco.opcode() != spv::Op::OpDecorate)
  245. assert(false && "unexpected decoration");
  246. return false;
  247. });
  248. // If use is a load, mark all locations of var
  249. auto ptr_type = type_mgr->GetType(var->type_id())->AsPointer();
  250. assert(ptr_type && "unexpected var type");
  251. auto var_type = ptr_type->pointee_type();
  252. if (ref->opcode() == spv::Op::OpLoad) {
  253. assert(!no_loc && "missing input variable location");
  254. MarkLocsLive(loc, GetLocSize(var_type));
  255. return;
  256. }
  257. // Mark just those locations indicated by access chain
  258. assert((ref->opcode() == spv::Op::OpAccessChain ||
  259. ref->opcode() == spv::Op::OpInBoundsAccessChain) &&
  260. "unexpected use of input variable");
  261. // Traverse access chain, compute location offset and type of reference
  262. // through constant indices and mark those locs live. Assert if no location
  263. // found.
  264. uint32_t offset = loc;
  265. Instruction* ptr_type_inst =
  266. context()->get_def_use_mgr()->GetDef(var->type_id());
  267. assert(ptr_type && "unexpected var type");
  268. const uint32_t kPointerTypePointeeIdx = 1;
  269. uint32_t var_type_id =
  270. ptr_type_inst->GetSingleWordInOperand(kPointerTypePointeeIdx);
  271. uint32_t curr_type_id =
  272. AnalyzeAccessChainLoc(ref, var_type_id, &offset, &no_loc, is_patch);
  273. auto curr_type = type_mgr->GetType(curr_type_id);
  274. assert(!no_loc && "missing input variable location");
  275. MarkLocsLive(offset, GetLocSize(curr_type));
  276. }
  277. void LivenessManager::ComputeLiveness() {
  278. InitializeAnalysis();
  279. analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
  280. // Process all input variables
  281. for (auto& var : context()->types_values()) {
  282. if (var.opcode() != spv::Op::OpVariable) {
  283. continue;
  284. }
  285. Instruction* var_type_inst = def_use_mgr->GetDef(var.type_id());
  286. assert(var_type_inst->opcode() == spv::Op::OpTypePointer &&
  287. "Expected a pointer type");
  288. const uint32_t kPointerTypeStorageClassInIdx = 0;
  289. spv::StorageClass sc = static_cast<spv::StorageClass>(
  290. var_type_inst->GetSingleWordInOperand(kPointerTypeStorageClassInIdx));
  291. if (sc != spv::StorageClass::Input) {
  292. continue;
  293. }
  294. // If var is builtin, mark live if analyzed and continue to next variable
  295. auto var_id = var.result_id();
  296. if (AnalyzeBuiltIn(var_id)) continue;
  297. // If interface block with builtin members, mark live if analyzed and
  298. // continue to next variable. Input interface blocks will only appear
  299. // in tesc, tese and geom shaders. Will need to strip off one level of
  300. // arrayness to get to block type.
  301. const uint32_t kPointerTypePointeeTypeInIdx = 1;
  302. uint32_t pte_type_id =
  303. var_type_inst->GetSingleWordInOperand(kPointerTypePointeeTypeInIdx);
  304. Instruction* pte_type_inst = def_use_mgr->GetDef(pte_type_id);
  305. if (pte_type_inst->opcode() == spv::Op::OpTypeArray) {
  306. uint32_t array_elt_type_id = pte_type_inst->GetSingleWordInOperand(0);
  307. Instruction* arr_elt_type = def_use_mgr->GetDef(array_elt_type_id);
  308. if (arr_elt_type->opcode() == spv::Op::OpTypeStruct) {
  309. if (AnalyzeBuiltIn(array_elt_type_id)) continue;
  310. }
  311. }
  312. // Mark all used locations of var live
  313. def_use_mgr->ForEachUser(var_id, [this, &var](Instruction* user) {
  314. auto op = user->opcode();
  315. if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
  316. op == spv::Op::OpDecorate || user->IsNonSemanticInstruction()) {
  317. return;
  318. }
  319. MarkRefLive(user, &var);
  320. });
  321. }
  322. }
  323. void LivenessManager::GetLiveness(std::unordered_set<uint32_t>* live_locs,
  324. std::unordered_set<uint32_t>* live_builtins) {
  325. if (!computed_) {
  326. ComputeLiveness();
  327. computed_ = true;
  328. }
  329. *live_locs = live_locs_;
  330. *live_builtins = live_builtins_;
  331. }
  332. } // namespace analysis
  333. } // namespace opt
  334. } // namespace spvtools