// Copyright Epic Games, Inc. All Rights Reserved. #include "Misc/AssertionMacros.h" #include "MuR/MutableTrace.h" #include "MuR/Operations.h" #include "MuR/Ptr.h" #include "MuR/RefCounted.h" #include "MuT/AST.h" #include "MuT/ASTOpConditional.h" #include "MuT/ASTOpInstanceAdd.h" #include "MuT/ASTOpSwitch.h" #include "MuT/ASTOpMeshMerge.h" #include "MuT/ASTOpBoolEqualIntConst.h" #include "MuT/CodeOptimiser.h" namespace mu { bool LocalLogicOptimiserAST(ASTOpList& roots) { MUTABLE_CPUPROFILER_SCOPE(LocalLogicOptimiserAST); bool bModified = false; // The separate steps should not be combined into one traversal // Unwrap some typical code daisy-chains //----------------------------------------------------------------------------------------- { MUTABLE_CPUPROFILER_SCOPE(Unwrap); ASTOp::Traverse_TopDown_Unique_Imprecise( roots, [&](Ptr& n) { bool bRecurse = true; if (n->GetOpType()==EOpType::IN_CONDITIONAL) { ASTOpConditional* topConditional = static_cast(n.get()); Ptr yes = topConditional->yes.child(); if (yes && yes->GetOpType()==EOpType::IN_ADDSURFACE) { ASTOpInstanceAdd* addSurface = static_cast(yes.get()); bool ended = false; while (!ended) { ended = true; Ptr base = addSurface->instance.child(); if ( base && base->GetOpType()==EOpType::IN_CONDITIONAL ) { ASTOpConditional* bottomConditional = static_cast(base.get()); // Are the two conditions exclusive? bool conditionaAreExclusive = false; ASTOpList facts; facts.Add(topConditional->condition.child()); // Check if the child condition has a value with the current facts Ptr pChildCond = bottomConditional->condition.child(); ASTOp::FBoolEvalResult result; { //MUTABLE_CPUPROFILER_SCOPE(EvaluateBool); result = pChildCond->EvaluateBool( facts ); } if ( result==ASTOp::BET_FALSE ) { conditionaAreExclusive = true; } if (conditionaAreExclusive) { if (addSurface->GetParentCount()==1) { // Directly modify the instruction to skip the impossible child option addSurface->instance = bottomConditional->no.child(); } else { // Other parents may not impose the same condition that allows the optimisation. Ptr newAddSurface = mu::Clone(addSurface); newAddSurface->instance = bottomConditional->no.child(); topConditional->yes = newAddSurface; addSurface = newAddSurface.get(); } bModified = true; ended = false; } } } } } else if (n->GetOpType()==EOpType::ME_CONDITIONAL) { ASTOpConditional* topConditional = static_cast(n.get()); Ptr yes = topConditional->yes.child(); if ( yes && yes->GetOpType()==EOpType::ME_MERGE ) { Ptr Merge = static_cast(yes.get()); bool ended = false; while (!ended) { ended = true; Ptr base = Merge->Base.child(); if ( base && base->GetOpType()==EOpType::ME_CONDITIONAL ) { ASTOpConditional* bottomConditional = static_cast(base.get()); // Are the two conditions exclusive? bool conditionaAreExclusive = false; ASTOpList facts; facts.Add(topConditional->condition.child()); // Check if the child condition has a value with the current facts Ptr pChildCond = bottomConditional->condition.child(); ASTOp::FBoolEvalResult result = pChildCond->EvaluateBool( facts ); if ( result==ASTOp::BET_FALSE ) { conditionaAreExclusive = true; } if (conditionaAreExclusive) { if (Merge->GetParentCount()==1) { // Directly modify the instruction to skip the impossible child option Merge->Base = bottomConditional->no.child(); } else { // Other parents may not impose the same condition that allows // the optimisation. Ptr NewMerge = mu::Clone(Merge); NewMerge->Base = bottomConditional->no.child(); topConditional->yes = NewMerge; Merge = NewMerge; } bModified = true; ended = false; } } } } } return bRecurse; }); } // See if we can turn conditional chains into switches: all conditions must be integer // comparison with the same variable. //----------------------------------------------------------------------------------------- { MUTABLE_CPUPROFILER_SCOPE(ConditionalToSwitch); ASTOp::Traverse_TopDown_Unique_Imprecise( roots, [&](Ptr& n) { bool bRecurse = true; ASTOpConditional* topConditional = nullptr; if (n && n->IsConditional()) { topConditional = static_cast(n.get()); } if (topConditional && topConditional->condition) { if ( topConditional->condition->GetOpType()==EOpType::BO_EQUAL_INT_CONST && topConditional->no && topConditional->no->GetOpType()==topConditional->GetOpType() ) { Ptr switchOp = new ASTOpSwitch(); switchOp->Type = GetSwitchForType(GetOpDataType(topConditional->GetOpType())); ASTOpBoolEqualIntConst* firstCompare = static_cast(topConditional->condition.child().get()); switchOp->Variable = firstCompare->Value.child(); Ptr current = n; while(current) { bool bValid = false; ASTOpConditional* conditional = nullptr; if (current && current->IsConditional()) { conditional = static_cast(current.get()); } if ( conditional && conditional->GetOpType()==topConditional->GetOpType() && conditional->condition && conditional->condition->GetOpType()==EOpType::BO_EQUAL_INT_CONST ) { ASTOpBoolEqualIntConst* compare = static_cast(conditional->condition.child().get()); check(compare); if (compare) { auto compareValue = compare->Value.child(); check(compare); if ( compareValue == switchOp->Variable.child() ) { switchOp->Cases.Emplace( compare->Constant, switchOp, conditional->yes.child() ); current = conditional->no.child(); bValid = true; } } } if (!bValid) { switchOp->Default = current; current = nullptr; } } const int MIN_CONDITIONS_TO_CREATE_SWITCH = 3; if (switchOp->Cases.Num()>=MIN_CONDITIONS_TO_CREATE_SWITCH) { ASTOp::Replace(n,switchOp); n = switchOp; bModified = true; } } } return bRecurse; }); } // Float operations up switches, to tidy up the code and reduce its size. // TODO? /* { MUTABLE_CPUPROFILER_SCOPE(FloatSwitches); ASTOp::Traverse_TopDown_Unique_Imprecise( roots, [&](Ptr& n) { bool bRecurse = true; ASTOpSwitch* TopSwitch = nullptr; if (n && n->IsSwitch()) { TopSwitch = static_cast(n.get()); } if (TopSwitch) { bool bFirst = true; EOpType CaseType = EOpType::NONE; for (const ASTOpSwitch::FCase& c: TopSwitch->Cases) { if ( c.Branch ) { if (bFirst) { bFirst = false; CaseType = c.Branch->GetOpType(); } else { if (CaseType !=c.Branch->GetOpType()) { CaseType = EOpType::NONE; break; } } } } } return bRecurse; }); } */ return bModified; } }