Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/BranchState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ void BranchState::writeFloat( Token::Argument& argument )
assert( i != m_floats.end() );
}

updateDependency( argument, i->second, Alias::FLOAT, i->first, ((i->second.fields() & ~argument.fields()) != 0) );
// a write that leaves lanes as they were keeps them in the earlier
// value's register, if one of them is read before it is written again
const unsigned int kept = i->second.fields() & ~argument.fields();
updateDependency( argument, i->second, Alias::FLOAT, i->first, (kept & m_allocator.lanesLiveAfter( argument )) != 0 );

i->second.setFields( i->second.fields() | argument.fields() );
}
Expand Down Expand Up @@ -429,6 +432,13 @@ void BranchState::setIntegerOutput( const std::string& name, unsigned int regNum

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool BranchState::isFloatOutput( const std::string& name ) const
{
return m_floatOutputs.find( name ) != m_floatOutputs.end();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BranchState::storeAddress( const Token::Argument& argument )
{
assert( argument.type() == Token::Argument::INTEGER_REGISTER );
Expand Down
2 changes: 2 additions & 0 deletions src/BranchState.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ class BranchState
void setFloatOutput( const std::string& name, unsigned int regNumber );
void setIntegerOutput( const std::string& name, unsigned int regNumber );

bool isFloatOutput( const std::string& name ) const;

bool applyRegisterOutputs();

const Register* inputRegister( Alias::Type type, const std::string& name );
Expand Down
243 changes: 179 additions & 64 deletions src/RegisterAllocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,9 @@ bool RegisterAllocator::process( std::list<Token>& tokens )
blocks.push_back( std::make_pair( (*j)->current(), *j ) );
}

for( std::vector< std::pair< std::list<Token>::iterator, BranchState* > >::iterator b = blocks.begin(); b != blocks.end(); ++b )
collectLanesLiveAfterWrites( b->first, b->second->exitPoint(), b->second );

for( j = m_states.begin(); j != m_states.end(); j++ )
{
if( !(*j) )
Expand Down Expand Up @@ -608,83 +611,207 @@ namespace
use->depend( def );
return true;
}
}

bool RegisterAllocator::completeLiveRanges( std::list<Token>::iterator begin, std::list<Token>::iterator end, BranchState* entry )
{
std::vector< std::list<Token>::iterator > nodes;
std::map< const Token*, unsigned int > index;
for( std::list<Token>::iterator t = begin; t != end; ++t )
// A code block's tokens and the control flow between them: fall-through,
// branches to labels, and jr/jalr to the instruction after each bal/jalr
// that sets their register.
struct BlockFlow
{
index[ &*t ] = static_cast<unsigned int>( nodes.size() );
nodes.push_back( t );
}
const unsigned int count = static_cast<unsigned int>( nodes.size() );
if( !count )
return true;

// control flow: fall-through, branches to labels, and jr/jalr to the
// instruction after each bal/jalr that sets their register
std::vector< std::list<Token>::iterator > nodes;
std::vector< std::vector<unsigned int> > successors;
std::vector< std::vector<unsigned int> > predecessors;
bool fallsOffEnd; // the last token runs on into the block's exit
};

std::vector< std::pair<const Token::Argument*, unsigned int> > returns;
for( unsigned int n = 0; n + 1 < count; ++n )
void buildBlockFlow( std::list<Token>::iterator begin, std::list<Token>::iterator end, const std::map< std::string, std::list<Token>::iterator >& labels, BlockFlow& flow )
{
const Token& token = *nodes[n];
if( !token.operand() || token.operand()->unit() != Operand::BRU )
continue;
for( std::list<Token::Argument>::const_iterator a = token.arguments().begin(); a != token.arguments().end(); ++a )
std::map< const Token*, unsigned int > index;
for( std::list<Token>::iterator t = begin; t != end; ++t )
{
if( (*a).flags() & Token::Argument::ADDRESS )
returns.push_back( std::make_pair( &*a, n + 1 ) );
index[ &*t ] = static_cast<unsigned int>( flow.nodes.size() );
flow.nodes.push_back( t );
}
}
const unsigned int count = static_cast<unsigned int>( flow.nodes.size() );
flow.successors.assign( count, std::vector<unsigned int>() );
flow.predecessors.assign( count, std::vector<unsigned int>() );
flow.fallsOffEnd = false;

std::vector< std::vector<unsigned int> > successors( count );
for( unsigned int n = 0; n < count; ++n )
{
const Token& token = *nodes[n];
bool fallsThrough = true;
std::vector< std::pair<const Token::Argument*, unsigned int> > returns;
for( unsigned int n = 0; n + 1 < count; ++n )
{
const Token& token = *flow.nodes[n];
if( !token.operand() || token.operand()->unit() != Operand::BRU )
continue;
for( std::list<Token::Argument>::const_iterator a = token.arguments().begin(); a != token.arguments().end(); ++a )
{
if( (*a).flags() & Token::Argument::ADDRESS )
returns.push_back( std::make_pair( &*a, n + 1 ) );
}
}

if( token.operand() && token.operand()->unit() == Operand::BRU )
for( unsigned int n = 0; n < count; ++n )
{
fallsThrough = (token.operand()->flags() & Operand::DYNAMIC) != 0;
const Token& token = *flow.nodes[n];
bool fallsThrough = true;

for( std::list<Token::Argument>::const_iterator a = token.arguments().begin(); a != token.arguments().end(); ++a )
if( token.operand() && token.operand()->unit() == Operand::BRU )
{
if( !((*a).flags() & Token::Argument::BRANCH) )
continue;
fallsThrough = (token.operand()->flags() & Operand::DYNAMIC) != 0;

if( (*a).type() == Token::Argument::IMMEDIATE )
for( std::list<Token::Argument>::const_iterator a = token.arguments().begin(); a != token.arguments().end(); ++a )
{
std::map< std::string, std::list<Token>::iterator >::const_iterator label = m_labels.find( (*a).immediate() );
if( label == m_labels.end() )
if( !((*a).flags() & Token::Argument::BRANCH) )
continue;
std::map< const Token*, unsigned int >::const_iterator target = index.find( &*label->second );
if( target != index.end() )
successors[n].push_back( target->second );
}
else if( (*a).type() == Token::Argument::INTEGER_REGISTER )
{
for( unsigned int r = 0; r < returns.size(); ++r )

if( (*a).type() == Token::Argument::IMMEDIATE )
{
if( sameRegister( *returns[r].first, *a ) )
successors[n].push_back( returns[r].second );
std::map< std::string, std::list<Token>::iterator >::const_iterator label = labels.find( (*a).immediate() );
if( label == labels.end() )
continue;
std::map< const Token*, unsigned int >::const_iterator target = index.find( &*label->second );
if( target != index.end() )
flow.successors[n].push_back( target->second );
}
else if( (*a).type() == Token::Argument::INTEGER_REGISTER )
{
for( unsigned int r = 0; r < returns.size(); ++r )
{
if( sameRegister( *returns[r].first, *a ) )
flow.successors[n].push_back( returns[r].second );
}
}
}
}

if( fallsThrough )
{
if( n + 1 < count )
flow.successors[n].push_back( n + 1 );
else
flow.fallsOffEnd = true;
}
}

if( fallsThrough && n + 1 < count )
successors[n].push_back( n + 1 );
for( unsigned int n = 0; n < count; ++n )
{
for( unsigned int s = 0; s < flow.successors[n].size(); ++s )
flow.predecessors[ flow.successors[n][s] ].push_back( n );
}
}

std::vector< std::vector<unsigned int> > predecessors( count );
// The lanes a named register access touches: the fields of a VF access,
// 1 for VI. A broadcast named by the opcode (maddaw, mulz) leaves its
// operand without fields; it reads the token's broadcast lane.
unsigned int accessLanes( const Token& token, const Token::Argument& argument )
{
if( argument.type() != Token::Argument::FLOAT_REGISTER )
return 1;

unsigned int lanes = argument.fields();
if( !lanes && (argument.flags() & Token::Argument::BROADCAST) )
lanes = token.broadcast();
return lanes ? lanes : (Token::X|Token::Y|Token::Z|Token::W);
}

// The lanes of one VF variable each token reads and writes, and the writes
// that leave some of its lanes as they were.
struct VariableLanes
{
std::vector<unsigned int> read;
std::vector<unsigned int> written;
std::vector< std::pair<unsigned int, const Token::Argument*> > partialWrites;
};
}

void RegisterAllocator::collectLanesLiveAfterWrites( std::list<Token>::iterator begin, std::list<Token>::iterator end, BranchState* entry )
{
BlockFlow flow;
buildBlockFlow( begin, end, m_labels, flow );
const unsigned int count = static_cast<unsigned int>( flow.nodes.size() );
const unsigned int all = Token::X|Token::Y|Token::Z|Token::W;

std::map< std::string, VariableLanes > variables;
for( unsigned int n = 0; n < count; ++n )
{
for( unsigned int s = 0; s < successors[n].size(); ++s )
predecessors[ successors[n][s] ].push_back( n );
const Token& token = *flow.nodes[n];
for( std::list<Token::Argument>::const_iterator a = token.arguments().begin(); a != token.arguments().end(); ++a )
{
if( ((*a).content() != Token::Argument::ALIAS) || ((*a).type() != Token::Argument::FLOAT_REGISTER) )
continue;

VariableLanes& variable = variables[ (*a).alias() ];
if( variable.read.empty() )
{
variable.read.assign( count, 0 );
variable.written.assign( count, 0 );
}

const unsigned int lanes = accessLanes( token, *a );
if( (*a).flags() & Token::Argument::WRITE )
{
variable.written[n] |= lanes;
if( lanes != all )
variable.partialWrites.push_back( std::make_pair( n, &*a ) );
}
else
variable.read[n] |= lanes;
}
}

for( std::map< std::string, VariableLanes >::const_iterator v = variables.begin(); v != variables.end(); ++v )
{
const VariableLanes& variable = v->second;
if( variable.partialWrites.empty() )
continue;

// the lanes read later on some path before they are written again; the
// block's exit reads every lane of its outputs

const unsigned int atExit = entry->isFloatOutput( v->first ) ? all : 0;
std::vector<unsigned int> liveIn( count, 0 );
std::vector<unsigned int> liveOut( count, 0 );
bool changed = true;
while( changed )
{
changed = false;
for( unsigned int n = count; n-- > 0; )
{
unsigned int out = (flow.fallsOffEnd && (n + 1 == count)) ? atExit : 0;
for( unsigned int s = 0; s < flow.successors[n].size(); ++s )
out |= liveIn[ flow.successors[n][s] ];
liveOut[n] = out;

const unsigned int in = variable.read[n] | (out & ~variable.written[n]);
if( in != liveIn[n] )
{
liveIn[n] = in;
changed = true;
}
}
}

for( unsigned int w = 0; w < variable.partialWrites.size(); ++w )
m_lanesLiveAfter[ variable.partialWrites[w].second ] = liveOut[ variable.partialWrites[w].first ];
}
}

unsigned int RegisterAllocator::lanesLiveAfter( const Token::Argument& write ) const
{
std::map< const Token::Argument*, unsigned int >::const_iterator i = m_lanesLiveAfter.find( &write );
return (i == m_lanesLiveAfter.end()) ? (Token::X|Token::Y|Token::Z|Token::W) : i->second;
}

bool RegisterAllocator::completeLiveRanges( std::list<Token>::iterator begin, std::list<Token>::iterator end, BranchState* entry )
{
BlockFlow flow;
buildBlockFlow( begin, end, m_labels, flow );
const std::vector< std::list<Token>::iterator >& nodes = flow.nodes;
const std::vector< std::vector<unsigned int> >& successors = flow.successors;
const std::vector< std::vector<unsigned int> >& predecessors = flow.predecessors;
const unsigned int count = static_cast<unsigned int>( nodes.size() );
if( !count )
return true;

// every read and write of a named register, by variable

std::map< Variable, VariableAccesses > variables;
Expand All @@ -699,21 +826,9 @@ bool RegisterAllocator::completeLiveRanges( std::list<Token>::iterator begin, st

const bool isFloat = ((*a).type() == Token::Argument::FLOAT_REGISTER);

// a broadcast named by the opcode (maddaw, mulz) leaves its operand
// without fields: it reads the token's broadcast lane
unsigned int lanes = 1;
if( isFloat )
{
lanes = (*a).fields();
if( !lanes && ((*a).flags() & Token::Argument::BROADCAST) )
lanes = nodes[n]->broadcast();
if( !lanes )
lanes = Token::X|Token::Y|Token::Z|Token::W;
}

VariableAccess access;
access.node = n;
access.lanes = lanes;
access.lanes = accessLanes( *nodes[n], *a );
access.dependency = (*a).dependency();

VariableAccesses& accesses = variables[ Variable( isFloat ? Alias::FLOAT : Alias::INTEGER, (*a).alias() ) ];
Expand Down
9 changes: 9 additions & 0 deletions src/RegisterAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class RegisterAllocator
const Register* floatRegister( unsigned int regNumber ) const;
const Register* integerRegister( unsigned int regNumber ) const;

// the lanes of a VF write's variable that are read after the write, before
// they are written again (every lane where that is not known)
unsigned int lanesLiveAfter( const Token::Argument& write ) const;

const std::string& name();

void setDynamicThreshold( unsigned int threshold );
Expand Down Expand Up @@ -94,6 +98,10 @@ class RegisterAllocator
// would have.
bool completeLiveRanges( std::list<Token>::iterator begin, std::list<Token>::iterator end, BranchState* entry );

// For each VF write that leaves lanes of its variable as they were, the
// lanes still read after it, for lanesLiveAfter.
void collectLanesLiveAfterWrites( std::list<Token>::iterator begin, std::list<Token>::iterator end, BranchState* entry );

bool processCommonDirective( Token& token );

bool processAliases( Alias*& unallocated );
Expand Down Expand Up @@ -123,6 +131,7 @@ class RegisterAllocator

std::map< std::string, std::list<Token>::iterator > m_labels;
std::list< BranchState* > m_states;
std::map< const Token::Argument*, unsigned int > m_lanesLiveAfter;

// void generateReadErrorReport( const Token::Argument& arg, const Token& token );

Expand Down
Loading
Loading