control_flow.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <list>
  5. #include <map>
  6. #include <stack>
  7. #include <unordered_map>
  8. #include <unordered_set>
  9. #include <vector>
  10. #include "common/assert.h"
  11. #include "common/common_types.h"
  12. #include "video_core/shader/control_flow.h"
  13. #include "video_core/shader/shader_ir.h"
  14. namespace VideoCommon::Shader {
  15. namespace {
  16. using Tegra::Shader::Instruction;
  17. using Tegra::Shader::OpCode;
  18. constexpr s32 unassigned_branch = -2;
  19. struct Query {
  20. u32 address{};
  21. std::stack<u32> ssy_stack{};
  22. std::stack<u32> pbk_stack{};
  23. };
  24. struct BlockStack {
  25. BlockStack() = default;
  26. BlockStack(const BlockStack& b) = default;
  27. BlockStack(const Query& q) : ssy_stack{q.ssy_stack}, pbk_stack{q.pbk_stack} {}
  28. std::stack<u32> ssy_stack{};
  29. std::stack<u32> pbk_stack{};
  30. };
  31. struct BlockBranchInfo {
  32. Condition condition{};
  33. s32 address{exit_branch};
  34. bool kill{};
  35. bool is_sync{};
  36. bool is_brk{};
  37. bool ignore{};
  38. };
  39. struct BlockInfo {
  40. u32 start{};
  41. u32 end{};
  42. bool visited{};
  43. BlockBranchInfo branch{};
  44. bool IsInside(const u32 address) const {
  45. return start <= address && address <= end;
  46. }
  47. };
  48. struct CFGRebuildState {
  49. explicit CFGRebuildState(const ProgramCode& program_code, const std::size_t program_size,
  50. const u32 start)
  51. : start{start}, program_code{program_code}, program_size{program_size} {}
  52. u32 start{};
  53. std::vector<BlockInfo> block_info{};
  54. std::list<u32> inspect_queries{};
  55. std::list<Query> queries{};
  56. std::unordered_map<u32, u32> registered{};
  57. std::unordered_set<u32> labels{};
  58. std::map<u32, u32> ssy_labels{};
  59. std::map<u32, u32> pbk_labels{};
  60. std::unordered_map<u32, BlockStack> stacks{};
  61. const ProgramCode& program_code;
  62. const std::size_t program_size;
  63. };
  64. enum class BlockCollision : u32 { None, Found, Inside };
  65. std::pair<BlockCollision, u32> TryGetBlock(CFGRebuildState& state, u32 address) {
  66. const auto& blocks = state.block_info;
  67. for (u32 index = 0; index < blocks.size(); index++) {
  68. if (blocks[index].start == address) {
  69. return {BlockCollision::Found, index};
  70. }
  71. if (blocks[index].IsInside(address)) {
  72. return {BlockCollision::Inside, index};
  73. }
  74. }
  75. return {BlockCollision::None, -1};
  76. }
  77. struct ParseInfo {
  78. BlockBranchInfo branch_info{};
  79. u32 end_address{};
  80. };
  81. BlockInfo& CreateBlockInfo(CFGRebuildState& state, u32 start, u32 end) {
  82. auto& it = state.block_info.emplace_back();
  83. it.start = start;
  84. it.end = end;
  85. const u32 index = static_cast<u32>(state.block_info.size() - 1);
  86. state.registered.insert({start, index});
  87. return it;
  88. }
  89. Pred GetPredicate(u32 index, bool negated) {
  90. return static_cast<Pred>(index + (negated ? 8 : 0));
  91. }
  92. /**
  93. * Returns whether the instruction at the specified offset is a 'sched' instruction.
  94. * Sched instructions always appear before a sequence of 3 instructions.
  95. */
  96. constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
  97. constexpr u32 SchedPeriod = 4;
  98. u32 absolute_offset = offset - main_offset;
  99. return (absolute_offset % SchedPeriod) == 0;
  100. }
  101. enum class ParseResult : u32 {
  102. ControlCaught,
  103. BlockEnd,
  104. AbnormalFlow,
  105. };
  106. std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address) {
  107. u32 offset = static_cast<u32>(address);
  108. const u32 end_address = static_cast<u32>(state.program_size / sizeof(Instruction));
  109. ParseInfo parse_info{};
  110. const auto insert_label = [](CFGRebuildState& state, u32 address) {
  111. const auto pair = state.labels.emplace(address);
  112. if (pair.second) {
  113. state.inspect_queries.push_back(address);
  114. }
  115. };
  116. while (true) {
  117. if (offset >= end_address) {
  118. // ASSERT_OR_EXECUTE can't be used, as it ignores the break
  119. ASSERT_MSG(false, "Shader passed the current limit!");
  120. parse_info.branch_info.address = exit_branch;
  121. parse_info.branch_info.ignore = false;
  122. break;
  123. }
  124. if (state.registered.count(offset) != 0) {
  125. parse_info.branch_info.address = offset;
  126. parse_info.branch_info.ignore = true;
  127. break;
  128. }
  129. if (IsSchedInstruction(offset, state.start)) {
  130. offset++;
  131. continue;
  132. }
  133. const Instruction instr = {state.program_code[offset]};
  134. const auto opcode = OpCode::Decode(instr);
  135. if (!opcode || opcode->get().GetType() != OpCode::Type::Flow) {
  136. offset++;
  137. continue;
  138. }
  139. switch (opcode->get().GetId()) {
  140. case OpCode::Id::EXIT: {
  141. const auto pred_index = static_cast<u32>(instr.pred.pred_index);
  142. parse_info.branch_info.condition.predicate =
  143. GetPredicate(pred_index, instr.negate_pred != 0);
  144. if (parse_info.branch_info.condition.predicate == Pred::NeverExecute) {
  145. offset++;
  146. continue;
  147. }
  148. const ConditionCode cc = instr.flow_condition_code;
  149. parse_info.branch_info.condition.cc = cc;
  150. if (cc == ConditionCode::F) {
  151. offset++;
  152. continue;
  153. }
  154. parse_info.branch_info.address = exit_branch;
  155. parse_info.branch_info.kill = false;
  156. parse_info.branch_info.is_sync = false;
  157. parse_info.branch_info.is_brk = false;
  158. parse_info.branch_info.ignore = false;
  159. parse_info.end_address = offset;
  160. return {ParseResult::ControlCaught, parse_info};
  161. }
  162. case OpCode::Id::BRA: {
  163. if (instr.bra.constant_buffer != 0) {
  164. return {ParseResult::AbnormalFlow, parse_info};
  165. }
  166. const auto pred_index = static_cast<u32>(instr.pred.pred_index);
  167. parse_info.branch_info.condition.predicate =
  168. GetPredicate(pred_index, instr.negate_pred != 0);
  169. if (parse_info.branch_info.condition.predicate == Pred::NeverExecute) {
  170. offset++;
  171. continue;
  172. }
  173. const ConditionCode cc = instr.flow_condition_code;
  174. parse_info.branch_info.condition.cc = cc;
  175. if (cc == ConditionCode::F) {
  176. offset++;
  177. continue;
  178. }
  179. const u32 branch_offset = offset + instr.bra.GetBranchTarget();
  180. if (branch_offset == 0) {
  181. parse_info.branch_info.address = exit_branch;
  182. } else {
  183. parse_info.branch_info.address = branch_offset;
  184. }
  185. insert_label(state, branch_offset);
  186. parse_info.branch_info.kill = false;
  187. parse_info.branch_info.is_sync = false;
  188. parse_info.branch_info.is_brk = false;
  189. parse_info.branch_info.ignore = false;
  190. parse_info.end_address = offset;
  191. return {ParseResult::ControlCaught, parse_info};
  192. }
  193. case OpCode::Id::SYNC: {
  194. const auto pred_index = static_cast<u32>(instr.pred.pred_index);
  195. parse_info.branch_info.condition.predicate =
  196. GetPredicate(pred_index, instr.negate_pred != 0);
  197. if (parse_info.branch_info.condition.predicate == Pred::NeverExecute) {
  198. offset++;
  199. continue;
  200. }
  201. const ConditionCode cc = instr.flow_condition_code;
  202. parse_info.branch_info.condition.cc = cc;
  203. if (cc == ConditionCode::F) {
  204. offset++;
  205. continue;
  206. }
  207. parse_info.branch_info.address = unassigned_branch;
  208. parse_info.branch_info.kill = false;
  209. parse_info.branch_info.is_sync = true;
  210. parse_info.branch_info.is_brk = false;
  211. parse_info.branch_info.ignore = false;
  212. parse_info.end_address = offset;
  213. return {ParseResult::ControlCaught, parse_info};
  214. }
  215. case OpCode::Id::BRK: {
  216. const auto pred_index = static_cast<u32>(instr.pred.pred_index);
  217. parse_info.branch_info.condition.predicate =
  218. GetPredicate(pred_index, instr.negate_pred != 0);
  219. if (parse_info.branch_info.condition.predicate == Pred::NeverExecute) {
  220. offset++;
  221. continue;
  222. }
  223. const ConditionCode cc = instr.flow_condition_code;
  224. parse_info.branch_info.condition.cc = cc;
  225. if (cc == ConditionCode::F) {
  226. offset++;
  227. continue;
  228. }
  229. parse_info.branch_info.address = unassigned_branch;
  230. parse_info.branch_info.kill = false;
  231. parse_info.branch_info.is_sync = false;
  232. parse_info.branch_info.is_brk = true;
  233. parse_info.branch_info.ignore = false;
  234. parse_info.end_address = offset;
  235. return {ParseResult::ControlCaught, parse_info};
  236. }
  237. case OpCode::Id::KIL: {
  238. const auto pred_index = static_cast<u32>(instr.pred.pred_index);
  239. parse_info.branch_info.condition.predicate =
  240. GetPredicate(pred_index, instr.negate_pred != 0);
  241. if (parse_info.branch_info.condition.predicate == Pred::NeverExecute) {
  242. offset++;
  243. continue;
  244. }
  245. const ConditionCode cc = instr.flow_condition_code;
  246. parse_info.branch_info.condition.cc = cc;
  247. if (cc == ConditionCode::F) {
  248. offset++;
  249. continue;
  250. }
  251. parse_info.branch_info.address = exit_branch;
  252. parse_info.branch_info.kill = true;
  253. parse_info.branch_info.is_sync = false;
  254. parse_info.branch_info.is_brk = false;
  255. parse_info.branch_info.ignore = false;
  256. parse_info.end_address = offset;
  257. return {ParseResult::ControlCaught, parse_info};
  258. }
  259. case OpCode::Id::SSY: {
  260. const u32 target = offset + instr.bra.GetBranchTarget();
  261. insert_label(state, target);
  262. state.ssy_labels.emplace(offset, target);
  263. break;
  264. }
  265. case OpCode::Id::PBK: {
  266. const u32 target = offset + instr.bra.GetBranchTarget();
  267. insert_label(state, target);
  268. state.pbk_labels.emplace(offset, target);
  269. break;
  270. }
  271. case OpCode::Id::BRX: {
  272. return {ParseResult::AbnormalFlow, parse_info};
  273. }
  274. default:
  275. break;
  276. }
  277. offset++;
  278. }
  279. parse_info.branch_info.kill = false;
  280. parse_info.branch_info.is_sync = false;
  281. parse_info.branch_info.is_brk = false;
  282. parse_info.end_address = offset - 1;
  283. return {ParseResult::BlockEnd, parse_info};
  284. }
  285. bool TryInspectAddress(CFGRebuildState& state) {
  286. if (state.inspect_queries.empty()) {
  287. return false;
  288. }
  289. const u32 address = state.inspect_queries.front();
  290. state.inspect_queries.pop_front();
  291. const auto [result, block_index] = TryGetBlock(state, address);
  292. switch (result) {
  293. case BlockCollision::Found: {
  294. return true;
  295. }
  296. case BlockCollision::Inside: {
  297. // This case is the tricky one:
  298. // We need to Split the block in 2 sepparate blocks
  299. const u32 end = state.block_info[block_index].end;
  300. BlockInfo& new_block = CreateBlockInfo(state, address, end);
  301. BlockInfo& current_block = state.block_info[block_index];
  302. current_block.end = address - 1;
  303. new_block.branch = current_block.branch;
  304. BlockBranchInfo forward_branch{};
  305. forward_branch.address = address;
  306. forward_branch.ignore = true;
  307. current_block.branch = forward_branch;
  308. return true;
  309. }
  310. default:
  311. break;
  312. }
  313. const auto [parse_result, parse_info] = ParseCode(state, address);
  314. if (parse_result == ParseResult::AbnormalFlow) {
  315. // if it's AbnormalFlow, we end it as false, ending the CFG reconstruction
  316. return false;
  317. }
  318. BlockInfo& block_info = CreateBlockInfo(state, address, parse_info.end_address);
  319. block_info.branch = parse_info.branch_info;
  320. if (parse_info.branch_info.condition.IsUnconditional()) {
  321. return true;
  322. }
  323. const u32 fallthrough_address = parse_info.end_address + 1;
  324. state.inspect_queries.push_front(fallthrough_address);
  325. return true;
  326. }
  327. bool TryQuery(CFGRebuildState& state) {
  328. const auto gather_labels = [](std::stack<u32>& cc, std::map<u32, u32>& labels,
  329. BlockInfo& block) {
  330. auto gather_start = labels.lower_bound(block.start);
  331. const auto gather_end = labels.upper_bound(block.end);
  332. while (gather_start != gather_end) {
  333. cc.push(gather_start->second);
  334. ++gather_start;
  335. }
  336. };
  337. if (state.queries.empty()) {
  338. return false;
  339. }
  340. Query& q = state.queries.front();
  341. const u32 block_index = state.registered[q.address];
  342. BlockInfo& block = state.block_info[block_index];
  343. // If the block is visited, check if the stacks match, else gather the ssy/pbk
  344. // labels into the current stack and look if the branch at the end of the block
  345. // consumes a label. Schedule new queries accordingly
  346. if (block.visited) {
  347. BlockStack& stack = state.stacks[q.address];
  348. const bool all_okay = (stack.ssy_stack.empty() || q.ssy_stack == stack.ssy_stack) &&
  349. (stack.pbk_stack.empty() || q.pbk_stack == stack.pbk_stack);
  350. state.queries.pop_front();
  351. return all_okay;
  352. }
  353. block.visited = true;
  354. state.stacks.insert_or_assign(q.address, BlockStack{q});
  355. Query q2(q);
  356. state.queries.pop_front();
  357. gather_labels(q2.ssy_stack, state.ssy_labels, block);
  358. gather_labels(q2.pbk_stack, state.pbk_labels, block);
  359. if (!block.branch.condition.IsUnconditional()) {
  360. q2.address = block.end + 1;
  361. state.queries.push_back(q2);
  362. }
  363. Query conditional_query{q2};
  364. if (block.branch.is_sync) {
  365. if (block.branch.address == unassigned_branch) {
  366. block.branch.address = conditional_query.ssy_stack.top();
  367. }
  368. conditional_query.ssy_stack.pop();
  369. }
  370. if (block.branch.is_brk) {
  371. if (block.branch.address == unassigned_branch) {
  372. block.branch.address = conditional_query.pbk_stack.top();
  373. }
  374. conditional_query.pbk_stack.pop();
  375. }
  376. conditional_query.address = block.branch.address;
  377. state.queries.push_back(std::move(conditional_query));
  378. return true;
  379. }
  380. } // Anonymous namespace
  381. std::optional<ShaderCharacteristics> ScanFlow(const ProgramCode& program_code,
  382. std::size_t program_size, u32 start_address) {
  383. CFGRebuildState state{program_code, program_size, start_address};
  384. // Inspect Code and generate blocks
  385. state.labels.clear();
  386. state.labels.emplace(start_address);
  387. state.inspect_queries.push_back(state.start);
  388. while (!state.inspect_queries.empty()) {
  389. if (!TryInspectAddress(state)) {
  390. return {};
  391. }
  392. }
  393. // Decompile Stacks
  394. state.queries.push_back(Query{state.start, {}, {}});
  395. bool decompiled = true;
  396. while (!state.queries.empty()) {
  397. if (!TryQuery(state)) {
  398. decompiled = false;
  399. break;
  400. }
  401. }
  402. // Sort and organize results
  403. std::sort(state.block_info.begin(), state.block_info.end(),
  404. [](const BlockInfo& a, const BlockInfo& b) { return a.start < b.start; });
  405. ShaderCharacteristics result_out{};
  406. result_out.decompilable = decompiled;
  407. result_out.start = start_address;
  408. result_out.end = start_address;
  409. for (const auto& block : state.block_info) {
  410. ShaderBlock new_block{};
  411. new_block.start = block.start;
  412. new_block.end = block.end;
  413. new_block.ignore_branch = block.branch.ignore;
  414. if (!new_block.ignore_branch) {
  415. new_block.branch.cond = block.branch.condition;
  416. new_block.branch.kills = block.branch.kill;
  417. new_block.branch.address = block.branch.address;
  418. }
  419. result_out.end = std::max(result_out.end, block.end);
  420. result_out.blocks.push_back(new_block);
  421. }
  422. if (result_out.decompilable) {
  423. result_out.labels = std::move(state.labels);
  424. return {std::move(result_out)};
  425. }
  426. // If it's not decompilable, merge the unlabelled blocks together
  427. auto back = result_out.blocks.begin();
  428. auto next = std::next(back);
  429. while (next != result_out.blocks.end()) {
  430. if (state.labels.count(next->start) == 0 && next->start == back->end + 1) {
  431. back->end = next->end;
  432. next = result_out.blocks.erase(next);
  433. continue;
  434. }
  435. back = next;
  436. ++next;
  437. }
  438. return {std::move(result_out)};
  439. }
  440. } // namespace VideoCommon::Shader