// 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/CodeOptimiser.h" namespace mu { //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- // IntExpressionPtr GetIntExpression( OP::ADDRESS at, const FProgram& program ) // { // IntExpressionPtr pRes = new IntExpression; // const OP& op = program.m_code[at]; // OP_TYPE type = op.type; // switch ( type ) // { // case OP_TYPE::NU_CONSTANT: // pRes->type = IntExpression::IET_CONSTANT; // pRes->value = op.args.IntConstant.value; // break; // case OP_TYPE::NU_PARAMETER: // pRes->type = IntExpression::IET_PARAMETER; // pRes->parameter = op.args.Parameter.variable; // break; // default: // //check( false ); // pRes->type = IntExpression::IET_UNKNOWN; // break; // } // return pRes; // } //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- // BoolExpressionPtr GetBoolExpression( OP::ADDRESS at, // const FProgram& program, // BOOL_EXPRESSION_CACHE* cache ) // { // if (cache && (size_t)at<=cache->m_values.size() && cache->m_values[at]) // { // return cache->m_values[at]; // } // BoolExpressionPtr pRes = new BoolExpression; // const OP& op = program.m_code[at]; // OP_TYPE type = (OP_TYPE)op.type; // switch ( type ) // { // case OP_TYPE::BO_CONSTANT: // pRes->type = op.args.BoolConstant.value // ? BoolExpression::BET_TRUE // : BoolExpression::BET_FALSE; // break; // case OP_TYPE::BO_PARAMETER: // pRes->type = BoolExpression::BET_PARAMETER; // pRes->parameter = op.args.Parameter.variable; // break; // case OP_TYPE::BO_AND: // pRes->type = BoolExpression::BET_AND; // pRes->a = GetBoolExpression( op.args.BoolBinary.a, program, cache ); // pRes->b = GetBoolExpression( op.args.BoolBinary.b, program, cache ); // break; // case OP_TYPE::BO_OR: // pRes->type = BoolExpression::BET_OR; // pRes->a = GetBoolExpression( op.args.BoolBinary.a, program, cache ); // pRes->b = GetBoolExpression( op.args.BoolBinary.b, program, cache ); // break; // case OP_TYPE::BO_NOT: // pRes->type = BoolExpression::BET_NOT; // pRes->a = GetBoolExpression( op.args.BoolNot.source, program, cache ); // break; // case OP_TYPE::BO_EQUAL_INT_CONST: // pRes->type = BoolExpression::BET_INT_EQUAL_CONST; // pRes->intExp = GetIntExpression( op.args.BoolEqualScalarConst.value, program ); // pRes->intValue = op.args.BoolEqualScalarConst.constant; // break; // default: // check( false ); // pRes->type = BoolExpression::BET_UNKNOWN; // break; // } // if (cache) // { // if (cache->m_values.size()m_values.resize(program.m_opAddress.Num()); // } // cache->m_values[at]=pRes; // } // return pRes; // } //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- // void LogicCallstackGather::Apply( FProgram& program, int state ) // { // MUTABLE_CPUPROFILER_SCOPE(LogicCallstackGather); // Traverse( program.m_states[state].m_root, program ); // } // //--------------------------------------------------------------------------------------------- // void LogicCallstackGather::Visit( OP::ADDRESS at, FProgram& program ) // { // // We traverse top down // OP_TYPE type = (OP_TYPE)program.m_code[at].type; // OP::ARGS args = program.m_code[at].args; // switch ( type ) // { // //----------------------------------------------------------------------------------------- // case OP_TYPE::NU_CONDITIONAL: // case OP_TYPE::SC_CONDITIONAL: // case OP_TYPE::CO_CONDITIONAL: // case OP_TYPE::IM_CONDITIONAL: // case OP_TYPE::ME_CONDITIONAL: // case OP_TYPE::LA_CONDITIONAL: // case OP_TYPE::IN_CONDITIONAL: // { // m_factsPerCallstack[at].push_back( m_facts ); // BoolExpressionPtr pCond = GetBoolExpression( args.Conditional.condition, program, // &m_boolExpressionCache ); // // Check if the condition has a value with the current callstack. If it does, we skip // // the contradictory paths, as they will never happen. // BoolExpression::TYPE result = pCond->Evaluate( m_facts ); // // Recurse the condition without adding extra facts // Visit( args.Conditional.condition, program ); // if ( result != BoolExpression::BET_FALSE ) // { // // yes branch // m_facts.push_back( pCond ); // Visit( args.Conditional.yes, program ); // m_facts.pop_back(); // } // if ( result != BoolExpression::BET_TRUE ) // { // // no branch // BoolExpressionPtr pNot = new BoolExpression; // pNot->type = BoolExpression::BET_NOT; // pNot->a = pCond; // m_facts.push_back( pNot ); // Visit( args.Conditional.no, program ); // m_facts.pop_back(); // } // break; // } // case OP_TYPE::NU_SWITCH: // case OP_TYPE::SC_SWITCH: // case OP_TYPE::CO_SWITCH: // case OP_TYPE::IM_SWITCH: // case OP_TYPE::VO_SWITCH: // case OP_TYPE::ME_SWITCH: // case OP_TYPE::LA_SWITCH: // { // m_factsPerCallstack[at].push_back( m_facts ); // IntExpressionPtr pValue = GetIntExpression( args.Switch.variable, program ); // const auto& options = program.m_constantSwitches[args.Switch.options].m_options; // for ( size_t i=0; itype = BoolExpression::BET_INT_EQUAL_CONST; // pCond->intExp = pValue; // pCond->intValue = args.Switch.conditions[i]; // // Check if the condition has a value with the current callstack. If it does, we // // skip the contradictory paths, as they will never happen. // BoolExpression::TYPE result = pCond->Evaluate( m_facts ); // if (result!=BoolExpression::BET_FALSE) // { // // We don't know if it is true or false. Add its value as a fact and // // recurse. // m_facts.push_back( pCond ); // Visit( options[i].at, program ); // m_facts.pop_back(); // } // } // } // if ( args.Switch.def ) // { // // TODO: Add facts negating the options in the switch // Visit( args.Switch.def, program ); // } // break; // } // default: // // Normal recursion // Recurse( at, program ); // break; // } // } //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- // bool ClonelessLogicOptimiser::Apply( FProgram& program, int state ) // { // MUTABLE_CPUPROFILER_SCOPE(ClonelessLogicOptimiser); // // Gather all the conditions for every op // m_callstacks.Apply( program, state ); // m_modified = false; // Traverse( program.m_states[state].m_root, program ); // return m_modified; // } // //--------------------------------------------------------------------------------------------- // OP::ADDRESS ClonelessLogicOptimiser::Visit( OP::ADDRESS at, FProgram& program ) // { // // We traverse top down // OP_TYPE type = (OP_TYPE)program.m_code[at].type; // OP::ARGS args = program.m_code[at].args; // switch ( type ) // { // //----------------------------------------------------------------------------------------- // case OP_TYPE::NU_CONDITIONAL: // case OP_TYPE::SC_CONDITIONAL: // case OP_TYPE::CO_CONDITIONAL: // case OP_TYPE::IM_CONDITIONAL: // case OP_TYPE::ME_CONDITIONAL: // case OP_TYPE::LA_CONDITIONAL: // case OP_TYPE::IN_CONDITIONAL: // { // BOOL_EXPRESSION_CACHE localBoolExpressionCache; // BoolExpressionPtr pCond = GetBoolExpression( args.Conditional.condition, program, &localBoolExpressionCache ); // // Check if the condition has a value with all the possible callstacks on this op // vector< vector >& callstacks = m_callstacks.m_factsPerCallstack[at]; // check( callstacks.size() ); // BoolExpression::TYPE result = pCond->Evaluate( callstacks[0] ); // for ( size_t c=1; cEvaluate( callstacks[c] ); // if (result!=thisResult) // { // result = BoolExpression::BET_UNKNOWN; // break; // } // } // if ( result==BoolExpression::BET_TRUE ) // { // m_modified = true; // m_callstacks.m_factsPerCallstack[at] = m_callstacks.m_factsPerCallstack[program.m_code[at].args.Conditional.yes]; // at = program.m_code[at].args.Conditional.yes; // at = Visit( at, program ); // } // else if ( result==BoolExpression::BET_FALSE ) // { // m_modified = true; // m_callstacks.m_factsPerCallstack[at] = m_callstacks.m_factsPerCallstack[program.m_code[at].args.Conditional.no]; // at = program.m_code[at].args.Conditional.no; // at = Visit( at, program ); // } // else // { // at = Recurse( at, program ); // } // break; // } //// case OP_TYPE::NU_SWITCH: //// case OP_TYPE::SC_SWITCH: //// case OP_TYPE::CO_SWITCH: //// case OP_TYPE::IM_SWITCH: //// case OP_TYPE::VO_SWITCH: //// case OP_TYPE::ME_SWITCH: //// case OP_TYPE::LA_SWITCH: //// { //// IntExpressionPtr pValue = GetIntExpression( args.Switch.variable, program ); //// vector< vector >& callstacks = m_callstacks.m_factsPerCallstack[at]; //// check( callstacks.size() ); //// for ( int i=0;itype = BoolExpression::BET_INT_EQUAL_CONST; //// pCond->intExp = pValue; //// pCond->intValue = args.Switch.conditions[i]; //// // Check if the condition has a value with all the possible callstacks on this op //// BoolExpression::TYPE result = pCond->Evaluate( callstacks[0] ); //// for ( size_t c=1; cEvaluate( callstacks[c] ); //// if (result!=thisResult) //// { //// result = BoolExpression::BET_UNKNOWN; //// break; //// } //// } //// if ( result==BoolExpression::BET_TRUE ) //// { //// // This is the only possible branch. //// m_modified = true; //// at = args.Switch.values[i]; //// //at = Visit( at, program ); //// return at; //// break; //// } //// else if ( result==BoolExpression::BET_FALSE ) //// { //// // This option is impossible //// args.Switch.values[i] = 0; //// args.Switch.conditions[i] = 0; //// } //// else //// { //// // We don't know if it is true or false. Add its value as a fact and //// // recurse. //// //args.Switch.values[i] = Visit( args.Switch.values[i], program ); //// } //// } //// } //// if ( args.Switch.def ) //// { //// // TODO: Add facts negating the options in the switch //// //args.Switch.def = Visit( args.Switch.def, program ); //// } //// program.m_code[at].args = args; //// break; //// } // default: // // Normal recursion // at = Recurse( at, program ); // break; // } // return at; // } // //--------------------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------------------- // bool LogicOptimiser::Apply( FProgram& program, int state ) // { // m_initialCodeSize = program.m_opAddress.Num(); // m_modified = false; // Traverse( program.m_states[state].m_root, program ); // return m_modified; // } // //--------------------------------------------------------------------------------------------- // OP::ADDRESS LogicOptimiser::Visit( OP::ADDRESS at, FProgram& program ) // { // // Sanity stop // const int sanityFactor = 4; // if ( program.m_opAddress.Num()>m_initialCodeSize*sanityFactor) // { // return at; // } // // We traverse top down // OP_TYPE type = (OP_TYPE)program.m_code[at].type; // OP::ARGS args = program.m_code[at].args; // // We only apply it to instances, for performance reasons. // if (GetOpType(type)!=DT_INSTANCE) // { // return at; // } // switch ( type ) // { // //----------------------------------------------------------------------------------------- //// case OP_TYPE::NU_CONDITIONAL: //// case OP_TYPE::SC_CONDITIONAL: //// case OP_TYPE::CO_CONDITIONAL: //// case OP_TYPE::IM_CONDITIONAL: //// case OP_TYPE::ME_CONDITIONAL: //// case OP_TYPE::LA_CONDITIONAL: // case OP_TYPE::IN_CONDITIONAL: // { // BoolExpressionPtr pCond = GetBoolExpression( args.Conditional.condition, program, &m_boolExpressionCache ); // // Check if the condition has a value with the current facts // // TODO: This is a simple adhoc check which is also quite imprecise. // BoolExpression::TYPE result = pCond->Evaluate( m_facts ); // if ( result==BoolExpression::BET_TRUE ) // { // m_modified = true; // at = program.m_code[at].args.Conditional.yes; // at = Visit( at, program ); // } // else if ( result==BoolExpression::BET_FALSE ) // { // m_modified = true; // at = program.m_code[at].args.Conditional.no; // at = Visit( at, program ); // } // else // { // // Recurse after modifying the current facts // // TODO: Recurse condition too? // // yes // m_facts.push_back( pCond ); // args.Conditional.yes = Visit( args.Conditional.yes, program ); // m_facts.pop_back(); // // no // BoolExpressionPtr pNot = new BoolExpression; // pNot->type = BoolExpression::BET_NOT; // pNot->a = pCond; // m_facts.push_back( pNot ); // args.Conditional.no = Visit( args.Conditional.no, program ); // m_facts.pop_back(); // // update op if necessary // if ( FMemory::Memcmp( &args, &program.m_code[at].args, sizeof(OP::ARGS) ) ) // { // OP op = program.m_code[at]; // op.args = args; // at = program.AddOp( op ); // } // } // break; // } //// case OP_TYPE::NU_SWITCH: //// case OP_TYPE::SC_SWITCH: //// case OP_TYPE::CO_SWITCH: //// case OP_TYPE::IM_SWITCH: //// case OP_TYPE::VO_SWITCH: //// case OP_TYPE::ME_SWITCH: //// case OP_TYPE::LA_SWITCH: //// { //// IntExpressionPtr pValue = GetIntExpression( args.Switch.variable, program ); //// for ( int i=0;itype = BoolExpression::BET_INT_EQUAL_CONST; //// pCond->intExp = pValue; //// pCond->intValue = args.Switch.conditions[i]; //// BoolExpression::TYPE result = pCond->Evaluate( m_facts ); //// if ( result==BoolExpression::BET_TRUE ) //// { //// // This is the only possible branch. //// m_modified = true; //// at = args.Switch.values[i]; //// at = Visit( at, program ); //// return at; //// break; //// } //// else if ( result==BoolExpression::BET_FALSE ) //// { //// // This option is impossible //// args.Switch.values[i] = 0; //// args.Switch.conditions[i] = 0; //// } //// else //// { //// // We don't know if it is true or false. Add its value as a fact and //// // recurse. //// m_facts.push_back( pCond ); //// args.Switch.values[i] = Visit( args.Switch.values[i], program ); //// m_facts.pop_back(); //// } //// } //// } //// if ( args.Switch.def ) //// { //// // TODO: Add facts negating the options in the switch //// args.Switch.def = Visit( args.Switch.def, program ); //// } //// // update op if necessary //// if ( FMemory::Memcmp( &args, &program.m_code[at].args, sizeof(OP::ARGS) ) ) //// { //// m_modified = true; //// OP op = program.m_code[at]; //// op.args = args; //// at = program.AddOp( op ); //// } //// break; //// } // default: // // Normal recursion // at = Recurse( at, program ); // break; // } // return at; // } //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- // IntExpression::IntExpression() // { // type = IET_UNKNOWN; // parameter = 0; // value = 0; // } // //--------------------------------------------------------------------------------------------- // OP::ADDRESS IntExpression::GenerateCode( FProgram& program, FProgram::OP_CACHE* cache ) // { // OP::ADDRESS at = 0; // switch ( type ) // { // case IET_CONSTANT: // { // OP op; // op.type = OP_TYPE::NU_CONSTANT; // op.args.IntConstant.value = value; // at = program.AddOpUnique( op, cache ); // break; // } // case IET_PARAMETER: // { // OP op; // op.type = OP_TYPE::NU_PARAMETER; // op.args.Parameter.variable = parameter; // at = program.AddOpUnique( op, cache ); // break; // } // default: // check( false ); // break; // } // return at; // } // //--------------------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------------------- // //--------------------------------------------------------------------------------------------- // BoolExpression::BoolExpression() // { // type = BET_UNKNOWN; // parameter = 0; // intValue = 0; // } // //--------------------------------------------------------------------------------------------- // BoolExpression::TYPE BoolExpression::Evaluate( vector& facts ) // { // TYPE result = BET_UNKNOWN; // switch ( type ) // { // case BET_UNKNOWN: // break; // case BET_TRUE: // result = BET_TRUE; // break; // case BET_FALSE: // result = BET_FALSE; // break; // case BET_PARAMETER: // for ( size_t f=0; fEvaluate( facts ); // switch ( result ) // { // case BET_TRUE: // return BET_FALSE; // break; // case BET_FALSE: // return BET_TRUE; // break; // default: // break; // } // } // break; // case BET_AND: // { // TYPE resultA = BET_UNKNOWN; // TYPE resultB = BET_UNKNOWN; // for ( size_t f=0; fEvaluate( facts ); // } // if ( resultB == BET_UNKNOWN ) // { // resultB = b->Evaluate( facts ); // } // } // if ( resultA==BET_TRUE && resultB==BET_TRUE ) // { // result = BET_TRUE; // } // else if ( resultA==BET_FALSE || resultB==BET_FALSE ) // { // result = BET_FALSE; // } // break; // } // case BET_OR: // { // TYPE resultA = BET_UNKNOWN; // TYPE resultB = BET_UNKNOWN; // for ( size_t f=0; fEvaluate( facts ); // } // if ( resultB == BET_UNKNOWN ) // { // resultB = b->Evaluate( facts ); // } // } // if ( resultA==BET_TRUE || resultB==BET_TRUE ) // { // result = BET_TRUE; // } // else if ( resultA==BET_FALSE && resultB==BET_FALSE ) // { // result = BET_FALSE; // } // break; // } // case BET_INT_EQUAL_CONST: // result = EvaluateIntEquality( facts, intExp, intValue ); // break; // default: // check( false ); // break; // } // return result; // } // //--------------------------------------------------------------------------------------------- // OP::ADDRESS BoolExpression::GenerateCode( FProgram& program, FProgram::OP_CACHE* cache ) // { // if (m_generated) // { // return m_generated; // } // OP::ADDRESS at = 0; // switch ( type ) // { // case BET_TRUE: // { // OP op; // op.type = OP_TYPE::BO_CONSTANT; // op.args.BoolConstant.value = true; // at = program.AddOpUnique( op, cache ); // break; // } // case BET_FALSE: // { // OP op; // op.type = OP_TYPE::BO_CONSTANT; // op.args.BoolConstant.value = false; // at = program.AddOpUnique( op, cache ); // break; // } // case BET_NOT: // { // OP op; // op.type = OP_TYPE::BO_NOT; // op.args.BoolNot.source = a->GenerateCode( program, cache ); // at = program.AddOpUnique( op, cache ); // break; // } // case BET_AND: // { // OP op; // op.type = OP_TYPE::BO_AND; // op.args.BoolBinary.a = a->GenerateCode( program, cache ); // op.args.BoolBinary.b = b->GenerateCode( program, cache ); // at = program.AddOpUnique( op, cache ); // break; // } // case BET_OR: // { // OP op; // op.type = OP_TYPE::BO_OR; // op.args.BoolBinary.a = a->GenerateCode( program, cache ); // op.args.BoolBinary.b = b->GenerateCode( program, cache ); // at = program.AddOpUnique( op, cache ); // break; // } // case BET_PARAMETER: // { // OP op; // op.type = OP_TYPE::BO_PARAMETER; // op.args.Parameter.variable = parameter; // at = program.AddOpUnique( op, cache ); // break; // } // case BET_INT_EQUAL_CONST: // { // OP op; // op.type = OP_TYPE::BO_EQUAL_INT_CONST; // op.args.BoolEqualScalarConst.value = intExp->GenerateCode( program, cache ); // op.args.BoolEqualScalarConst.constant = (int16_t)intValue; // at = program.AddOpUnique( op, cache ); // break; // } // default: // check( false ); // break; // } // m_generated = at; // return at; // } // //--------------------------------------------------------------------------------------------- // BoolExpression::TYPE BoolExpression::EvaluateParameter( BoolExpressionPtr fact, OP::ADDRESS parameter) // { // TYPE result = BET_UNKNOWN; // switch (fact->type) // { // case BET_PARAMETER: // if ( parameter==fact->parameter ) // { // result = BET_TRUE; // } // break; // case BET_NOT: // result = EvaluateParameter( fact->a, parameter ); // if ( result == BET_TRUE ) // { // result = BET_FALSE; // } // else if ( result == BET_FALSE ) // { // result = BET_TRUE; // } // break; // case BET_AND: // result = EvaluateParameter( fact->a, parameter ); // if ( result == BET_UNKNOWN ) // { // result = EvaluateParameter( fact->b, parameter ); // } // break; // case BET_OR: // { // TYPE resultA = EvaluateParameter( fact->a, parameter ); // TYPE resultB = EvaluateParameter( fact->b, parameter ); // if ( resultA==BET_TRUE && resultB==BET_TRUE ) // { // result = BET_TRUE; // } // else if ( resultA==BET_FALSE && resultB==BET_FALSE ) // { // result = BET_FALSE; // } // else // { // // TODO: Check true+false value // result = BET_UNKNOWN; // } // break; // } // case BET_INT_EQUAL_CONST: // case BET_UNKNOWN: // case BET_TRUE: // case BET_FALSE: // break; // default: // check( false); // break; // } // return result; // } // //--------------------------------------------------------------------------------------------- // BoolExpression::TYPE BoolExpression::EvaluateIntEquality // ( const vector& facts, IntExpressionPtr intExp, int value ) // { // TYPE result = BET_UNKNOWN; // switch (intExp->type) // { // case IntExpression::IET_CONSTANT: // if ( value==intExp->value ) // { // result = BET_TRUE; // } // else // { // result = BET_FALSE; // } // break; // case IntExpression::IET_PARAMETER: // for ( size_t f=0; (result==BET_UNKNOWN) && ftype == BET_INT_EQUAL_CONST ) // { // if ( fact->intExp->type == IntExpression::IET_PARAMETER // && // fact->intExp->parameter == intExp->parameter // ) // { // if ( fact->intValue == value ) // { // result = BET_TRUE; // } // else // { // result = BET_FALSE; // } // } // } // else if( fact->type == BET_NOT ) // { // if (fact->a->type==BET_INT_EQUAL_CONST) // { // TYPE notResult = EvaluateIntEquality( fact->a.get(), intExp, value ); // if (notResult==BET_TRUE) // { // result = BET_FALSE; // } // else // { // // We cannot know, because there are other values. // } // } // else // { // MUTABLE_CPUPROFILER_SCOPE(Dummy); // } // } // else // { // MUTABLE_CPUPROFILER_SCOPE(Dummy); // } // return result; // } //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- bool LocalLogicOptimiserAST(ASTOpList& roots) { MUTABLE_CPUPROFILER_SCOPE(LocalLogicOptimiserAST); bool modified = 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 recurse = true; if (n->GetOpType()==OP_TYPE::IN_CONDITIONAL) { ASTOpConditional* topConditional = static_cast(n.get()); Ptr yes = topConditional->yes.child(); if (yes && yes->GetOpType()==OP_TYPE::IN_ADDSURFACE) { ASTOpInstanceAdd* addSurface = static_cast(yes.get()); bool ended = false; while (!ended) { ended = true; Ptr base = addSurface->instance.child(); if ( base && base->GetOpType()==OP_TYPE::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; } modified = true; ended = false; } } } } } else if (n->GetOpType()==OP_TYPE::ME_CONDITIONAL) { ASTOpConditional* topConditional = static_cast(n.get()); Ptr yes = topConditional->yes.child(); if ( yes && yes->GetOpType()==OP_TYPE::ME_MERGE ) { Ptr merge = static_cast(yes.get()); bool ended = false; while (!ended) { ended = true; Ptr base = merge->children[merge->op.args.MeshMerge.base].child(); if ( base && base->GetOpType()==OP_TYPE::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->SetChild(merge->op.args.MeshMerge.base, bottomConditional->no.child() ); } else { // Other parents may not impose the same condition that allows // the optimisation. Ptr newMerge = mu::Clone(merge); newMerge->SetChild(newMerge->op.args.MeshMerge.base, bottomConditional->no.child()); topConditional->yes = newMerge; merge = newMerge; } modified = true; ended = false; } } } } // todo, since the move to aggregated remove mask op // else if ( yes->GetOpType()==OP_TYPE::ME_REMOVEMASK ) // { // Ptr remove = dynamic_cast(yes.get()); // bool ended = false; // while (!ended) // { // ended = true; // auto base = remove->children[remove->op.args.MeshRemoveMask.source].child(); // if ( base && base->GetOpType()==OP_TYPE::ME_CONDITIONAL ) // { // auto bottomConditional = dynamic_cast(base.get()); // // Are the two conditions exclusive? // bool conditionaAreExclusive = false; // ASTOpList facts; // facts.push_back(topConditional->condition.child()); // // Check if the child condition has a value with the current facts // auto pChildCond = bottomConditional->condition.child(); // ASTOp::FBoolEvalResult result = pChildCond->EvaluateBool( facts ); // if ( result==ASTOp::BET_FALSE ) // { // conditionaAreExclusive = true; // } // if (conditionaAreExclusive) // { // if (remove->GetParentCount()==1) // { // // Directly modify the instruction to skip the impossible child option // remove->SetChild(remove->op.args.MeshRemoveMask.source, bottomConditional->no.child() ); // } // else // { // // Other parents may not impose the same condition that allows // // the optimisation. // auto newRemove = mu::Clone(remove); // newRemove->SetChild(newRemove->op.args.MeshRemoveMask.source, bottomConditional->no.child()); // topConditional->yes = newRemove; // } // modified = true; // ended = false; // } // } // } // } } return recurse; }); } // 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 recurse = true; ASTOpConditional* topConditional = nullptr; if (n && n->IsConditional()) { topConditional = static_cast(n.get()); } if (topConditional && topConditional->condition) { if ( topConditional->condition->GetOpType()==OP_TYPE::BO_EQUAL_INT_CONST && topConditional->no && topConditional->no->GetOpType()==topConditional->GetOpType() ) { Ptr switchOp = new ASTOpSwitch(); switchOp->type = GetSwitchForType(GetOpDataType(topConditional->GetOpType())); ASTOpFixed* firstCompare = static_cast(topConditional->condition.child().get()); switchOp->variable = firstCompare->children[firstCompare->op.args.BoolEqualScalarConst.value].child(); auto current = n; while(current) { bool valid = false; ASTOpConditional* conditional = nullptr; if (current && current->IsConditional()) { conditional = static_cast(current.get()); } if ( conditional && conditional->GetOpType()==topConditional->GetOpType() && conditional->condition && conditional->condition->GetOpType()==OP_TYPE::BO_EQUAL_INT_CONST ) { ASTOpFixed* compare = static_cast(conditional->condition.child().get()); check(compare); if (compare) { auto compareValue = compare->children[compare->op.args.BoolEqualScalarConst.value].child(); check(compare); if ( compareValue == switchOp->variable.child() ) { switchOp->cases.Emplace( compare->op.args.BoolEqualScalarConst.constant, switchOp, conditional->yes.child() ); current = conditional->no.child(); valid = true; } } } if (!valid) { switchOp->def = 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; modified = true; } } } return recurse; }); } // Float operations up switches, to tidy up the code and reduce its size. //----------------------------------------------------------------------------------------- { MUTABLE_CPUPROFILER_SCOPE(FloatSwitches); ASTOp::Traverse_TopDown_Unique_Imprecise( roots, [&](Ptr& n) { bool recurse = true; ASTOpSwitch* topSwitch = nullptr; if (n && n->IsSwitch()) { topSwitch = static_cast(n.get()); } if (topSwitch) { bool first = true; OP_TYPE caseType = OP_TYPE::NONE; for (const ASTOpSwitch::FCase& c:topSwitch->cases) { if ( c.branch ) { if (first) { first = false; caseType = c.branch->GetOpType(); } else { if (caseType!=c.branch->GetOpType()) { caseType=OP_TYPE::NONE; break; } } } } // todo, since the move to aggregated remove mask op // if (caseType==OP_TYPE::ME_REMOVEMASK) // { // // Is the source of all remove operations the default option in the switch? // auto source = topSwitch->def.child(); // for (const auto& c:topSwitch->cases) // { // if ( c.branch ) // { // auto typedRemove = dynamic_cast(c.branch.child().get()); // check( typedRemove ); // if (typedRemove->children[typedRemove->op.args.MeshRemoveMask.source].child()!=source) // { // source = nullptr; // break; // } // } // } // if (source) // { // auto newSwitch = mu::Clone(topSwitch); // for (auto& c: newSwitch->cases) // { // if ( c.branch ) // { // auto typedRemove = dynamic_cast(c.branch.child().get()); // c.branch = typedRemove->children[typedRemove->op.args.MeshRemoveMask.mask].child(); // } // } // newSwitch->def = nullptr; // Ptr newRemove = new ASTOpFixed(); // newRemove->op.type = OP_TYPE::ME_REMOVEMASK; // newRemove->SetChild( newRemove->op.args.MeshRemoveMask.mask, newSwitch ); // newRemove->SetChild( newRemove->op.args.MeshRemoveMask.source, source); // ASTOp::Replace(n,newRemove); // n = newRemove; // modified = true; // } // } } return recurse; }); } return modified; } }