diff options
| author | 2019-10-05 21:52:20 +1000 | |
|---|---|---|
| committer | 2019-10-05 21:52:20 +1000 | |
| commit | 3728bbc22a9224ff75fff22487a47bcaaf6ac2be (patch) | |
| tree | 80809634787307002bf9e07b710a4aa968019f26 /src/video_core/shader/ast.cpp | |
| parent | Merge pull request #2917 from FernandoS27/fermi-deduction-2 (diff) | |
| parent | Shader_ir: Address feedback (diff) | |
| download | yuzu-3728bbc22a9224ff75fff22487a47bcaaf6ac2be.tar.gz yuzu-3728bbc22a9224ff75fff22487a47bcaaf6ac2be.tar.xz yuzu-3728bbc22a9224ff75fff22487a47bcaaf6ac2be.zip | |
Merge pull request #2888 from FernandoS27/decompiler2
Shader_IR: Implement a full control flow decompiler for the shader IR.
Diffstat (limited to 'src/video_core/shader/ast.cpp')
| -rw-r--r-- | src/video_core/shader/ast.cpp | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/src/video_core/shader/ast.cpp b/src/video_core/shader/ast.cpp new file mode 100644 index 000000000..2eb065c3d --- /dev/null +++ b/src/video_core/shader/ast.cpp | |||
| @@ -0,0 +1,766 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "video_core/shader/ast.h" | ||
| 10 | #include "video_core/shader/expr.h" | ||
| 11 | |||
| 12 | namespace VideoCommon::Shader { | ||
| 13 | |||
| 14 | ASTZipper::ASTZipper() = default; | ||
| 15 | |||
| 16 | void ASTZipper::Init(const ASTNode new_first, const ASTNode parent) { | ||
| 17 | ASSERT(new_first->manager == nullptr); | ||
| 18 | first = new_first; | ||
| 19 | last = new_first; | ||
| 20 | ASTNode current = first; | ||
| 21 | while (current) { | ||
| 22 | current->manager = this; | ||
| 23 | current->parent = parent; | ||
| 24 | last = current; | ||
| 25 | current = current->next; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | void ASTZipper::PushBack(const ASTNode new_node) { | ||
| 30 | ASSERT(new_node->manager == nullptr); | ||
| 31 | new_node->previous = last; | ||
| 32 | if (last) { | ||
| 33 | last->next = new_node; | ||
| 34 | } | ||
| 35 | new_node->next.reset(); | ||
| 36 | last = new_node; | ||
| 37 | if (!first) { | ||
| 38 | first = new_node; | ||
| 39 | } | ||
| 40 | new_node->manager = this; | ||
| 41 | } | ||
| 42 | |||
| 43 | void ASTZipper::PushFront(const ASTNode new_node) { | ||
| 44 | ASSERT(new_node->manager == nullptr); | ||
| 45 | new_node->previous.reset(); | ||
| 46 | new_node->next = first; | ||
| 47 | if (first) { | ||
| 48 | first->previous = new_node; | ||
| 49 | } | ||
| 50 | if (last == first) { | ||
| 51 | last = new_node; | ||
| 52 | } | ||
| 53 | first = new_node; | ||
| 54 | new_node->manager = this; | ||
| 55 | } | ||
| 56 | |||
| 57 | void ASTZipper::InsertAfter(const ASTNode new_node, const ASTNode at_node) { | ||
| 58 | ASSERT(new_node->manager == nullptr); | ||
| 59 | if (!at_node) { | ||
| 60 | PushFront(new_node); | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | const ASTNode next = at_node->next; | ||
| 64 | if (next) { | ||
| 65 | next->previous = new_node; | ||
| 66 | } | ||
| 67 | new_node->previous = at_node; | ||
| 68 | if (at_node == last) { | ||
| 69 | last = new_node; | ||
| 70 | } | ||
| 71 | new_node->next = next; | ||
| 72 | at_node->next = new_node; | ||
| 73 | new_node->manager = this; | ||
| 74 | } | ||
| 75 | |||
| 76 | void ASTZipper::InsertBefore(const ASTNode new_node, const ASTNode at_node) { | ||
| 77 | ASSERT(new_node->manager == nullptr); | ||
| 78 | if (!at_node) { | ||
| 79 | PushBack(new_node); | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | const ASTNode previous = at_node->previous; | ||
| 83 | if (previous) { | ||
| 84 | previous->next = new_node; | ||
| 85 | } | ||
| 86 | new_node->next = at_node; | ||
| 87 | if (at_node == first) { | ||
| 88 | first = new_node; | ||
| 89 | } | ||
| 90 | new_node->previous = previous; | ||
| 91 | at_node->previous = new_node; | ||
| 92 | new_node->manager = this; | ||
| 93 | } | ||
| 94 | |||
| 95 | void ASTZipper::DetachTail(const ASTNode node) { | ||
| 96 | ASSERT(node->manager == this); | ||
| 97 | if (node == first) { | ||
| 98 | first.reset(); | ||
| 99 | last.reset(); | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | |||
| 103 | last = node->previous; | ||
| 104 | last->next.reset(); | ||
| 105 | node->previous.reset(); | ||
| 106 | ASTNode current = node; | ||
| 107 | while (current) { | ||
| 108 | current->manager = nullptr; | ||
| 109 | current->parent.reset(); | ||
| 110 | current = current->next; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | void ASTZipper::DetachSegment(const ASTNode start, const ASTNode end) { | ||
| 115 | ASSERT(start->manager == this && end->manager == this); | ||
| 116 | if (start == end) { | ||
| 117 | DetachSingle(start); | ||
| 118 | return; | ||
| 119 | } | ||
| 120 | const ASTNode prev = start->previous; | ||
| 121 | const ASTNode post = end->next; | ||
| 122 | if (!prev) { | ||
| 123 | first = post; | ||
| 124 | } else { | ||
| 125 | prev->next = post; | ||
| 126 | } | ||
| 127 | if (!post) { | ||
| 128 | last = prev; | ||
| 129 | } else { | ||
| 130 | post->previous = prev; | ||
| 131 | } | ||
| 132 | start->previous.reset(); | ||
| 133 | end->next.reset(); | ||
| 134 | ASTNode current = start; | ||
| 135 | bool found = false; | ||
| 136 | while (current) { | ||
| 137 | current->manager = nullptr; | ||
| 138 | current->parent.reset(); | ||
| 139 | found |= current == end; | ||
| 140 | current = current->next; | ||
| 141 | } | ||
| 142 | ASSERT(found); | ||
| 143 | } | ||
| 144 | |||
| 145 | void ASTZipper::DetachSingle(const ASTNode node) { | ||
| 146 | ASSERT(node->manager == this); | ||
| 147 | const ASTNode prev = node->previous; | ||
| 148 | const ASTNode post = node->next; | ||
| 149 | node->previous.reset(); | ||
| 150 | node->next.reset(); | ||
| 151 | if (!prev) { | ||
| 152 | first = post; | ||
| 153 | } else { | ||
| 154 | prev->next = post; | ||
| 155 | } | ||
| 156 | if (!post) { | ||
| 157 | last = prev; | ||
| 158 | } else { | ||
| 159 | post->previous = prev; | ||
| 160 | } | ||
| 161 | |||
| 162 | node->manager = nullptr; | ||
| 163 | node->parent.reset(); | ||
| 164 | } | ||
| 165 | |||
| 166 | void ASTZipper::Remove(const ASTNode node) { | ||
| 167 | ASSERT(node->manager == this); | ||
| 168 | const ASTNode next = node->next; | ||
| 169 | const ASTNode previous = node->previous; | ||
| 170 | if (previous) { | ||
| 171 | previous->next = next; | ||
| 172 | } | ||
| 173 | if (next) { | ||
| 174 | next->previous = previous; | ||
| 175 | } | ||
| 176 | node->parent.reset(); | ||
| 177 | node->manager = nullptr; | ||
| 178 | if (node == last) { | ||
| 179 | last = previous; | ||
| 180 | } | ||
| 181 | if (node == first) { | ||
| 182 | first = next; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | class ExprPrinter final { | ||
| 187 | public: | ||
| 188 | ExprPrinter() = default; | ||
| 189 | |||
| 190 | void operator()(ExprAnd const& expr) { | ||
| 191 | inner += "( "; | ||
| 192 | std::visit(*this, *expr.operand1); | ||
| 193 | inner += " && "; | ||
| 194 | std::visit(*this, *expr.operand2); | ||
| 195 | inner += ')'; | ||
| 196 | } | ||
| 197 | |||
| 198 | void operator()(ExprOr const& expr) { | ||
| 199 | inner += "( "; | ||
| 200 | std::visit(*this, *expr.operand1); | ||
| 201 | inner += " || "; | ||
| 202 | std::visit(*this, *expr.operand2); | ||
| 203 | inner += ')'; | ||
| 204 | } | ||
| 205 | |||
| 206 | void operator()(ExprNot const& expr) { | ||
| 207 | inner += "!"; | ||
| 208 | std::visit(*this, *expr.operand1); | ||
| 209 | } | ||
| 210 | |||
| 211 | void operator()(ExprPredicate const& expr) { | ||
| 212 | inner += "P" + std::to_string(expr.predicate); | ||
| 213 | } | ||
| 214 | |||
| 215 | void operator()(ExprCondCode const& expr) { | ||
| 216 | u32 cc = static_cast<u32>(expr.cc); | ||
| 217 | inner += "CC" + std::to_string(cc); | ||
| 218 | } | ||
| 219 | |||
| 220 | void operator()(ExprVar const& expr) { | ||
| 221 | inner += "V" + std::to_string(expr.var_index); | ||
| 222 | } | ||
| 223 | |||
| 224 | void operator()(ExprBoolean const& expr) { | ||
| 225 | inner += expr.value ? "true" : "false"; | ||
| 226 | } | ||
| 227 | |||
| 228 | std::string& GetResult() { | ||
| 229 | return inner; | ||
| 230 | } | ||
| 231 | |||
| 232 | std::string inner{}; | ||
| 233 | }; | ||
| 234 | |||
| 235 | class ASTPrinter { | ||
| 236 | public: | ||
| 237 | ASTPrinter() = default; | ||
| 238 | |||
| 239 | void operator()(ASTProgram& ast) { | ||
| 240 | scope++; | ||
| 241 | inner += "program {\n"; | ||
| 242 | ASTNode current = ast.nodes.GetFirst(); | ||
| 243 | while (current) { | ||
| 244 | Visit(current); | ||
| 245 | current = current->GetNext(); | ||
| 246 | } | ||
| 247 | inner += "}\n"; | ||
| 248 | scope--; | ||
| 249 | } | ||
| 250 | |||
| 251 | void operator()(ASTIfThen& ast) { | ||
| 252 | ExprPrinter expr_parser{}; | ||
| 253 | std::visit(expr_parser, *ast.condition); | ||
| 254 | inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n"; | ||
| 255 | scope++; | ||
| 256 | ASTNode current = ast.nodes.GetFirst(); | ||
| 257 | while (current) { | ||
| 258 | Visit(current); | ||
| 259 | current = current->GetNext(); | ||
| 260 | } | ||
| 261 | scope--; | ||
| 262 | inner += Ident() + "}\n"; | ||
| 263 | } | ||
| 264 | |||
| 265 | void operator()(ASTIfElse& ast) { | ||
| 266 | inner += Ident() + "else {\n"; | ||
| 267 | scope++; | ||
| 268 | ASTNode current = ast.nodes.GetFirst(); | ||
| 269 | while (current) { | ||
| 270 | Visit(current); | ||
| 271 | current = current->GetNext(); | ||
| 272 | } | ||
| 273 | scope--; | ||
| 274 | inner += Ident() + "}\n"; | ||
| 275 | } | ||
| 276 | |||
| 277 | void operator()(ASTBlockEncoded& ast) { | ||
| 278 | inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) + | ||
| 279 | ");\n"; | ||
| 280 | } | ||
| 281 | |||
| 282 | void operator()(ASTBlockDecoded& ast) { | ||
| 283 | inner += Ident() + "Block;\n"; | ||
| 284 | } | ||
| 285 | |||
| 286 | void operator()(ASTVarSet& ast) { | ||
| 287 | ExprPrinter expr_parser{}; | ||
| 288 | std::visit(expr_parser, *ast.condition); | ||
| 289 | inner += | ||
| 290 | Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n"; | ||
| 291 | } | ||
| 292 | |||
| 293 | void operator()(ASTLabel& ast) { | ||
| 294 | inner += "Label_" + std::to_string(ast.index) + ":\n"; | ||
| 295 | } | ||
| 296 | |||
| 297 | void operator()(ASTGoto& ast) { | ||
| 298 | ExprPrinter expr_parser{}; | ||
| 299 | std::visit(expr_parser, *ast.condition); | ||
| 300 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" + | ||
| 301 | std::to_string(ast.label) + ";\n"; | ||
| 302 | } | ||
| 303 | |||
| 304 | void operator()(ASTDoWhile& ast) { | ||
| 305 | ExprPrinter expr_parser{}; | ||
| 306 | std::visit(expr_parser, *ast.condition); | ||
| 307 | inner += Ident() + "do {\n"; | ||
| 308 | scope++; | ||
| 309 | ASTNode current = ast.nodes.GetFirst(); | ||
| 310 | while (current) { | ||
| 311 | Visit(current); | ||
| 312 | current = current->GetNext(); | ||
| 313 | } | ||
| 314 | scope--; | ||
| 315 | inner += Ident() + "} while (" + expr_parser.GetResult() + ");\n"; | ||
| 316 | } | ||
| 317 | |||
| 318 | void operator()(ASTReturn& ast) { | ||
| 319 | ExprPrinter expr_parser{}; | ||
| 320 | std::visit(expr_parser, *ast.condition); | ||
| 321 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> " + | ||
| 322 | (ast.kills ? "discard" : "exit") + ";\n"; | ||
| 323 | } | ||
| 324 | |||
| 325 | void operator()(ASTBreak& ast) { | ||
| 326 | ExprPrinter expr_parser{}; | ||
| 327 | std::visit(expr_parser, *ast.condition); | ||
| 328 | inner += Ident() + "(" + expr_parser.GetResult() + ") -> break;\n"; | ||
| 329 | } | ||
| 330 | |||
| 331 | std::string& Ident() { | ||
| 332 | if (memo_scope == scope) { | ||
| 333 | return tabs_memo; | ||
| 334 | } | ||
| 335 | tabs_memo = tabs.substr(0, scope * 2); | ||
| 336 | memo_scope = scope; | ||
| 337 | return tabs_memo; | ||
| 338 | } | ||
| 339 | |||
| 340 | void Visit(ASTNode& node) { | ||
| 341 | std::visit(*this, *node->GetInnerData()); | ||
| 342 | } | ||
| 343 | |||
| 344 | std::string& GetResult() { | ||
| 345 | return inner; | ||
| 346 | } | ||
| 347 | |||
| 348 | private: | ||
| 349 | std::string inner{}; | ||
| 350 | u32 scope{}; | ||
| 351 | |||
| 352 | std::string tabs_memo{}; | ||
| 353 | u32 memo_scope{}; | ||
| 354 | |||
| 355 | static std::string tabs; | ||
| 356 | }; | ||
| 357 | |||
| 358 | std::string ASTPrinter::tabs = " "; | ||
| 359 | |||
| 360 | std::string ASTManager::Print() { | ||
| 361 | ASTPrinter printer{}; | ||
| 362 | printer.Visit(main_node); | ||
| 363 | return printer.GetResult(); | ||
| 364 | } | ||
| 365 | |||
| 366 | ASTManager::ASTManager(bool full_decompile, bool disable_else_derivation) | ||
| 367 | : full_decompile{full_decompile}, disable_else_derivation{disable_else_derivation} {}; | ||
| 368 | |||
| 369 | ASTManager::~ASTManager() { | ||
| 370 | Clear(); | ||
| 371 | } | ||
| 372 | |||
| 373 | void ASTManager::Init() { | ||
| 374 | main_node = ASTBase::Make<ASTProgram>(ASTNode{}); | ||
| 375 | program = std::get_if<ASTProgram>(main_node->GetInnerData()); | ||
| 376 | false_condition = MakeExpr<ExprBoolean>(false); | ||
| 377 | } | ||
| 378 | |||
| 379 | ASTManager::ASTManager(ASTManager&& other) noexcept | ||
| 380 | : labels_map(std::move(other.labels_map)), labels_count{other.labels_count}, | ||
| 381 | gotos(std::move(other.gotos)), labels(std::move(other.labels)), variables{other.variables}, | ||
| 382 | program{other.program}, main_node{other.main_node}, false_condition{other.false_condition}, | ||
| 383 | disable_else_derivation{other.disable_else_derivation} { | ||
| 384 | other.main_node.reset(); | ||
| 385 | } | ||
| 386 | |||
| 387 | ASTManager& ASTManager::operator=(ASTManager&& other) noexcept { | ||
| 388 | full_decompile = other.full_decompile; | ||
| 389 | labels_map = std::move(other.labels_map); | ||
| 390 | labels_count = other.labels_count; | ||
| 391 | gotos = std::move(other.gotos); | ||
| 392 | labels = std::move(other.labels); | ||
| 393 | variables = other.variables; | ||
| 394 | program = other.program; | ||
| 395 | main_node = other.main_node; | ||
| 396 | false_condition = other.false_condition; | ||
| 397 | disable_else_derivation = other.disable_else_derivation; | ||
| 398 | |||
| 399 | other.main_node.reset(); | ||
| 400 | return *this; | ||
| 401 | } | ||
| 402 | |||
| 403 | void ASTManager::DeclareLabel(u32 address) { | ||
| 404 | const auto pair = labels_map.emplace(address, labels_count); | ||
| 405 | if (pair.second) { | ||
| 406 | labels_count++; | ||
| 407 | labels.resize(labels_count); | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | void ASTManager::InsertLabel(u32 address) { | ||
| 412 | const u32 index = labels_map[address]; | ||
| 413 | const ASTNode label = ASTBase::Make<ASTLabel>(main_node, index); | ||
| 414 | labels[index] = label; | ||
| 415 | program->nodes.PushBack(label); | ||
| 416 | } | ||
| 417 | |||
| 418 | void ASTManager::InsertGoto(Expr condition, u32 address) { | ||
| 419 | const u32 index = labels_map[address]; | ||
| 420 | const ASTNode goto_node = ASTBase::Make<ASTGoto>(main_node, condition, index); | ||
| 421 | gotos.push_back(goto_node); | ||
| 422 | program->nodes.PushBack(goto_node); | ||
| 423 | } | ||
| 424 | |||
| 425 | void ASTManager::InsertBlock(u32 start_address, u32 end_address) { | ||
| 426 | const ASTNode block = ASTBase::Make<ASTBlockEncoded>(main_node, start_address, end_address); | ||
| 427 | program->nodes.PushBack(block); | ||
| 428 | } | ||
| 429 | |||
| 430 | void ASTManager::InsertReturn(Expr condition, bool kills) { | ||
| 431 | const ASTNode node = ASTBase::Make<ASTReturn>(main_node, condition, kills); | ||
| 432 | program->nodes.PushBack(node); | ||
| 433 | } | ||
| 434 | |||
| 435 | // The decompile algorithm is based on | ||
| 436 | // "Taming control flow: A structured approach to eliminating goto statements" | ||
| 437 | // by AM Erosa, LJ Hendren 1994. In general, the idea is to get gotos to be | ||
| 438 | // on the same structured level as the label which they jump to. This is done, | ||
| 439 | // through outward/inward movements and lifting. Once they are at the same | ||
| 440 | // level, you can enclose them in an "if" structure or a "do-while" structure. | ||
| 441 | void ASTManager::Decompile() { | ||
| 442 | auto it = gotos.begin(); | ||
| 443 | while (it != gotos.end()) { | ||
| 444 | const ASTNode goto_node = *it; | ||
| 445 | const auto label_index = goto_node->GetGotoLabel(); | ||
| 446 | if (!label_index) { | ||
| 447 | return; | ||
| 448 | } | ||
| 449 | const ASTNode label = labels[*label_index]; | ||
| 450 | if (!full_decompile) { | ||
| 451 | // We only decompile backward jumps | ||
| 452 | if (!IsBackwardsJump(goto_node, label)) { | ||
| 453 | it++; | ||
| 454 | continue; | ||
| 455 | } | ||
| 456 | } | ||
| 457 | if (IndirectlyRelated(goto_node, label)) { | ||
| 458 | while (!DirectlyRelated(goto_node, label)) { | ||
| 459 | MoveOutward(goto_node); | ||
| 460 | } | ||
| 461 | } | ||
| 462 | if (DirectlyRelated(goto_node, label)) { | ||
| 463 | u32 goto_level = goto_node->GetLevel(); | ||
| 464 | const u32 label_level = label->GetLevel(); | ||
| 465 | while (label_level < goto_level) { | ||
| 466 | MoveOutward(goto_node); | ||
| 467 | goto_level--; | ||
| 468 | } | ||
| 469 | // TODO(Blinkhawk): Implement Lifting and Inward Movements | ||
| 470 | } | ||
| 471 | if (label->GetParent() == goto_node->GetParent()) { | ||
| 472 | bool is_loop = false; | ||
| 473 | ASTNode current = goto_node->GetPrevious(); | ||
| 474 | while (current) { | ||
| 475 | if (current == label) { | ||
| 476 | is_loop = true; | ||
| 477 | break; | ||
| 478 | } | ||
| 479 | current = current->GetPrevious(); | ||
| 480 | } | ||
| 481 | |||
| 482 | if (is_loop) { | ||
| 483 | EncloseDoWhile(goto_node, label); | ||
| 484 | } else { | ||
| 485 | EncloseIfThen(goto_node, label); | ||
| 486 | } | ||
| 487 | it = gotos.erase(it); | ||
| 488 | continue; | ||
| 489 | } | ||
| 490 | it++; | ||
| 491 | } | ||
| 492 | if (full_decompile) { | ||
| 493 | for (const ASTNode& label : labels) { | ||
| 494 | auto& manager = label->GetManager(); | ||
| 495 | manager.Remove(label); | ||
| 496 | } | ||
| 497 | labels.clear(); | ||
| 498 | } else { | ||
| 499 | auto it = labels.begin(); | ||
| 500 | while (it != labels.end()) { | ||
| 501 | bool can_remove = true; | ||
| 502 | ASTNode label = *it; | ||
| 503 | for (const ASTNode& goto_node : gotos) { | ||
| 504 | const auto label_index = goto_node->GetGotoLabel(); | ||
| 505 | if (!label_index) { | ||
| 506 | return; | ||
| 507 | } | ||
| 508 | ASTNode& glabel = labels[*label_index]; | ||
| 509 | if (glabel == label) { | ||
| 510 | can_remove = false; | ||
| 511 | break; | ||
| 512 | } | ||
| 513 | } | ||
| 514 | if (can_remove) { | ||
| 515 | label->MarkLabelUnused(); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | bool ASTManager::IsBackwardsJump(ASTNode goto_node, ASTNode label_node) const { | ||
| 522 | u32 goto_level = goto_node->GetLevel(); | ||
| 523 | u32 label_level = label_node->GetLevel(); | ||
| 524 | while (goto_level > label_level) { | ||
| 525 | goto_level--; | ||
| 526 | goto_node = goto_node->GetParent(); | ||
| 527 | } | ||
| 528 | while (label_level > goto_level) { | ||
| 529 | label_level--; | ||
| 530 | label_node = label_node->GetParent(); | ||
| 531 | } | ||
| 532 | while (goto_node->GetParent() != label_node->GetParent()) { | ||
| 533 | goto_node = goto_node->GetParent(); | ||
| 534 | label_node = label_node->GetParent(); | ||
| 535 | } | ||
| 536 | ASTNode current = goto_node->GetPrevious(); | ||
| 537 | while (current) { | ||
| 538 | if (current == label_node) { | ||
| 539 | return true; | ||
| 540 | } | ||
| 541 | current = current->GetPrevious(); | ||
| 542 | } | ||
| 543 | return false; | ||
| 544 | } | ||
| 545 | |||
| 546 | bool ASTManager::IndirectlyRelated(ASTNode first, ASTNode second) { | ||
| 547 | return !(first->GetParent() == second->GetParent() || DirectlyRelated(first, second)); | ||
| 548 | } | ||
| 549 | |||
| 550 | bool ASTManager::DirectlyRelated(ASTNode first, ASTNode second) { | ||
| 551 | if (first->GetParent() == second->GetParent()) { | ||
| 552 | return false; | ||
| 553 | } | ||
| 554 | const u32 first_level = first->GetLevel(); | ||
| 555 | const u32 second_level = second->GetLevel(); | ||
| 556 | u32 min_level; | ||
| 557 | u32 max_level; | ||
| 558 | ASTNode max; | ||
| 559 | ASTNode min; | ||
| 560 | if (first_level > second_level) { | ||
| 561 | min_level = second_level; | ||
| 562 | min = second; | ||
| 563 | max_level = first_level; | ||
| 564 | max = first; | ||
| 565 | } else { | ||
| 566 | min_level = first_level; | ||
| 567 | min = first; | ||
| 568 | max_level = second_level; | ||
| 569 | max = second; | ||
| 570 | } | ||
| 571 | |||
| 572 | while (max_level > min_level) { | ||
| 573 | max_level--; | ||
| 574 | max = max->GetParent(); | ||
| 575 | } | ||
| 576 | |||
| 577 | return min->GetParent() == max->GetParent(); | ||
| 578 | } | ||
| 579 | |||
| 580 | void ASTManager::ShowCurrentState(std::string state) { | ||
| 581 | LOG_CRITICAL(HW_GPU, "\nState {}:\n\n{}\n", state, Print()); | ||
| 582 | SanityCheck(); | ||
| 583 | } | ||
| 584 | |||
| 585 | void ASTManager::SanityCheck() { | ||
| 586 | for (auto& label : labels) { | ||
| 587 | if (!label->GetParent()) { | ||
| 588 | LOG_CRITICAL(HW_GPU, "Sanity Check Failed"); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | } | ||
| 592 | |||
| 593 | void ASTManager::EncloseDoWhile(ASTNode goto_node, ASTNode label) { | ||
| 594 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 595 | const ASTNode loop_start = label->GetNext(); | ||
| 596 | if (loop_start == goto_node) { | ||
| 597 | zipper.Remove(goto_node); | ||
| 598 | return; | ||
| 599 | } | ||
| 600 | const ASTNode parent = label->GetParent(); | ||
| 601 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 602 | zipper.DetachSegment(loop_start, goto_node); | ||
| 603 | const ASTNode do_while_node = ASTBase::Make<ASTDoWhile>(parent, condition); | ||
| 604 | ASTZipper* sub_zipper = do_while_node->GetSubNodes(); | ||
| 605 | sub_zipper->Init(loop_start, do_while_node); | ||
| 606 | zipper.InsertAfter(do_while_node, label); | ||
| 607 | sub_zipper->Remove(goto_node); | ||
| 608 | } | ||
| 609 | |||
| 610 | void ASTManager::EncloseIfThen(ASTNode goto_node, ASTNode label) { | ||
| 611 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 612 | const ASTNode if_end = label->GetPrevious(); | ||
| 613 | if (if_end == goto_node) { | ||
| 614 | zipper.Remove(goto_node); | ||
| 615 | return; | ||
| 616 | } | ||
| 617 | const ASTNode prev = goto_node->GetPrevious(); | ||
| 618 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 619 | bool do_else = false; | ||
| 620 | if (!disable_else_derivation && prev->IsIfThen()) { | ||
| 621 | const Expr if_condition = prev->GetIfCondition(); | ||
| 622 | do_else = ExprAreEqual(if_condition, condition); | ||
| 623 | } | ||
| 624 | const ASTNode parent = label->GetParent(); | ||
| 625 | zipper.DetachSegment(goto_node, if_end); | ||
| 626 | ASTNode if_node; | ||
| 627 | if (do_else) { | ||
| 628 | if_node = ASTBase::Make<ASTIfElse>(parent); | ||
| 629 | } else { | ||
| 630 | Expr neg_condition = MakeExprNot(condition); | ||
| 631 | if_node = ASTBase::Make<ASTIfThen>(parent, neg_condition); | ||
| 632 | } | ||
| 633 | ASTZipper* sub_zipper = if_node->GetSubNodes(); | ||
| 634 | sub_zipper->Init(goto_node, if_node); | ||
| 635 | zipper.InsertAfter(if_node, prev); | ||
| 636 | sub_zipper->Remove(goto_node); | ||
| 637 | } | ||
| 638 | |||
| 639 | void ASTManager::MoveOutward(ASTNode goto_node) { | ||
| 640 | ASTZipper& zipper = goto_node->GetManager(); | ||
| 641 | const ASTNode parent = goto_node->GetParent(); | ||
| 642 | ASTZipper& zipper2 = parent->GetManager(); | ||
| 643 | const ASTNode grandpa = parent->GetParent(); | ||
| 644 | const bool is_loop = parent->IsLoop(); | ||
| 645 | const bool is_else = parent->IsIfElse(); | ||
| 646 | const bool is_if = parent->IsIfThen(); | ||
| 647 | |||
| 648 | const ASTNode prev = goto_node->GetPrevious(); | ||
| 649 | const ASTNode post = goto_node->GetNext(); | ||
| 650 | |||
| 651 | const Expr condition = goto_node->GetGotoCondition(); | ||
| 652 | zipper.DetachSingle(goto_node); | ||
| 653 | if (is_loop) { | ||
| 654 | const u32 var_index = NewVariable(); | ||
| 655 | const Expr var_condition = MakeExpr<ExprVar>(var_index); | ||
| 656 | const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition); | ||
| 657 | const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition); | ||
| 658 | zipper2.InsertBefore(var_node_init, parent); | ||
| 659 | zipper.InsertAfter(var_node, prev); | ||
| 660 | goto_node->SetGotoCondition(var_condition); | ||
| 661 | const ASTNode break_node = ASTBase::Make<ASTBreak>(parent, var_condition); | ||
| 662 | zipper.InsertAfter(break_node, var_node); | ||
| 663 | } else if (is_if || is_else) { | ||
| 664 | const u32 var_index = NewVariable(); | ||
| 665 | const Expr var_condition = MakeExpr<ExprVar>(var_index); | ||
| 666 | const ASTNode var_node = ASTBase::Make<ASTVarSet>(parent, var_index, condition); | ||
| 667 | const ASTNode var_node_init = ASTBase::Make<ASTVarSet>(parent, var_index, false_condition); | ||
| 668 | if (is_if) { | ||
| 669 | zipper2.InsertBefore(var_node_init, parent); | ||
| 670 | } else { | ||
| 671 | zipper2.InsertBefore(var_node_init, parent->GetPrevious()); | ||
| 672 | } | ||
| 673 | zipper.InsertAfter(var_node, prev); | ||
| 674 | goto_node->SetGotoCondition(var_condition); | ||
| 675 | if (post) { | ||
| 676 | zipper.DetachTail(post); | ||
| 677 | const ASTNode if_node = ASTBase::Make<ASTIfThen>(parent, MakeExprNot(var_condition)); | ||
| 678 | ASTZipper* sub_zipper = if_node->GetSubNodes(); | ||
| 679 | sub_zipper->Init(post, if_node); | ||
| 680 | zipper.InsertAfter(if_node, var_node); | ||
| 681 | } | ||
| 682 | } else { | ||
| 683 | UNREACHABLE(); | ||
| 684 | } | ||
| 685 | const ASTNode next = parent->GetNext(); | ||
| 686 | if (is_if && next && next->IsIfElse()) { | ||
| 687 | zipper2.InsertAfter(goto_node, next); | ||
| 688 | goto_node->SetParent(grandpa); | ||
| 689 | return; | ||
| 690 | } | ||
| 691 | zipper2.InsertAfter(goto_node, parent); | ||
| 692 | goto_node->SetParent(grandpa); | ||
| 693 | } | ||
| 694 | |||
| 695 | class ASTClearer { | ||
| 696 | public: | ||
| 697 | ASTClearer() = default; | ||
| 698 | |||
| 699 | void operator()(ASTProgram& ast) { | ||
| 700 | ASTNode current = ast.nodes.GetFirst(); | ||
| 701 | while (current) { | ||
| 702 | Visit(current); | ||
| 703 | current = current->GetNext(); | ||
| 704 | } | ||
| 705 | } | ||
| 706 | |||
| 707 | void operator()(ASTIfThen& ast) { | ||
| 708 | ASTNode current = ast.nodes.GetFirst(); | ||
| 709 | while (current) { | ||
| 710 | Visit(current); | ||
| 711 | current = current->GetNext(); | ||
| 712 | } | ||
| 713 | } | ||
| 714 | |||
| 715 | void operator()(ASTIfElse& ast) { | ||
| 716 | ASTNode current = ast.nodes.GetFirst(); | ||
| 717 | while (current) { | ||
| 718 | Visit(current); | ||
| 719 | current = current->GetNext(); | ||
| 720 | } | ||
| 721 | } | ||
| 722 | |||
| 723 | void operator()(ASTBlockEncoded& ast) {} | ||
| 724 | |||
| 725 | void operator()(ASTBlockDecoded& ast) { | ||
| 726 | ast.nodes.clear(); | ||
| 727 | } | ||
| 728 | |||
| 729 | void operator()(ASTVarSet& ast) {} | ||
| 730 | |||
| 731 | void operator()(ASTLabel& ast) {} | ||
| 732 | |||
| 733 | void operator()(ASTGoto& ast) {} | ||
| 734 | |||
| 735 | void operator()(ASTDoWhile& ast) { | ||
| 736 | ASTNode current = ast.nodes.GetFirst(); | ||
| 737 | while (current) { | ||
| 738 | Visit(current); | ||
| 739 | current = current->GetNext(); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | |||
| 743 | void operator()(ASTReturn& ast) {} | ||
| 744 | |||
| 745 | void operator()(ASTBreak& ast) {} | ||
| 746 | |||
| 747 | void Visit(ASTNode& node) { | ||
| 748 | std::visit(*this, *node->GetInnerData()); | ||
| 749 | node->Clear(); | ||
| 750 | } | ||
| 751 | }; | ||
| 752 | |||
| 753 | void ASTManager::Clear() { | ||
| 754 | if (!main_node) { | ||
| 755 | return; | ||
| 756 | } | ||
| 757 | ASTClearer clearer{}; | ||
| 758 | clearer.Visit(main_node); | ||
| 759 | main_node.reset(); | ||
| 760 | program = nullptr; | ||
| 761 | labels_map.clear(); | ||
| 762 | labels.clear(); | ||
| 763 | gotos.clear(); | ||
| 764 | } | ||
| 765 | |||
| 766 | } // namespace VideoCommon::Shader | ||