Files
UnrealEngine/Engine/Plugins/Mutable/Source/MutableTools/Private/MuT/CodeOptimiser_Logic.cpp
2025-05-18 13:04:45 +08:00

296 lines
12 KiB
C++

// 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<ASTOp>& n)
{
bool bRecurse = true;
if (n->GetOpType()==EOpType::IN_CONDITIONAL)
{
ASTOpConditional* topConditional = static_cast<ASTOpConditional*>(n.get());
Ptr<ASTOp> yes = topConditional->yes.child();
if (yes && yes->GetOpType()==EOpType::IN_ADDSURFACE)
{
ASTOpInstanceAdd* addSurface = static_cast<ASTOpInstanceAdd*>(yes.get());
bool ended = false;
while (!ended)
{
ended = true;
Ptr<ASTOp> base = addSurface->instance.child();
if ( base && base->GetOpType()==EOpType::IN_CONDITIONAL )
{
ASTOpConditional* bottomConditional = static_cast<ASTOpConditional*>(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<ASTOp> 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<ASTOpInstanceAdd> newAddSurface = mu::Clone<ASTOpInstanceAdd>(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<ASTOpConditional*>(n.get());
Ptr<ASTOp> yes = topConditional->yes.child();
if ( yes && yes->GetOpType()==EOpType::ME_MERGE )
{
Ptr<ASTOpMeshMerge> Merge = static_cast<ASTOpMeshMerge*>(yes.get());
bool ended = false;
while (!ended)
{
ended = true;
Ptr<ASTOp> base = Merge->Base.child();
if ( base && base->GetOpType()==EOpType::ME_CONDITIONAL )
{
ASTOpConditional* bottomConditional = static_cast<ASTOpConditional*>(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<ASTOp> 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<ASTOpMeshMerge> NewMerge = mu::Clone<ASTOpMeshMerge>(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<ASTOp>& n)
{
bool bRecurse = true;
ASTOpConditional* topConditional = nullptr;
if (n && n->IsConditional())
{
topConditional = static_cast<ASTOpConditional*>(n.get());
}
if (topConditional && topConditional->condition)
{
if ( topConditional->condition->GetOpType()==EOpType::BO_EQUAL_INT_CONST
&&
topConditional->no
&&
topConditional->no->GetOpType()==topConditional->GetOpType()
)
{
Ptr<ASTOpSwitch> switchOp = new ASTOpSwitch();
switchOp->Type = GetSwitchForType(GetOpDataType(topConditional->GetOpType()));
ASTOpBoolEqualIntConst* firstCompare = static_cast<ASTOpBoolEqualIntConst*>(topConditional->condition.child().get());
switchOp->Variable = firstCompare->Value.child();
Ptr<ASTOp> current = n;
while(current)
{
bool bValid = false;
ASTOpConditional* conditional = nullptr;
if (current && current->IsConditional())
{
conditional = static_cast<ASTOpConditional*>(current.get());
}
if ( conditional
&&
conditional->GetOpType()==topConditional->GetOpType()
&&
conditional->condition
&&
conditional->condition->GetOpType()==EOpType::BO_EQUAL_INT_CONST )
{
ASTOpBoolEqualIntConst* compare = static_cast<ASTOpBoolEqualIntConst*>(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<ASTOp>& n)
{
bool bRecurse = true;
ASTOpSwitch* TopSwitch = nullptr;
if (n && n->IsSwitch())
{
TopSwitch = static_cast<ASTOpSwitch*>(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;
}
}