@ -660,6 +660,9 @@ public:
IR : : Block & first_block { * syntax_list . front ( ) . data . block } ;
IR : : IREmitter ir ( first_block , first_block . begin ( ) ) ;
ir . Prologue ( ) ;
if ( uses_demote_to_helper ) {
DemoteCombinationPass ( ) ;
}
}
private :
@ -809,7 +812,14 @@ private:
}
case StatementType : : Return : {
ensure_block ( ) ;
IR : : IREmitter { * current_block } . Epilogue ( ) ;
IR : : Block * return_block { block_pool . Create ( inst_pool ) } ;
IR : : IREmitter { * return_block } . Epilogue ( ) ;
current_block - > AddBranch ( return_block ) ;
auto & merge { syntax_list . emplace_back ( ) } ;
merge . type = IR : : AbstractSyntaxNode : : Type : : Block ;
merge . data . block = return_block ;
current_block = nullptr ;
syntax_list . emplace_back ( ) . type = IR : : AbstractSyntaxNode : : Type : : Return ;
break ;
@ -824,6 +834,7 @@ private:
auto & merge { syntax_list . emplace_back ( ) } ;
merge . type = IR : : AbstractSyntaxNode : : Type : : Block ;
merge . data . block = demote_block ;
uses_demote_to_helper = true ;
break ;
}
case StatementType : : Unreachable : {
@ -855,11 +866,106 @@ private:
return block_pool . Create ( inst_pool ) ;
}
void DemoteCombinationPass ( ) {
using Type = IR : : AbstractSyntaxNode : : Type ;
std : : vector < IR : : Block * > demote_blocks ;
std : : vector < IR : : U1 > demote_conds ;
u32 num_epilogues { } ;
for ( const IR : : AbstractSyntaxNode & node : syntax_list ) {
if ( node . type ! = Type : : Block ) {
continue ;
}
for ( const IR : : Inst & inst : node . data . block - > Instructions ( ) ) {
const IR : : Opcode op { inst . GetOpcode ( ) } ;
if ( op = = IR : : Opcode : : DemoteToHelperInvocation ) {
demote_blocks . push_back ( node . data . block ) ;
break ;
}
if ( op = = IR : : Opcode : : Epilogue ) {
+ + num_epilogues ;
}
}
}
if ( demote_blocks . size ( ) = = 0 ) {
return ;
}
if ( num_epilogues > 1 ) {
LOG_DEBUG ( Shader , " Combining demotes with more than one return is not implemented. " ) ;
return ;
}
s64 last_iterator_offset { } ;
auto & asl { syntax_list } ;
for ( const IR : : Block * demote_block : demote_blocks ) {
const auto start_it { asl . begin ( ) + last_iterator_offset } ;
auto asl_it { std : : find_if ( start_it , asl . end ( ) , [ & ] ( const IR : : AbstractSyntaxNode & asn ) {
return asn . type = = Type : : If & & asn . data . if_node . body = = demote_block ;
} ) } ;
if ( asl_it = = asl . end ( ) ) {
// Demote without a conditional branch.
// No need to proceed since all fragment instances will be demoted regardless.
return ;
}
const IR : : Block * const end_if = asl_it - > data . if_node . merge ;
demote_conds . push_back ( asl_it - > data . if_node . cond ) ;
last_iterator_offset = std : : distance ( asl . begin ( ) , asl_it ) ;
asl_it = asl . erase ( asl_it ) ;
asl_it = std : : find_if ( asl_it , asl . end ( ) , [ & ] ( const IR : : AbstractSyntaxNode & asn ) {
return asn . type = = Type : : Block & & asn . data . block = = demote_block ;
} ) ;
asl_it = asl . erase ( asl_it ) ;
asl_it = std : : find_if ( asl_it , asl . end ( ) , [ & ] ( const IR : : AbstractSyntaxNode & asn ) {
return asn . type = = Type : : EndIf & & asn . data . end_if . merge = = end_if ;
} ) ;
asl_it = asl . erase ( asl_it ) ;
}
const auto epilogue_func { [ ] ( const IR : : AbstractSyntaxNode & asn ) {
if ( asn . type ! = Type : : Block ) {
return false ;
}
for ( const auto & inst : asn . data . block - > Instructions ( ) ) {
if ( inst . GetOpcode ( ) = = IR : : Opcode : : Epilogue ) {
return true ;
}
}
return false ;
} } ;
const auto reverse_it { std : : find_if ( asl . rbegin ( ) , asl . rend ( ) , epilogue_func ) } ;
const auto return_block_it { ( reverse_it + 1 ) . base ( ) } ;
IR : : IREmitter ir { * ( return_block_it - 1 ) - > data . block } ;
IR : : U1 cond ( IR : : Value ( false ) ) ;
for ( const auto & demote_cond : demote_conds ) {
cond = ir . LogicalOr ( cond , demote_cond ) ;
}
cond . Inst ( ) - > DestructiveAddUsage ( 1 ) ;
IR : : AbstractSyntaxNode demote_if_node { } ;
demote_if_node . type = Type : : If ;
demote_if_node . data . if_node . cond = cond ;
demote_if_node . data . if_node . body = demote_blocks [ 0 ] ;
demote_if_node . data . if_node . merge = return_block_it - > data . block ;
IR : : AbstractSyntaxNode demote_node { } ;
demote_node . type = Type : : Block ;
demote_node . data . block = demote_blocks [ 0 ] ;
IR : : AbstractSyntaxNode demote_endif_node { } ;
demote_endif_node . type = Type : : EndIf ;
demote_endif_node . data . end_if . merge = return_block_it - > data . block ;
asl . insert ( return_block_it , demote_endif_node ) ;
asl . insert ( return_block_it , demote_node ) ;
asl . insert ( return_block_it , demote_if_node ) ;
}
ObjectPool < Statement > & stmt_pool ;
ObjectPool < IR : : Inst > & inst_pool ;
ObjectPool < IR : : Block > & block_pool ;
Environment & env ;
IR : : AbstractSyntaxList & syntax_list ;
bool uses_demote_to_helper { } ;
// TODO: C++20 Remove this when all compilers support constexpr std::vector
# if __cpp_lib_constexpr_vector >= 201907