2019-08-23 11:56:54 -07:00
//TODO: Temporary, to add asserts
# define BF_DEBUG_ASSERTS
# include <deque>
# include "BeMCContext.h"
# include "BeCOFFObject.h"
# include "BeIRCodeGen.h"
# include "BeefySysLib/util/BeefPerf.h"
# include "BeefySysLib/util/AllocDebug.h"
# pragma warning(disable:4146)
USING_NS_BF ;
//////////////////////////////////////////////////////////////////////////
# define IS_BIT_SET_32(bits, idx) (((bits)[(idx) / 32] & (1 << ((idx) % 32))) != 0)
// Only use "rep stosb"/"rep movsb" if total size is at least this value. The drawback is requiring saving/restore of RAX, RDI, and RCX (and RSI for movsb)
const int BF_REP_MOV_LIMIT = 128 ;
static const X64CPURegister gVolatileRegs [ ] = { X64Reg_RAX , X64Reg_RCX , X64Reg_RDX , X64Reg_R8 , X64Reg_R9 , X64Reg_R10 , X64Reg_R11 ,
X64Reg_XMM0_f64 , X64Reg_XMM1_f64 , X64Reg_XMM2_f64 , X64Reg_XMM3_f64 , X64Reg_XMM4_f64 , X64Reg_XMM5_f64 } ;
static const char * gOpName [ ] =
{
" None " ,
" @Def " ,
" @DefLoad " ,
" @DefPhi " ,
" @DbgDecl " ,
" @DbgRangeStart " ,
" @DbgRangeEnd " ,
" @LifetimeExtend " ,
" @LifetimeStart " ,
" @LifetimeEnd " ,
" @ValueScopeSoftEnd " ,
" @ValueScopeHardEnd " ,
" @Label " ,
//"PhiValue",
" CmpToBool " ,
" MemSet " ,
" MemCpy " ,
" FastCheckStack " ,
" TLSSetup " ,
" PreserveVolatiles " ,
" RestoreVolatiles " ,
" Unwind_PushReg " ,
" Unwind_SaveXMM " ,
" Unwind_Alloc " ,
" Unwind_SetBP " ,
" Rem " ,
" IRem " ,
" Nop " ,
" Unreachable " ,
" EnsureCodeAt " ,
" DbgBreak " ,
" MFence " ,
" Mov " ,
" MovRaw " ,
" MovSX " ,
" XChg " ,
" XAdd " ,
" CmpXChg " ,
" Load " ,
" Store " ,
" Push " ,
" Pop " ,
" Neg " ,
" Not " ,
" Add " ,
" Sub " ,
" Mul " ,
" IMul " ,
" Div " ,
" IDiv " ,
" Cmp " ,
" And " ,
" Or " ,
" Xor " ,
" Shl " ,
" Shr " ,
" Sar " ,
" Test " ,
" CondBr " ,
" Br " ,
" Ret " ,
" Call " ,
} ;
static_assert ( BF_ARRAY_COUNT ( gOpName ) = = ( int ) BeMCInstKind_COUNT , " gOpName incorrect size " ) ;
void PrintBoolVec ( Array < bool > & boolVec )
{
String str ;
for ( int i = 0 ; i < ( int ) boolVec . size ( ) ; i + + )
{
str + = StrFormat ( " %d: %s \n " , i , boolVec [ i ] ? " true " : " false " ) ;
}
OutputDebugStr ( str ) ;
}
static bool IsPowerOfTwo ( int64 val )
{
return ( val ! = 0 ) & & ( ( val & ( val - 1 ) ) = = 0 ) ;
}
bool BeVTrackingBits : : IsSet ( int idx )
{
return IS_BIT_SET_32 ( ( uint32 * ) this , idx ) ;
}
void BeVTrackingBits : : Set ( int idx )
{
uint32 * bits = ( uint32 * ) this ;
bits [ ( idx ) / 32 ] | = ( 1 < < ( idx % 32 ) ) ;
}
void BeVTrackingBits : : Clear ( int idx )
{
uint32 * bits = ( uint32 * ) this ;
bits [ ( idx ) / 32 ] & = ~ ( 1 < < ( idx % 32 ) ) ;
}
BeVTrackingContext : : BeVTrackingContext ( BeMCContext * mcContext )
{
mMCContext = mcContext ;
mNumEntries = 0 ;
mNumItems = 0 ;
mNumBlocks = - 1 ;
mTrackKindCount = ( int ) BeTrackKind_COUNT ;
}
void BeVTrackingContext : : Init ( int numItems )
{
mNumItems = numItems ;
mNumEntries = mNumItems * mTrackKindCount ;
mNumBlocks = ( mNumEntries + 31 ) / 32 ;
}
void BeVTrackingContext : : Clear ( )
{
mAlloc . Clear ( ) ;
mNumBlocks = - 1 ;
}
int BeVTrackingContext : : GetBitsBytes ( )
{
return mNumBlocks * 4 ;
}
int BeVTrackingContext : : GetIdx ( int baseIdx , BeTrackKind liveKind )
{
return baseIdx + mNumItems * ( int ) liveKind ;
}
void BeVTrackingContext : : Print ( BeVTrackingList * list )
{
String str ;
for ( int i : * list )
str + = StrFormat ( " %d " , i ) ;
if ( list - > mNumChanges > 0 )
{
str + = " | " ;
for ( int changeIdx = 0 ; changeIdx < list - > mNumChanges ; changeIdx + + )
{
int change = list - > GetChange ( changeIdx ) ;
if ( change > = 0 )
str + = StrFormat ( " +%d " , change ) ;
else
str + = StrFormat ( " -%d " , - change - 1 ) ;
}
}
str + = " \n " ;
OutputDebugStr ( str ) ;
}
BeVTrackingList * BeVTrackingContext : : AllocEmptyList ( )
{
int allocBytes = sizeof ( int ) * ( 2 ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
newList - > mSize = 0 ;
newList - > mNumChanges = 0 ;
return newList ;
}
BeVTrackingList * BeVTrackingContext : : AddFiltered ( BeVTrackingList * list , SizedArrayImpl < int > & filteredAdds , bool perserveChangeList )
{
int newSize = list - > mSize + filteredAdds . size ( ) ;
int allocBytes = sizeof ( int ) * ( 2 + newSize ) ;
if ( perserveChangeList )
allocBytes + = sizeof ( int ) * ( int ) ( list - > mNumChanges + filteredAdds . size ( ) ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
{
if ( filteredAdds . size ( ) > 1 )
std : : sort ( filteredAdds . begin ( ) , filteredAdds . end ( ) ) ;
int addIdx = 0 ;
int nextAdd ;
if ( addIdx < ( int ) filteredAdds . size ( ) )
nextAdd = filteredAdds [ addIdx + + ] ;
else
nextAdd = 0x7FFFFFFF ;
int * outPtr = & newList - > mEntries [ 0 ] ;
for ( auto idx : * list )
{
while ( idx > nextAdd )
{
* ( outPtr + + ) = nextAdd ;
if ( addIdx < ( int ) filteredAdds . size ( ) )
nextAdd = filteredAdds [ addIdx + + ] ;
else
nextAdd = 0x7FFFFFFF ;
}
* ( outPtr + + ) = idx ;
}
while ( nextAdd ! = 0x7FFFFFFF )
{
* ( outPtr + + ) = nextAdd ;
if ( addIdx > = ( int ) filteredAdds . size ( ) )
break ;
nextAdd = filteredAdds [ addIdx + + ] ;
}
}
newList - > mSize = newSize ;
if ( perserveChangeList )
{
for ( int changeIdx = 0 ; changeIdx < list - > mNumChanges ; changeIdx + + )
{
newList - > mEntries [ newSize + changeIdx ] = list - > GetChange ( changeIdx ) ;
}
for ( int changeIdx = 0 ; changeIdx < ( int ) filteredAdds . size ( ) ; changeIdx + + )
{
newList - > mEntries [ newSize + list - > mNumChanges + changeIdx ] = filteredAdds [ changeIdx ] ;
}
newList - > mNumChanges = list - > mNumChanges + ( int ) filteredAdds . size ( ) ;
}
else
newList - > mNumChanges = 0 ;
return newList ;
}
2019-10-18 09:13:37 -07:00
BeVTrackingList * BeVTrackingContext : : AddFiltered ( BeVTrackingList * list , int idx , bool perserveChangeList )
{
int newSize = list - > mSize + 1 ;
int allocBytes = sizeof ( int ) * ( 2 + newSize ) ;
if ( perserveChangeList )
allocBytes + = sizeof ( int ) * ( int ) ( list - > mNumChanges + 1 ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
{
int addIdx = 0 ;
int nextAdd ;
nextAdd = idx ;
int * outPtr = & newList - > mEntries [ 0 ] ;
for ( auto idx : * list )
{
while ( idx > nextAdd )
{
* ( outPtr + + ) = nextAdd ;
nextAdd = 0x7FFFFFFF ;
}
* ( outPtr + + ) = idx ;
}
while ( nextAdd ! = 0x7FFFFFFF )
{
* ( outPtr + + ) = nextAdd ;
break ;
}
}
newList - > mSize = newSize ;
if ( perserveChangeList )
{
for ( int changeIdx = 0 ; changeIdx < list - > mNumChanges ; changeIdx + + )
{
newList - > mEntries [ newSize + changeIdx ] = list - > GetChange ( changeIdx ) ;
}
newList - > mEntries [ newSize + list - > mNumChanges ] = idx ;
newList - > mNumChanges = list - > mNumChanges + ( int ) 1 ;
}
else
newList - > mNumChanges = 0 ;
return newList ;
}
2019-08-23 11:56:54 -07:00
BeVTrackingList * BeVTrackingContext : : Add ( BeVTrackingList * list , const SizedArrayImpl < int > & indices , bool perserveChangeList )
{
SizedArray < int , 16 > newIndices ;
for ( int idx : indices )
{
if ( ! IsSet ( list , idx ) )
{
newIndices . push_back ( idx ) ;
}
}
if ( newIndices . empty ( ) )
return list ;
return AddFiltered ( list , newIndices , perserveChangeList ) ;
}
2019-10-18 09:13:37 -07:00
BeVTrackingList * BeVTrackingContext : : Add ( BeVTrackingList * list , int idx , bool perserveChangeList )
{
if ( IsSet ( list , idx ) )
return list ;
return AddFiltered ( list , idx , perserveChangeList ) ;
}
2019-08-23 11:56:54 -07:00
// Performs an 'add' for items that were in prevDest
BeVTrackingList * BeVTrackingContext : : SetChanges ( BeVTrackingList * prevDestEntry , BeVTrackingList * mergeFrom )
{
int removeCount = 0 ;
int addCount = 0 ;
int newSize = prevDestEntry - > mSize ;
auto prevItr = prevDestEntry - > begin ( ) ;
auto prevEnd = prevDestEntry - > end ( ) ;
auto mergeFromItr = mergeFrom - > begin ( ) ;
auto mergeFromEnd = mergeFrom - > end ( ) ;
while ( ( prevItr ! = prevEnd ) & & ( mergeFromItr ! = mergeFromEnd ) )
{
int prevIdx = * prevItr ;
int mergeIdx = * mergeFromItr ;
bool done = false ;
while ( mergeIdx < prevIdx )
{
removeCount + + ;
+ + mergeFromItr ;
if ( mergeFromItr = = mergeFromEnd )
{
done = true ;
break ;
}
mergeIdx = * mergeFromItr ;
}
if ( done )
break ;
while ( prevIdx < mergeIdx )
{
addCount + + ;
+ + prevItr ;
if ( prevItr = = prevEnd )
{
done = true ;
break ;
}
prevIdx = * prevItr ;
}
if ( done )
break ;
if ( prevIdx = = mergeIdx )
{
+ + prevItr ;
+ + mergeFromItr ;
}
}
while ( prevItr ! = prevEnd )
{
addCount + + ;
+ + prevItr ;
}
while ( mergeFromItr ! = mergeFromEnd )
{
removeCount + + ;
+ + mergeFromItr ;
}
int allocBytes = sizeof ( int ) * ( 2 + newSize + addCount + removeCount ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
int * outPtr = & newList - > mEntries [ 0 ] ;
int * changePtr = & newList - > mEntries [ newSize ] ;
prevItr = prevDestEntry - > begin ( ) ;
mergeFromItr = mergeFrom - > begin ( ) ;
while ( ( prevItr ! = prevEnd ) & & ( mergeFromItr ! = mergeFromEnd ) )
{
int prevIdx = * prevItr ;
int mergeIdx = * mergeFromItr ;
bool done = false ;
while ( mergeIdx < prevIdx )
{
* ( changePtr + + ) = ~ mergeIdx ;
+ + mergeFromItr ;
if ( mergeFromItr = = mergeFromEnd )
{
done = true ;
break ;
}
mergeIdx = * mergeFromItr ;
}
if ( done )
break ;
while ( prevIdx < mergeIdx )
{
* ( outPtr + + ) = prevIdx ;
* ( changePtr + + ) = prevIdx ;
+ + prevItr ;
if ( prevItr = = prevEnd )
{
done = true ;
break ;
}
prevIdx = * prevItr ;
}
if ( done )
break ;
if ( prevIdx = = mergeIdx )
{
* ( outPtr + + ) = * prevItr ;
+ + prevItr ;
+ + mergeFromItr ;
}
}
while ( prevItr ! = prevEnd )
{
int prevIdx = * prevItr ;
* ( outPtr + + ) = * prevItr ;
* ( changePtr + + ) = prevIdx ;
+ + prevItr ;
}
while ( mergeFromItr ! = mergeFromEnd )
{
int mergeIdx = * mergeFromItr ;
* ( changePtr + + ) = ~ mergeIdx ;
+ + mergeFromItr ;
}
BF_ASSERT ( ( outPtr - & newList - > mEntries [ 0 ] ) = = newSize ) ;
BF_ASSERT ( ( changePtr - & newList - > mEntries [ newSize ] ) = = addCount + removeCount ) ;
newList - > mSize = newSize ;
newList - > mNumChanges = addCount + removeCount ;
return newList ;
}
BeVTrackingList * BeVTrackingContext : : ClearFiltered ( BeVTrackingList * list , const SizedArrayImpl < int > & indices )
{
/*int newSize = list->mSize - indices.size();
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( sizeof ( int ) * ( 1 + newSize ) ) ;
if ( indices . size ( ) = = 1 )
{
int findIdx0 = indices [ 0 ] ;
int * outPtr = 0 ;
for ( auto idx : indices )
{
if ( idx ! = findIdx0 )
* ( outPtr + + ) = idx ;
}
BF_ASSERT ( outPtr = = & newList - > mEntries [ 0 ] + indices . size ( ) ) ;
}
else if ( indices . size ( ) = = 2 )
{
int findIdx0 = indices [ 0 ] ;
int findIdx1 = indices [ 1 ] ;
int * outPtr = 0 ;
for ( auto idx : indices )
{
if ( ( idx ! = findIdx0 ) & & ( idx ! = findIdx1 ) )
* ( outPtr + + ) = idx ;
}
BF_ASSERT ( outPtr = = & newList - > mEntries [ 0 ] + indices . size ( ) ) ;
}
else
{
int * outPtr = 0 ;
for ( auto idx : indices )
{
if ( std : : find ( indices . begin ( ) , indices . end ( ) , idx ) = = indices . end ( ) )
* ( outPtr + + ) = idx ;
}
BF_ASSERT ( outPtr = = & newList - > mEntries [ 0 ] + indices . size ( ) ) ;
}
newList - > mSize = newSize ;
return newList ; */
BF_FATAL ( " Broken " ) ;
return NULL ;
}
BeVTrackingList * BeVTrackingContext : : Modify ( BeVTrackingList * list , const SizedArrayImpl < int > & inAdds , const SizedArrayImpl < int > & inRemoves , SizedArrayImpl < int > & filteredAdds , SizedArrayImpl < int > & filteredRemoves )
{
for ( int idx : inAdds )
{
if ( ! IsSet ( list , idx ) )
{
filteredAdds . push_back ( idx ) ;
}
}
for ( int idx : inRemoves )
{
if ( IsSet ( list , idx ) )
{
filteredRemoves . push_back ( idx ) ;
}
}
if ( ( filteredAdds . empty ( ) ) & & ( filteredRemoves . empty ( ) ) )
return list ;
int newSize = list - > mSize - filteredRemoves . size ( ) + filteredAdds . size ( ) ;
int changeSize = filteredRemoves . size ( ) + filteredAdds . size ( ) ;
int allocBytes = sizeof ( int ) * ( 2 + newSize + changeSize ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
/*if (filteredRemoves.size() == 0)
{
memcpy ( newList - > mEntries , unit . mList - > mEntries , sizeof ( int ) * unit . mList - > mSize ) ;
}
else if ( filteredRemoves . size ( ) = = 1 )
{
int findIdx0 = filteredRemoves [ 0 ] ;
int * outPtr = & newList - > mEntries [ 0 ] ;
for ( auto idx : * unit . mList )
{
if ( idx ! = findIdx0 )
* ( outPtr + + ) = idx ;
}
BF_ASSERT ( outPtr = = & newList - > mEntries [ 0 ] + unit . mList - > mSize - filteredRemoves . size ( ) ) ;
}
else if ( filteredRemoves . size ( ) = = 2 )
{
int findIdx0 = filteredRemoves [ 0 ] ;
int findIdx1 = filteredRemoves [ 1 ] ;
int * outPtr = & newList - > mEntries [ 0 ] ;
for ( auto idx : * unit . mList )
{
if ( ( idx ! = findIdx0 ) & & ( idx ! = findIdx1 ) )
* ( outPtr + + ) = idx ;
}
BF_ASSERT ( outPtr = = & newList - > mEntries [ 0 ] + unit . mList - > mSize - filteredRemoves . size ( ) ) ;
}
else
{
int * outPtr = & newList - > mEntries [ 0 ] ;
for ( auto idx : * unit . mList )
{
if ( std : : find ( filteredRemoves . begin ( ) , filteredRemoves . end ( ) , idx ) = = filteredRemoves . end ( ) )
* ( outPtr + + ) = idx ;
}
BF_ASSERT ( outPtr = = & newList - > mEntries [ 0 ] + unit . mList - > mSize - filteredRemoves . size ( ) ) ;
} */
{
if ( filteredAdds . size ( ) > 1 )
std : : sort ( filteredAdds . begin ( ) , filteredAdds . end ( ) ) ;
if ( filteredRemoves . size ( ) > 1 )
std : : sort ( filteredRemoves . begin ( ) , filteredRemoves . end ( ) ) ;
int addIdx = 0 ;
int nextAdd ;
if ( addIdx < ( int ) filteredAdds . size ( ) )
nextAdd = filteredAdds [ addIdx + + ] ;
else
nextAdd = 0x7FFFFFFF ;
int removeIdx = 0 ;
int nextRemove ;
if ( removeIdx < ( int ) filteredRemoves . size ( ) )
nextRemove = filteredRemoves [ removeIdx + + ] ;
else
nextRemove = 0x7FFFFFFF ;
int * outPtr = & newList - > mEntries [ 0 ] ;
for ( auto idx : * list )
{
if ( idx = = nextRemove )
{
if ( removeIdx < ( int ) filteredRemoves . size ( ) )
nextRemove = filteredRemoves [ removeIdx + + ] ;
else
nextRemove = 0x7FFFFFFF ;
continue ;
}
while ( idx > nextAdd )
{
* ( outPtr + + ) = nextAdd ;
if ( addIdx < ( int ) filteredAdds . size ( ) )
nextAdd = filteredAdds [ addIdx + + ] ;
else
nextAdd = 0x7FFFFFFF ;
}
* ( outPtr + + ) = idx ;
}
while ( nextAdd ! = 0x7FFFFFFF )
{
* ( outPtr + + ) = nextAdd ;
if ( addIdx > = ( int ) filteredAdds . size ( ) )
break ;
nextAdd = filteredAdds [ addIdx + + ] ;
}
BF_ASSERT ( outPtr = = & newList - > mEntries [ 0 ] + newSize ) ;
BF_ASSERT ( ( nextAdd = 0x7FFFFFFF ) & & ( nextRemove = = 0x7FFFFFFF ) ) ;
}
for ( int i = 0 ; i < ( int ) filteredRemoves . size ( ) ; i + + )
{
newList - > mEntries [ newSize + i ] = - filteredRemoves [ i ] - 1 ;
}
for ( int i = 0 ; i < ( int ) filteredAdds . size ( ) ; i + + )
{
newList - > mEntries [ newSize + filteredRemoves . size ( ) + i ] = filteredAdds [ i ] ;
}
newList - > mSize = newSize ;
newList - > mNumChanges = changeSize ;
///
/*for (int i = 0; i < mNumEntries; i++)
BF_ASSERT ( IsSet ( newList , i ) = = unit . mBits - > IsSet ( i ) ) ;
int prevIdx = - 1 ;
for ( int idx : * newList )
{
BF_ASSERT ( idx > prevIdx ) ;
prevIdx = idx ;
}
OutputDebugStrF ( " Modify %d %@ \n " , modifyItrIdx , newList ) ; */
///
return newList ;
}
int BeVTrackingContext : : FindIndex ( BeVTrackingList * entry , int val )
{
int lo = 0 ;
int hi = entry - > mSize - 1 ;
while ( lo < = hi )
{
int i = ( lo + hi ) / 2 ;
int midVal = entry - > mEntries [ i ] ;
int c = midVal - val ;
if ( c = = 0 ) return i ;
if ( c < 0 )
lo = i + 1 ;
else
hi = i - 1 ;
}
return ~ lo ;
}
bool BeVTrackingContext : : IsSet ( BeVTrackingList * entry , int idx )
{
return FindIndex ( entry , idx ) > = 0 ;
}
bool BeVTrackingContext : : IsSet ( BeVTrackingList * entry , int idx , BeTrackKind trackKind )
{
return IsSet ( entry , GetIdx ( idx , trackKind ) ) ;
}
BeVTrackingList * BeVTrackingContext : : Clear ( BeVTrackingList * list , const SizedArrayImpl < int > & indices )
{
SizedArray < int , 16 > newIndices ;
for ( int idx : indices )
{
if ( IsSet ( list , idx ) )
{
newIndices . push_back ( idx ) ;
}
}
if ( newIndices . empty ( ) )
return list ;
return ClearFiltered ( list , newIndices ) ;
}
bool BeVTrackingContext : : IsEmpty ( BeVTrackingList * list )
{
return list - > mSize = = 0 ;
}
BeVTrackingList * BeVTrackingContext : : Merge ( BeVTrackingList * prevDestEntry , BeVTrackingList * mergeFrom )
{
if ( prevDestEntry = = NULL )
return mergeFrom ;
if ( mergeFrom - > mSize = = 0 )
return prevDestEntry ;
if ( prevDestEntry - > mSize = = 0 )
return mergeFrom ;
int newSize = prevDestEntry - > mSize ;
auto prevItr = prevDestEntry - > begin ( ) ;
auto prevEnd = prevDestEntry - > end ( ) ;
auto mergeFromItr = mergeFrom - > begin ( ) ;
auto mergeFromEnd = mergeFrom - > end ( ) ;
while ( ( prevItr ! = prevEnd ) & & ( mergeFromItr ! = mergeFromEnd ) )
{
int prevIdx = * prevItr ;
int mergeIdx = * mergeFromItr ;
bool done = false ;
while ( mergeIdx < prevIdx )
{
newSize + + ;
+ + mergeFromItr ;
if ( mergeFromItr = = mergeFromEnd )
{
done = true ;
break ;
}
mergeIdx = * mergeFromItr ;
}
if ( done )
break ;
while ( prevIdx < mergeIdx )
{
+ + prevItr ;
if ( prevItr = = prevEnd )
{
done = true ;
break ;
}
prevIdx = * prevItr ;
}
if ( done )
break ;
if ( prevIdx = = mergeIdx )
{
+ + prevItr ;
+ + mergeFromItr ;
}
}
while ( mergeFromItr ! = mergeFromEnd )
{
newSize + + ;
+ + mergeFromItr ;
}
if ( newSize = = prevDestEntry - > mSize )
return prevDestEntry ;
int allocBytes = sizeof ( int ) * ( 2 + newSize ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
int * outPtr = & newList - > mEntries [ 0 ] ;
prevItr = prevDestEntry - > begin ( ) ;
mergeFromItr = mergeFrom - > begin ( ) ;
while ( ( prevItr ! = prevEnd ) & & ( mergeFromItr ! = mergeFromEnd ) )
{
int prevIdx = * prevItr ;
int mergeIdx = * mergeFromItr ;
bool done = false ;
while ( mergeIdx < prevIdx )
{
* ( outPtr + + ) = mergeIdx ;
+ + mergeFromItr ;
if ( mergeFromItr = = mergeFromEnd )
{
done = true ;
break ;
}
mergeIdx = * mergeFromItr ;
}
if ( done )
break ;
while ( prevIdx < mergeIdx )
{
* ( outPtr + + ) = prevIdx ;
+ + prevItr ;
if ( prevItr = = prevEnd )
{
done = true ;
break ;
}
prevIdx = * prevItr ;
}
if ( done )
break ;
if ( prevIdx = = mergeIdx )
{
* ( outPtr + + ) = * prevItr ;
+ + prevItr ;
+ + mergeFromItr ;
}
}
while ( prevItr ! = prevEnd )
{
* ( outPtr + + ) = * prevItr ;
+ + prevItr ;
}
while ( mergeFromItr ! = mergeFromEnd )
{
* ( outPtr + + ) = * mergeFromItr ;
+ + mergeFromItr ;
}
BF_ASSERT ( ( outPtr - & newList - > mEntries [ 0 ] ) = = newSize ) ;
newList - > mSize = newSize ;
newList - > mNumChanges = 0 ;
return newList ;
}
BeVTrackingList * BeVTrackingContext : : MergeChanges ( BeVTrackingList * prevDestEntry , BeVTrackingList * mergeFrom )
{
if ( prevDestEntry = = mergeFrom )
return prevDestEntry ;
if ( mergeFrom - > mNumChanges = = 0 )
return prevDestEntry ;
SizedArray < int , 2 > changes ;
for ( int changeIdx = 0 ; changeIdx < ( int ) prevDestEntry - > mNumChanges ; changeIdx + + )
changes . push_back ( prevDestEntry - > GetChange ( changeIdx ) ) ;
for ( int changeIdx = 0 ; changeIdx < ( int ) mergeFrom - > mNumChanges ; changeIdx + + )
{
// If there isn't already a change (whether and add or a remove) that refers to this same vreg,
// then we add this change as well
int change = mergeFrom - > GetChange ( changeIdx ) ;
int negChange = - change - 1 ;
/*if (((std::find(changes.begin(), changes.end(), change) == changes.end())) &&
( ( std : : find ( changes . begin ( ) , changes . end ( ) , negChange ) = = changes . end ( ) ) ) ) */
if ( ( ! changes . Contains ( change ) ) & & ( ! changes . Contains ( negChange ) ) )
{
changes . push_back ( change ) ;
}
}
int newSize = prevDestEntry - > mSize ;
int allocBytes = ( int ) ( sizeof ( int ) * ( 2 + newSize + changes . size ( ) ) ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
memcpy ( & newList - > mEntries [ 0 ] , & prevDestEntry - > mEntries [ 0 ] , newSize * sizeof ( int ) ) ;
for ( int i = 0 ; i < ( int ) changes . size ( ) ; i + + )
{
newList - > mEntries [ newSize + i ] = changes [ i ] ;
}
newList - > mSize = newSize ;
newList - > mNumChanges = ( int ) changes . size ( ) ;
return newList ;
}
BeVTrackingList * BeVTrackingContext : : RemoveChange ( BeVTrackingList * prevDestEntry , int idx )
{
int newSize = prevDestEntry - > mSize ;
int allocBytes = ( int ) ( sizeof ( int ) * ( 2 + newSize + prevDestEntry - > mNumChanges - 1 ) ) ;
auto newList = ( BeVTrackingList * ) mAlloc . AllocBytes ( allocBytes ) ;
mStats . mListBytes + = allocBytes ;
memcpy ( & newList - > mEntries [ 0 ] , & prevDestEntry - > mEntries [ 0 ] , newSize * sizeof ( int ) ) ;
bool found = false ;
int outIdx = newSize ;
for ( int i = 0 ; i < ( int ) prevDestEntry - > mNumChanges ; i + + )
{
int change = prevDestEntry - > GetChange ( i ) ;
if ( change = = idx )
{
found = true ;
continue ;
}
newList - > mEntries [ outIdx + + ] = change ;
}
BF_ASSERT ( found ) ;
newList - > mSize = newSize ;
newList - > mNumChanges = prevDestEntry - > mNumChanges - 1 ;
return newList ;
}
//////////////////////////////////////////////////////////////////////////
BeMCColorizer : : BeMCColorizer ( BeMCContext * mcContext )
{
mContext = mcContext ;
mReserveParamRegs = false ;
}
void BeMCColorizer : : Prepare ( )
{
mReserveParamRegs = false ;
mNodes . Resize ( mContext - > mVRegInfo . size ( ) ) ;
if ( mContext - > mDebugging )
{
NOP ;
}
for ( int vregIdx = 0 ; vregIdx < ( int ) mNodes . size ( ) ; vregIdx + + )
{
auto node = & mNodes [ vregIdx ] ;
node - > Prepare ( ) ;
//node->mActualVRegIdx = vregIdx;
auto vregInfo = mContext - > mVRegInfo [ vregIdx ] ;
if ( ( vregInfo - > mIsRetVal ) & & ( mContext - > mCompositeRetVRegIdx ! = - 1 ) & & ( vregIdx ! = mContext - > mCompositeRetVRegIdx ) )
continue ;
if ( vregInfo - > mRelTo )
{
BF_ASSERT ( vregInfo - > mIsExpr ) ;
/*if ((vregInfo->IsDirectRelTo()) && (vregInfo->mRelTo.mKind == BeMCOperandKind_VReg))
node - > mActualVRegIdx = vregInfo - > mRelTo . mVRegIdx ; */
//node->mWantsReg = false;
//vregInfo->mReg = X64Reg_None;
//continue;
}
if ( ( vregInfo - > mRefCount > 0 ) | | ( vregInfo - > mIsRetVal ) )
{
node - > mWantsReg = ( ( vregInfo - > mType - > mSize > 0 ) & & ( ! vregInfo - > mRegNumPinned ) & & ( ! vregInfo - > mSpilled ) & &
( ! vregInfo - > mIsExpr ) & & ( ! vregInfo - > mForceMem ) & & ( vregInfo - > mFrameOffset = = INT_MIN ) ) ;
}
// if (vregInfo->mIsRetVal)
// node->mWantsReg = true;
if ( ! node - > mWantsReg )
vregInfo - > mReg = X64Reg_None ;
vregInfo - > mVRegAffinity = - 1 ;
/*if (vregInfo->mForceReg)
{
// We can't have reg restrictions when we have forceReg set
BF_ASSERT ( ! vregInfo - > mDisableR12 ) ;
BF_ASSERT ( ! vregInfo - > mDisableRDX ) ;
} */
if ( vregInfo - > mDisableR11 )
node - > AdjustRegCost ( X64Reg_R11 , 0x0FFFFFFF ) ;
if ( vregInfo - > mDisableR12 )
node - > AdjustRegCost ( X64Reg_R12 , 0x0FFFFFFF ) ;
if ( vregInfo - > mDisableR13 )
node - > AdjustRegCost ( X64Reg_R13 , 0x0FFFFFFF ) ;
if ( vregInfo - > mDisableRAX )
node - > AdjustRegCost ( X64Reg_RAX , 0x0FFFFFFF ) ;
if ( vregInfo - > mDisableRDX )
node - > AdjustRegCost ( X64Reg_RDX , 0x0FFFFFFF ) ;
if ( vregInfo - > mDisableEx )
{
for ( int i = X64Reg_RSI ; i < = X64Reg_R15 ; i + + )
node - > AdjustRegCost ( ( X64CPURegister ) i , 0x0FFFFFFF ) ;
}
}
}
void BeMCColorizer : : AddEdge ( int vreg0 , int vreg1 )
{
int checkVRegIdx0 = mContext - > GetUnderlyingVReg ( vreg0 ) ;
int checkVRegIdx1 = mContext - > GetUnderlyingVReg ( vreg1 ) ;
if ( checkVRegIdx0 = = checkVRegIdx1 )
return ;
auto node0 = & mNodes [ checkVRegIdx0 ] ;
auto node1 = & mNodes [ checkVRegIdx1 ] ;
if ( ( node0 - > mWantsReg ) & & ( node1 - > mWantsReg ) )
{
node0 - > mEdges . Add ( checkVRegIdx1 ) ;
node1 - > mEdges . Add ( checkVRegIdx0 ) ;
}
}
void BeMCColorizer : : PropogateMemCost ( const BeMCOperand & operand , int memCost )
{
if ( operand . IsVRegAny ( ) )
{
auto vregInfo = mContext - > mVRegInfo [ operand . mVRegIdx ] ;
mNodes [ operand . mVRegIdx ] . mMemCost + = memCost ;
PropogateMemCost ( vregInfo - > mRelTo , memCost ) ;
PropogateMemCost ( vregInfo - > mRelOffset , memCost ) ;
}
}
void BeMCColorizer : : GenerateRegCosts ( )
{
bool doRegCost = true ;
// Disallow param reg cross-refs
{
Array < X64CPURegister > paramRegsLeft = mContext - > mParamsUsedRegs ;
Array < int > prevRegMovs ;
auto mcBlock = mContext - > mBlocks [ 0 ] ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( ( inst - > IsMov ( ) ) & & ( inst - > mArg1 . IsNativeReg ( ) ) & & ( ! inst - > mArg0 . IsNativeReg ( ) ) )
{
BF_ASSERT ( inst - > mArg0 . IsVReg ( ) ) ;
int vregIdx = mContext - > GetUnderlyingVReg ( inst - > mArg0 . mVRegIdx ) ;
auto reg = mContext - > ResizeRegister ( inst - > mArg1 . mReg , 8 ) ;
// Don't allow any previous "direct reg movs" assign to a vreg that gets bound
// to this reg. This is to avoid "cross-referencing" param vregs. IE:
// Mov %vreg0<RDX>, RCX
// Mov %vreg1<RCX>, RDX
// Because the first MOV would destroy the incoming RDX
for ( auto prevVRegIdx : prevRegMovs )
{
auto node = & mNodes [ prevVRegIdx ] ;
node - > AdjustRegCost ( reg , 0x0FFFFFFF ) ;
}
/*auto itr = std::find(paramRegsLeft.begin(), paramRegsLeft.end(), reg);
if ( itr ! = paramRegsLeft . end ( ) )
{
paramRegsLeft . erase ( itr ) ;
if ( paramRegsLeft . size ( ) = = 0 )
break ;
prevRegMovs . push_back ( vregIdx ) ;
} */
if ( paramRegsLeft . Remove ( reg ) )
{
if ( paramRegsLeft . size ( ) = = 0 )
break ;
prevRegMovs . push_back ( vregIdx ) ;
}
}
}
BF_ASSERT ( paramRegsLeft . size ( ) = = 0 ) ;
}
int preserveDepth = 0 ;
for ( auto mcBlock : mContext - > mBlocks )
{
int costMult = mcBlock - > mIsLooped ? 4 : 1 ;
for ( int instIdx = ( int ) mcBlock - > mInstructions . size ( ) - 1 ; instIdx > = 0 ; instIdx - - )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
// We could adjust register cost based on a possibility of cross-dependencies that won't allow us
// to reorder the movs for param setup
if ( inst - > mKind = = BeMCInstKind_PreserveVolatiles )
{
preserveDepth + + ;
int preserveCost = 8 * costMult ;
if ( inst - > mArg0 . mKind = = BeMCOperandKind_PreserveFlag )
{
// Just use a small cost for NoReturns, those are implied to be "unlikely" paths (ie: errors)
// But at the least, the code is smaller if we don't need to save the regs so use a small cost
if ( ( inst - > mArg0 . mPreserveFlag & BeMCPreserveFlag_NoRestore ) ! = 0 )
preserveCost = 1 ;
}
auto restoreIdx = mContext - > FindRestoreVolatiles ( mcBlock , instIdx ) ;
if ( restoreIdx = = - 1 )
restoreIdx = mcBlock - > mInstructions . size ( ) - 1 ; // 'Ret'
auto restoreInst = mcBlock - > mInstructions [ restoreIdx ] ;
// Any vregs alive during this call will incur a cost of saving/restoring if we allocate onto a volatile register.
// If the vreg is either input-only or output-only (short lived temporary) then we can map it directly to a volatile register
for ( int vregIdx : * inst - > mLiveness )
{
if ( vregIdx > = mContext - > mLivenessContext . mNumItems )
continue ;
auto checkVRegIdx = mContext - > GetUnderlyingVReg ( vregIdx ) ;
// Already handled the underlying vreg?
if ( ( checkVRegIdx ! = vregIdx ) & & ( mContext - > mLivenessContext . IsSet ( inst - > mLiveness , checkVRegIdx ) ) )
continue ;
if ( mContext - > mLivenessContext . IsSet ( restoreInst - > mLiveness , vregIdx ) )
{
auto node = & mNodes [ checkVRegIdx ] ;
if ( inst - > mArg0 . IsNativeReg ( ) )
{
// Only one specific register used being preserved
node - > AdjustRegCost ( inst - > mArg0 . mReg , preserveCost ) ;
}
else
{
// All volatile registers preserved
for ( auto reg : gVolatileRegs )
{
node - > AdjustRegCost ( reg , preserveCost ) ;
}
}
}
}
}
else if ( inst - > mKind = = BeMCInstKind_RestoreVolatiles )
{
preserveDepth - - ;
}
if ( inst - > IsPsuedo ( ) )
continue ;
// If both args are non-registers then we presume we may have to create a scratch register
// so inflate the cost. Note that the first time through, this will presumably occur for all
// operations but we can change our minds as instructions are legalized
if ( ( inst - > mArg0 . IsVReg ( ) ) & & ( inst - > mArg1 . IsVReg ( ) ) )
{
auto vregInfo0 = mContext - > mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
auto vregInfo1 = mContext - > mVRegInfo [ inst - > mArg1 . mVRegIdx ] ;
if ( ( vregInfo0 - > mReg = = X64Reg_None ) & & ( vregInfo1 - > mReg = = X64Reg_None ) )
{
Node * node0 = & mNodes [ inst - > mArg1 . mVRegIdx ] ;
Node * node1 = & mNodes [ inst - > mArg1 . mVRegIdx ] ;
node0 - > mMemCost + = 4 * costMult ;
node1 - > mMemCost + = 4 * costMult ;
}
}
if ( ( inst - > mKind = = BeMCInstKind_Div ) | | ( inst - > mKind = = BeMCInstKind_IDiv ) )
{
if ( inst - > mArg0 . IsVReg ( ) )
{
Node * node = & mNodes [ inst - > mArg0 . mVRegIdx ] ;
int adjustCost = - 4 ;
if ( preserveDepth > = 2 )
{
// This has to be large enough to counteract the 'PreserveVolatile RAX'
adjustCost - = 8 ;
}
node - > AdjustRegCost ( X64Reg_RAX , costMult * adjustCost ) ;
continue ;
}
}
if ( inst - > mKind = = BeMCInstKind_MovSX )
{
// We can only perform MovSX on reg targets
if ( inst - > mArg0 . IsVReg ( ) )
mNodes [ inst - > mArg0 . mVRegIdx ] . mMemCost + = 4 * costMult ;
}
if ( inst - > mKind = = BeMCInstKind_Mov )
{
if ( ( inst - > mArg0 . mKind = = BeMCOperandKind_VReg ) & &
( inst - > mArg1 . mKind = = BeMCOperandKind_VReg ) )
{
int arg0Idx = mContext - > GetUnderlyingVReg ( inst - > mArg0 . mVRegIdx ) ;
int arg1Idx = mContext - > GetUnderlyingVReg ( inst - > mArg1 . mVRegIdx ) ;
auto vregInfo0 = mContext - > mVRegInfo [ arg0Idx ] ;
auto vregInfo1 = mContext - > mVRegInfo [ arg1Idx ] ;
if ( vregInfo0 - > mVRegAffinity = = - 1 )
vregInfo0 - > mVRegAffinity = arg1Idx ;
if ( vregInfo1 - > mVRegAffinity = = - 1 )
vregInfo1 - > mVRegAffinity = arg0Idx ;
}
}
if ( inst - > mKind = = BeMCInstKind_Call )
{
if ( inst - > mArg0 . IsVRegAny ( ) )
{
for ( int checkInstIdx = instIdx - 1 ; checkInstIdx > = 0 ; checkInstIdx - - )
{
auto checkInst = mcBlock - > mInstructions [ checkInstIdx ] ;
if ( ( checkInst - > mKind = = BeMCInstKind_Mov ) & & ( checkInst - > mArg0 . IsNativeReg ( ) ) )
{
// This can save us from needing to mov the call addr to RAX before the call
mNodes [ inst - > mArg0 . mVRegIdx ] . AdjustRegCost ( checkInst - > mArg0 . mReg , 2 * costMult ) ;
}
else
break ;
}
}
}
if ( inst - > IsMov ( ) )
{
auto argTo = inst - > mArg0 ;
auto vregInfo = mContext - > GetVRegInfo ( argTo ) ;
if ( ( vregInfo ! = NULL ) & & ( vregInfo - > IsDirectRelTo ( ) ) )
argTo = vregInfo - > mRelTo ;
auto argFrom = inst - > mArg1 ;
vregInfo = mContext - > GetVRegInfo ( argFrom ) ;
if ( ( vregInfo ! = NULL ) & & ( vregInfo - > IsDirectRelTo ( ) ) )
argFrom = vregInfo - > mRelTo ;
if ( argTo = = argFrom )
continue ;
}
auto operands = { & inst - > mResult , & inst - > mArg0 , & inst - > mArg1 } ;
for ( auto operand : operands )
{
if ( operand - > IsVRegAny ( ) )
{
Node * node = & mNodes [ operand - > mVRegIdx ] ;
auto vregInfo = mContext - > mVRegInfo [ operand - > mVRegIdx ] ;
if ( ( vregInfo - > mIsRetVal ) & & ( mContext - > mCompositeRetVRegIdx ! = - 1 ) )
vregInfo = mContext - > mVRegInfo [ mContext - > mCompositeRetVRegIdx ] ;
if ( operand - > mKind = = BeMCOperandKind_VRegLoad )
{
// We propagate this mem cost to RelTo/RelOffset layer
Node * node = & mNodes [ operand - > mVRegIdx ] ;
node - > mMemCost + = 6 * costMult ;
}
while ( vregInfo - > IsDirectRelTo ( ) )
{
node = & mNodes [ vregInfo - > mRelTo . mVRegIdx ] ;
vregInfo = mContext - > mVRegInfo [ vregInfo - > mRelTo . mVRegIdx ] ;
}
node - > mMemCost + = 4 * costMult ;
if ( vregInfo - > mRelTo . IsVRegAny ( ) )
mNodes [ vregInfo - > mRelTo . mVRegIdx ] . mMemCost + = 4 * costMult ;
if ( vregInfo - > mRelOffset . IsVRegAny ( ) ) // Higher cost, will definitely require a reg
mNodes [ vregInfo - > mRelOffset . mVRegIdx ] . mMemCost + = 8 * costMult ;
if ( inst - > IsMov ( ) )
{
if ( operand ! = & inst - > mResult )
{
auto otherReg = X64Reg_None ;
if ( operand = = & inst - > mArg0 )
{
if ( inst - > mArg1 . mKind = = BeMCOperandKind_NativeReg )
otherReg = inst - > mArg1 . mReg ;
if ( inst - > mArg1 . IsImmediateFloat ( ) ) // Moving a float immediate into memory takes two instructions
node - > mMemCost + = 4 * costMult ;
}
else
{
if ( inst - > mArg0 . mKind = = BeMCOperandKind_NativeReg )
otherReg = inst - > mArg0 . mReg ;
}
// If we assigned ourselves to this reg then it would be a "mov reg, reg" which could
// be eliminated so the cost is negative. We could be tempted to more strongly favor this,
// but that could cause RAX to be used for __return when it would be less costly to use
// memory so we don't have to preserve/restore it across call boundaries
if ( otherReg ! = X64Reg_None )
node - > AdjustRegCost ( mContext - > ResizeRegister ( otherReg , 8 ) , - 2 * costMult ) ;
}
}
}
}
}
}
for ( int nodeIdx = 0 ; nodeIdx < ( int ) mNodes . size ( ) ; nodeIdx + + )
{
auto node = & mNodes [ nodeIdx ] ;
if ( node - > mMemCost > 0 )
{
auto vregInfo = mContext - > mVRegInfo [ nodeIdx ] ;
if ( vregInfo - > mIsExpr )
{
PropogateMemCost ( vregInfo - > mRelTo , node - > mMemCost ) ;
PropogateMemCost ( vregInfo - > mRelOffset , node - > mMemCost ) ;
}
}
}
BF_ASSERT ( preserveDepth = = 0 ) ;
}
void BeMCColorizer : : AssignRegs ( RegKind regKind )
{
if ( mContext - > mDebugging )
{
NOP ;
}
X64CPURegister highestReg ;
int totalRegs32 = 0 ;
int totalRegs16 = 0 ;
SizedArray < X64CPURegister , 32 > validRegs ;
if ( regKind = = BeMCColorizer : : RegKind_Ints )
{
highestReg = X64Reg_R15 ;
validRegs = { X64Reg_RAX , X64Reg_RBX , X64Reg_RCX , X64Reg_RDX , X64Reg_RSI , X64Reg_RDI , X64Reg_R8 ,
X64Reg_R9 , X64Reg_R10 , X64Reg_R11 , X64Reg_R12 , X64Reg_R13 , X64Reg_R14 , X64Reg_R15 } ;
}
else
{
highestReg = X64Reg_XMM15_f64 ;
validRegs = {
X64Reg_XMM0_f64 , X64Reg_XMM1_f64 , X64Reg_XMM2_f64 , X64Reg_XMM3_f64 ,
X64Reg_XMM4_f64 , X64Reg_XMM5_f64 , X64Reg_XMM6_f64 , X64Reg_XMM7_f64 ,
X64Reg_XMM8_f64 , X64Reg_XMM9_f64 , X64Reg_XMM10_f64 , X64Reg_XMM11_f64 ,
X64Reg_XMM12_f64 , X64Reg_XMM13_f64 , X64Reg_XMM14_f64 , X64Reg_XMM15_f64 } ;
}
int totalRegs = ( int ) validRegs . size ( ) ;
# define BF_DEQUE
# ifdef BF_DEQUE
std : : deque < int > vregStack ;
# else
std : : vector < int > vregStack ;
# endif
//vregStack.reserve(mNodes.size());
SizedArray < int , 32 > vregHiPriStack ;
vregHiPriStack . reserve ( mNodes . size ( ) ) ;
SizedArray < int , 32 > vregGraph ;
vregGraph . reserve ( mNodes . size ( ) ) ;
SizedArray < int , 32 > orderedSpillList ;
//
{
BP_ZONE ( " AssignRegs:vregGraph build " ) ;
for ( int vregIdx = ( int ) mNodes . size ( ) - 1 ; vregIdx > = 0 ; vregIdx - - )
{
auto node = & mNodes [ vregIdx ] ;
if ( node - > mWantsReg )
{
auto vregInfo = mContext - > mVRegInfo [ vregIdx ] ;
bool canBeReg = false ;
if ( regKind = = RegKind_Ints )
{
if ( ( vregInfo - > mType - > IsInt ( ) ) | | ( vregInfo - > mType - > mTypeCode = = BeTypeCode_Boolean ) | | ( vregInfo - > mType - > mTypeCode = = BeTypeCode_Pointer ) )
canBeReg = true ;
}
else if ( regKind = = RegKind_Floats )
{
if ( vregInfo - > mType - > IsFloat ( ) )
canBeReg = true ;
}
if ( canBeReg )
{
node - > mInGraph = true ;
node - > mGraphEdgeCount = 0 ;
vregGraph . push_back ( vregIdx ) ;
}
else
{
node - > mInGraph = false ;
}
}
}
}
for ( int vregIdx = ( int ) mNodes . size ( ) - 1 ; vregIdx > = 0 ; vregIdx - - )
{
auto node = & mNodes [ vregIdx ] ;
if ( node - > mInGraph )
{
for ( auto connNodeIdx : node - > mEdges )
{
Node * connNode = & mNodes [ connNodeIdx ] ;
if ( connNode - > mInGraph )
node - > mGraphEdgeCount + + ;
}
}
}
//TODO: This sort was meaningless I think...
//int xorVal = 227;
// For vregs that have a 'preferred register', make sure we pop those off the stack first to help
// ensure they actually get that assignment
//std::stable_sort(vregGraph.begin(), vregGraph.end(), [&] (int lhs, int rhs)
// {
// auto lhsInfo = mContext->mVRegInfo[lhs];
// auto rhsInfo = mContext->mVRegInfo[rhs];
// if (lhsInfo->mForceReg != rhsInfo->mForceReg)
// return !lhsInfo->mForceReg;
// return mNodes[lhs].mLowestRegCost > mNodes[rhs].mLowestRegCost;
// //return (lhs ^ xorVal) < (rhs ^ xorVal);
// });
//
{
int graphSize = ( int ) vregGraph . size ( ) ;
BP_ZONE ( " AssignRegs:buildStack " ) ;
while ( graphSize > 0 )
{
bool hadNewStackItem = false ;
# ifdef _DEBUG
2019-10-18 09:13:37 -07:00
// for (int graphIdx = 0; graphIdx < (int)vregGraph.size(); graphIdx++)
// {
// int validatedCount = 0;
// int vregIdx = vregGraph[graphIdx];
// if (vregIdx == -1)
// continue;
// Node* node = &mNodes[vregIdx];
// for (auto connNodeIdx : node->mEdges)
// {
// Node* connNode = &mNodes[connNodeIdx];
// if (connNode->mInGraph)
// validatedCount++;
// }
// BF_ASSERT(validatedCount == node->mGraphEdgeCount);
// }
2019-08-23 11:56:54 -07:00
# endif
for ( int graphIdx = 0 ; graphIdx < ( int ) vregGraph . size ( ) ; graphIdx + + )
{
int vregIdx = vregGraph [ graphIdx ] ;
if ( vregIdx = = - 1 )
continue ;
Node * node = & mNodes [ vregIdx ] ;
auto vregInfo = mContext - > mVRegInfo [ vregIdx ] ;
int validRegCount = ( int ) validRegs . size ( ) ;
BF_ASSERT ( node - > mGraphEdgeCount < = vregGraph . size ( ) - 1 ) ;
if ( ( node - > mSpilled ) | | ( node - > mGraphEdgeCount < validRegCount ) )
{
node - > mInGraph = false ;
for ( auto connNodeIdx : node - > mEdges )
{
Node * connNode = & mNodes [ connNodeIdx ] ;
connNode - > mGraphEdgeCount - - ;
}
vregGraph [ graphIdx ] = - 1 ;
graphSize - - ;
if ( ! node - > mSpilled )
{
vregStack . push_back ( vregIdx ) ;
hadNewStackItem = true ;
}
else
{
// We insert spills at the front so we can try to "optimistically" unspill them
// after all the definite coloring is done
# ifdef BF_DEQUE
vregStack . push_front ( vregIdx ) ;
# else
vregStack . insert ( vregStack . begin ( ) , vregIdx ) ;
# endif
}
}
}
if ( ! hadNewStackItem )
{
BP_ZONE ( " Spill " ) ;
// We need to spill!
int bestSpillVReg = - 1 ;
for ( int regPassIdx = 0 ; regPassIdx < 2 ; regPassIdx + + )
{
while ( ! orderedSpillList . empty ( ) )
{
int vregIdx = orderedSpillList . back ( ) ;
orderedSpillList . pop_back ( ) ;
if ( mNodes [ vregIdx ] . mInGraph )
{
bestSpillVReg = vregIdx ;
break ;
}
}
if ( bestSpillVReg ! = - 1 )
break ;
// Order by mem cost
orderedSpillList . reserve ( vregGraph . size ( ) ) ;
for ( int graphIdx = 0 ; graphIdx < ( int ) vregGraph . size ( ) ; graphIdx + + )
{
int vregIdx = vregGraph [ graphIdx ] ;
if ( vregIdx = = - 1 )
continue ;
auto vregInfo = mContext - > mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mForceReg )
continue ;
orderedSpillList . push_back ( vregIdx ) ;
}
std : : sort ( orderedSpillList . begin ( ) , orderedSpillList . end ( ) , [ & ] ( int lhs , int rhs )
{
return mNodes [ lhs ] . mMemCost > mNodes [ rhs ] . mMemCost ;
} ) ;
}
/*int bestSpillVReg = -1;
int bestSpillCost = INT_MAX ;
// We need to spill now! Try to spill something with the fewest references
for ( int graphIdx = 0 ; graphIdx < ( int ) vregGraph . size ( ) ; graphIdx + + )
{
int vregIdx = vregGraph [ graphIdx ] ;
auto vregInfo = mContext - > mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mForceReg )
continue ;
if ( vregInfo - > mRefCount < bestSpillCost )
{
bestSpillCost = vregInfo - > mRefCount ;
bestSpillVReg = vregIdx ;
}
} */
if ( bestSpillVReg ! = - 1 )
{
auto node = & mNodes [ bestSpillVReg ] ;
node - > mSpilled = true ;
auto spillVRegInfo = mContext - > mVRegInfo [ bestSpillVReg ] ;
spillVRegInfo - > mSpilled = true ;
}
else
{
mContext - > Fail ( " Unable to spill vreg " ) ;
}
}
}
}
SizedArray < bool , 32 > globalRegUsedVec ;
globalRegUsedVec . resize ( highestReg + 1 ) ;
SizedArray < bool , 32 > regUsedVec ;
regUsedVec . resize ( highestReg + 1 ) ;
/*String dbgStr;
if ( mContext - > mDebugging )
dbgStr + = " AssignRegs " ; */
BP_ZONE ( " AssignRegs:assign " ) ;
while ( vregStack . size ( ) > 0 )
{
int vregIdx = vregStack . back ( ) ;
vregStack . pop_back ( ) ;
/*if (mContext->mDebugging)
{
dbgStr + = StrFormat ( " VReg %d " , vregIdx ) ;
} */
BeMCVRegInfo * vregInfo = mContext - > mVRegInfo [ vregIdx ] ;
Node * node = & mNodes [ vregIdx ] ;
if ( vregInfo - > mVRegAffinity ! = - 1 )
{
auto affinityVRegInfo = mContext - > mVRegInfo [ vregInfo - > mVRegAffinity ] ;
if ( affinityVRegInfo - > mReg ! = X64Reg_None )
node - > AdjustRegCost ( affinityVRegInfo - > mReg , - 2 ) ;
}
for ( int i = 0 ; i < = highestReg ; i + + )
regUsedVec [ i ] = false ;
if ( mReserveParamRegs )
{
// This is a fallback case for when the "streams get crossed" during initialization-
// IE: when arg0 gets assigned to RDX and arg1 gets assigned to RCX and we end up with:
// MOV arg0<RDX>, RCX
// MOV arg1<RCX>, RDX
// Which is bad.
for ( auto reg : mContext - > mParamsUsedRegs )
{
if ( ( ( int ) reg < regUsedVec . size ( ) ) & & ( reg ! = vregInfo - > mNaturalReg ) )
regUsedVec [ ( int ) reg ] = true ;
}
}
for ( auto connNodeIdx : node - > mEdges )
{
Node * connNode = & mNodes [ connNodeIdx ] ;
if ( connNode - > mInGraph )
{
auto connVRegInfo = mContext - > mVRegInfo [ connNodeIdx ] ;
auto usedReg = mContext - > ResizeRegister ( connVRegInfo - > mReg , 8 ) ;
BF_ASSERT ( usedReg ! = X64Reg_None ) ;
regUsedVec [ ( int ) usedReg ] = true ;
}
}
auto bestReg = X64Reg_None ;
int bestRegCost = 0x07FFFFFF ; // 0x0FFFFFFF is considered illegal for a reg, so set the mem cost to lower than that...;
// This is the cost of just leaving the vreg as a memory access. In cases where we bind to a volatile
// register, we need to consider the cost of preserving and restoring that register across calls, so
// it cases where we have just a few accesses to this vreg but it spans a lot of calls then we just
// leave it as memory
if ( ! vregInfo - > mForceReg )
bestRegCost = node - > mMemCost ;
//for (auto validReg : validRegs)
int validRegCount = ( int ) validRegs . size ( ) ;
for ( int regIdx = 0 ; regIdx < validRegCount ; regIdx + + )
{
auto validReg = validRegs [ regIdx ] ;
if ( ! regUsedVec [ ( int ) validReg ] )
{
int checkCost = node - > mRegCost [ ( int ) validReg ] ;
// If this register is non-volatile then we'd have to save and restore it, which costs... unless
// some other vreg has already used this, then there's no additional cost
if ( ( ! globalRegUsedVec [ ( int ) validReg ] ) & & ( ! mContext - > IsVolatileReg ( validReg ) ) )
{
int costMult = 1 ;
if ( regKind = = BeMCColorizer : : RegKind_Floats )
costMult = 2 ;
checkCost + = 7 * costMult ;
}
if ( checkCost < bestRegCost )
{
// Try not to use registers that other params may want
for ( auto argReg : mContext - > mParamsUsedRegs )
{
if ( validReg = = argReg )
checkCost + = 1 ;
}
}
/*if (mContext->mDebugging)
{
dbgStr + = StrFormat ( " Cost %d:%d " , validReg , checkCost ) ;
} */
if ( checkCost < bestRegCost )
{
bestReg = validReg ;
bestRegCost = checkCost ;
}
}
}
if ( mContext - > mDebugging )
{
//auto itr = mContext->mDbgPreferredRegs.find(vregIdx);
//if (itr != mContext->mDbgPreferredRegs.end())
X64CPURegister * regPtr = NULL ;
if ( mContext - > mDbgPreferredRegs . TryGetValue ( vregIdx , & regPtr ) )
{
auto reg = * regPtr ;
if ( reg = = X64Reg_None )
{
if ( ! vregInfo - > mForceReg )
bestReg = reg ;
}
else
{
if ( ! regUsedVec [ ( int ) reg ] )
bestReg = reg ;
}
}
}
if ( vregInfo - > mSpilled )
{
if ( bestReg ! = X64Reg_None )
{
// We managed to optimistically unspill
vregInfo - > mSpilled = false ;
}
}
else
{
if ( vregInfo - > mForceReg )
{
// We didn't end up with a usable reg -- steal a reg from one of our edges
if ( bestReg = = X64Reg_None )
{
int bestSpillVReg = - 1 ;
int bestSpillCost = INT_MAX ;
for ( auto connNodeIdx : node - > mEdges )
{
Node * connNode = & mNodes [ connNodeIdx ] ;
if ( connNode - > mInGraph )
{
auto connVRegInfo = mContext - > mVRegInfo [ connNodeIdx ] ;
auto usedReg = mContext - > ResizeRegister ( connVRegInfo - > mReg , 8 ) ;
if ( connVRegInfo - > mForceReg )
continue ;
if ( node - > mRegCost [ usedReg ] < 0x07FFFFFFF )
{
if ( connVRegInfo - > mRefCount < bestSpillCost )
{
bestSpillCost = connVRegInfo - > mRefCount ;
bestSpillVReg = connNodeIdx ;
}
}
}
}
if ( bestSpillVReg ! = - 1 )
{
/*if (mContext->mDebugging)
{
dbgStr + = StrFormat ( " StealingVReg %d " , bestSpillVReg ) ;
} */
auto connNode = & mNodes [ bestSpillVReg ] ;
auto connVRegInfo = mContext - > mVRegInfo [ bestSpillVReg ] ;
bestReg = mContext - > ResizeRegister ( connVRegInfo - > mReg , 8 ) ;
connVRegInfo - > mReg = X64Reg_None ;
connNode - > mSpilled = true ;
connVRegInfo - > mSpilled = true ;
connNode - > mInGraph = false ;
for ( auto connNodeEdgeIdx : connNode - > mEdges )
{
Node * connNodeEdge = & mNodes [ connNodeEdgeIdx ] ;
connNodeEdge - > mGraphEdgeCount - - ;
}
// We insert spills at the front so we can try to "optimistically" unspill them
// after all the definite coloring is done
vregStack . insert ( vregStack . begin ( ) , bestSpillVReg ) ;
}
}
// The only way we should have failed to allocate a register is if we spilled...
BF_ASSERT ( ( bestReg ! = X64Reg_None ) /*|| (mReserveParamRegs)*/ ) ;
}
}
vregInfo - > mReg = mContext - > ResizeRegister ( bestReg , vregInfo - > mType - > mSize ) ;
if ( bestReg ! = X64Reg_None )
{
node - > mInGraph = true ;
globalRegUsedVec [ ( int ) bestReg ] = true ;
for ( auto connNodeIdx : node - > mEdges )
{
Node * connNode = & mNodes [ connNodeIdx ] ;
connNode - > mGraphEdgeCount + + ;
}
}
/*if (mContext->mDebugging)
{
dbgStr + = StrFormat ( " Reg %d " , vregInfo - > mReg ) ;
} */
}
/*if (!dbgStr.empty())
{
dbgStr + = " \n \n " ;
OutputDebugStr ( dbgStr ) ;
} */
/*bool dumpStats = true;
if ( dumpStats )
{
String str ;
str + = " Register costs: \n " ;
str + = " Mem " ;
for ( int regNum = 0 ; regNum < = X64Reg_EDI ; regNum + + )
{
str + = StrFormat ( " %4s " , X64CPURegisters : : GetRegisterName ( ( X64CPURegister ) regNum ) ) ;
}
str + = " \n " ;
for ( int liveVRregIdx = 0 ; liveVRregIdx < mContext - > mVRegInfo . size ( ) ; liveVRregIdx + + )
{
auto vregInfo = mContext - > mVRegInfo [ liveVRregIdx ] ;
auto node = & mNodes [ liveVRregIdx ] ;
String name = mContext - > ToString ( BeMCOperand : : FromVReg ( liveVRregIdx ) ) ;
str + = StrFormat ( " %24s " , name . c_str ( ) ) ;
str + = StrFormat ( " %3d " , node - > mMemCost ) ;
for ( int regNum = 0 ; regNum < = X64Reg_EDI ; regNum + + )
{
str + = StrFormat ( " %3d " , node - > mRegCost [ regNum ] ) ;
}
str + = " \n " ;
}
OutputDebugStr ( str ) ;
} */
}
bool BeMCColorizer : : Validate ( )
{
# ifdef _DEBUG
auto paramsLeft = mContext - > mParamsUsedRegs ;
for ( auto mcBlock : mContext - > mBlocks )
{
for ( auto inst : mcBlock - > mInstructions )
{
if ( paramsLeft . size ( ) = = 0 )
break ;
if ( inst - > IsMov ( ) )
{
if ( inst - > mArg1 . IsNativeReg ( ) )
{
BF_ASSERT ( inst - > mArg0 . IsVReg ( ) ) ;
BF_ASSERT ( mContext - > ResizeRegister ( inst - > mArg1 . mReg , 8 ) = = paramsLeft [ 0 ] ) ;
paramsLeft . erase ( paramsLeft . begin ( ) ) ;
auto vregInfo = mContext - > mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
if ( vregInfo - > mReg ! = X64Reg_None )
{
auto checkReg = mContext - > ResizeRegister ( vregInfo - > mReg , 8 ) ;
//auto itr = std::find(paramsLeft.begin(), paramsLeft.end(), checkReg);
//if (itr != paramsLeft.end())
if ( paramsLeft . Contains ( checkReg ) )
{
// This will happen if we have assigned a 'wrong' register to a parameter-
// a register that is not the 'natural' register (ie: rcx for param0),
// but it's a register that is already bound to another parameter which
// will be needed later. Given the 'natural' order of RCX, RDX, R8, R9,
// This is a valid order still: R10, RCX, R8, R9 since RCX will get written
// to R10 first so it won't get clobbered, but: RDX, R10, R8, R9 is not
// valid because RDX gets clobbered before it can be written to R10.
return false ;
}
}
}
}
}
}
# endif
return true ;
}
//////////////////////////////////////////////////////////////////////////
BeMCLoopDetector : : BeMCLoopDetector ( BeMCContext * context ) : mTrackingContext ( context )
{
mMCContext = context ;
mTrackingContext . mTrackKindCount = 1 ;
mTrackingContext . Init ( ( int ) mMCContext - > mMCBlockAlloc . size ( ) ) ;
mNodes . Resize ( mMCContext - > mMCBlockAlloc . size ( ) ) ;
}
void BeMCLoopDetector : : DetectLoops ( BeMCBlock * mcBlock , BeVTrackingList * predBlocksSeen )
{
2019-10-18 09:13:37 -07:00
mMCContext - > mDetectLoopIdx + + ;
2019-08-23 11:56:54 -07:00
auto node = & mNodes [ mcBlock - > mBlockIdx ] ;
auto blocksSeen = mTrackingContext . Merge ( node - > mPredBlocksSeen , predBlocksSeen ) ;
if ( blocksSeen = = node - > mPredBlocksSeen )
return ;
node - > mPredBlocksSeen = blocksSeen ;
2019-10-18 09:13:37 -07:00
//SizedArray<int, 2> addVec = { mcBlock->mBlockIdx };
//auto newBlocksSeen = mTrackingContext.Add(blocksSeen, addVec, false);
auto newBlocksSeen = mTrackingContext . Add ( blocksSeen , mcBlock - > mBlockIdx , false ) ;
2019-08-23 11:56:54 -07:00
if ( newBlocksSeen = = blocksSeen )
{
// Our ID was already set, so this is a re-entry and thus we are looped
mcBlock - > mIsLooped = true ;
}
blocksSeen = newBlocksSeen ;
for ( auto succ : mcBlock - > mSuccs )
{
DetectLoops ( succ , blocksSeen ) ;
}
}
void BeMCLoopDetector : : DetectLoops ( )
{
2019-10-18 09:13:37 -07:00
// auto blocksSeen = mTrackingContext.AllocEmptyList();
// DetectLoops(mMCContext->mBlocks[0], blocksSeen);
//
// HashSet<int> wasLooped;
// for (auto block : mMCContext->mBlocks)
// {
// if (block->mIsLooped)
// {
// wasLooped.Add(block->mBlockIdx);
// block->mIsLooped = false;
// }
// }
for ( auto block : mMCContext - > mBlocks )
{
for ( auto succ : block - > mSuccs )
{
if ( succ - > mBlockIdx < block - > mBlockIdx )
{
auto prevBlock = mMCContext - > mBlocks [ block - > mBlockIdx - 1 ] ;
//if ((!succ->mIsLooped) || (!prevBlock->mIsLooped))
{
for ( int setBlockIdx = succ - > mBlockIdx ; setBlockIdx < block - > mBlockIdx ; setBlockIdx + + )
mMCContext - > mBlocks [ setBlockIdx ] - > mIsLooped = true ;
}
}
}
}
2019-08-23 11:56:54 -07:00
}
//////////////////////////////////////////////////////////////////////////
void BeMCBlock : : AddPred ( BeMCBlock * pred )
2019-10-18 09:13:37 -07:00
{
2019-08-23 11:56:54 -07:00
if ( ! mPreds . Contains ( pred ) )
{
pred - > mSuccs . push_back ( this ) ;
mPreds . push_back ( pred ) ;
}
}
int BeMCBlock : : FindLabelInstIdx ( int labelIdx )
{
for ( int instIdx = 0 ; instIdx < ( int ) mInstructions . size ( ) ; instIdx + + )
{
auto inst = mInstructions [ instIdx ] ;
if ( ( inst - > mKind = = BeMCInstKind_Label ) & & ( inst - > mArg0 . mLabelIdx = = labelIdx ) )
return instIdx ;
}
return - 1 ;
}
//////////////////////////////////////////////////////////////////////////
BeMCContext : : BeMCContext ( BeCOFFObject * coffObject ) : mOut ( coffObject - > mTextSect . mData ) , mLivenessContext ( this ) , mVRegInitializedContext ( this ) , mColorizer ( this )
{
mLivenessContext . mTrackKindCount = 1 ;
mCOFFObject = coffObject ;
mModule = NULL ;
mBeFunction = NULL ;
mActiveBeBlock = NULL ;
mActiveBlock = NULL ;
mDbgFunction = NULL ;
mCompositeRetVRegIdx = - 1 ;
mTLSVRegIdx = - 1 ;
mStackSize = 0 ;
mCurLabelIdx = 0 ;
mCurPhiIdx = 0 ;
mMaxCallParamCount = - 1 ;
mCurDbgLoc = NULL ;
mCurVRegsInit = NULL ;
mCurVRegsLive = NULL ;
mUseBP = false ;
mInsertInstIdxRef = NULL ;
mNativeIntType = mCOFFObject - > mBeModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
mDebugging = false ;
mFailed = false ;
2019-10-18 09:13:37 -07:00
mDetectLoopIdx = 0 ;
2019-08-23 11:56:54 -07:00
}
void BeMCContext : : NotImpl ( )
{
Fail ( " Not implemented " ) ;
}
void BeMCContext : : Fail ( const StringImpl & str )
{
String errStr = StrFormat ( " Failure during codegen of %s in %s: %s " , mBeFunction - > mName . c_str ( ) , mModule - > mModuleName . c_str ( ) , str . c_str ( ) ) ;
BfpSystem_FatalError ( errStr . c_str ( ) , " FATAL ERROR " ) ;
}
void BeMCContext : : SoftFail ( const StringImpl & str , BeDbgLoc * dbgLoc )
{
if ( mFailed )
return ;
mFailed = true ;
String errStr = StrFormat ( " Failure during codegen of %s: %s " , mBeFunction - > mName . c_str ( ) , str . c_str ( ) ) ;
if ( dbgLoc ! = NULL )
{
auto dbgFile = dbgLoc - > GetDbgFile ( ) ;
if ( dbgFile ! = NULL )
errStr + = StrFormat ( " at line %d:%d in %s/%s " , dbgLoc - > mLine + 1 , dbgLoc - > mColumn + 1 , dbgFile - > mDirectory . c_str ( ) , dbgFile - > mFileName . c_str ( ) ) ;
}
mModule - > mBeIRCodeGen - > Fail ( errStr ) ;
}
String BeMCContext : : ToString ( const BeMCOperand & operand )
{
if ( operand . mKind = = BeMCOperandKind_NativeReg )
{
return String ( " % " ) + X64CPURegisters : : GetRegisterName ( ( int ) operand . mReg ) ;
}
if ( operand . IsVRegAny ( ) )
{
auto vregInfo = GetVRegInfo ( operand ) ;
String str ;
mModule - > ToString ( str , GetType ( operand ) ) ;
str + = " " ;
if ( operand . IsVRegAny ( ) )
{
auto vregInfo = GetVRegInfo ( operand ) ;
if ( operand . mKind = = BeMCOperandKind_VRegAddr )
str + = " & " ;
if ( operand . mKind = = BeMCOperandKind_VRegLoad )
str + = " * " ;
if ( vregInfo - > mDbgVariable ! = NULL )
str + = " # " + vregInfo - > mDbgVariable - > mName + StrFormat ( " /%d " , operand . mVRegIdx ) ;
else
str + = StrFormat ( " %%vreg%d " , operand . mVRegIdx ) ;
}
if ( vregInfo - > mReg ! = X64Reg_None )
{
str + = " < " ;
str + = X64CPURegisters : : GetRegisterName ( ( int ) vregInfo - > mReg ) ;
str + = " > " ;
}
else if ( vregInfo - > mForceReg )
{
str + = " <reg> " ;
}
if ( vregInfo - > mDisableR11 )
{
str + = " <NoR11> " ;
}
if ( vregInfo - > mDisableR12 )
{
str + = " <NoR12> " ;
}
if ( vregInfo - > mDisableR13 )
{
str + = " <NoR13> " ;
}
if ( vregInfo - > mDisableRAX )
{
str + = " <NoRAX> " ;
}
if ( vregInfo - > mDisableRDX )
{
str + = " <NoRDX> " ;
}
if ( vregInfo - > mDisableEx )
{
str + = " <NoEx> " ;
}
if ( vregInfo - > mForceMem )
{
str + = " <mem> " ;
}
if ( vregInfo - > mIsRetVal )
{
str + = " <retval> " ;
}
if ( vregInfo - > mForceMerge )
{
str + = " <canMerge> " ;
}
if ( vregInfo - > mRelTo )
{
str + = " ( " ;
str + = ToString ( vregInfo - > mRelTo ) ;
if ( vregInfo - > mRelOffset )
{
str + = " + " ;
str + = ToString ( vregInfo - > mRelOffset ) ;
}
if ( vregInfo - > mRelOffsetScale ! = 1 )
str + = StrFormat ( " *%d " , vregInfo - > mRelOffsetScale ) ;
str + = " ) " ;
}
return str ;
}
if ( operand . IsImmediate ( ) )
{
String str ;
switch ( operand . mKind )
{
case BeMCOperandKind_Immediate_i8 : str + = " i8 " ; break ;
case BeMCOperandKind_Immediate_i16 : str + = " i16 " ; break ;
case BeMCOperandKind_Immediate_i32 : str + = " i32 " ; break ;
case BeMCOperandKind_Immediate_i64 : str + = " i64 " ; break ;
case BeMCOperandKind_Immediate_Null :
if ( operand . mType ! = NULL )
{
mModule - > ToString ( str , operand . mType ) ;
str + = " null " ;
}
else
str + = " null " ;
return str ;
case BeMCOperandKind_Immediate_f32 : return StrFormat ( " f32 %f " , operand . mImmFloat ) ;
case BeMCOperandKind_Immediate_f32_Packed128 : return StrFormat ( " f32_packed %f " , operand . mImmFloat ) ;
case BeMCOperandKind_Immediate_f64 : return StrFormat ( " f64 %f " , operand . mImmFloat ) ;
case BeMCOperandKind_Immediate_f64_Packed128 : return StrFormat ( " f64_packed %f " , operand . mImmFloat ) ;
}
//if (operand.mImmediate < 10)
str + = StrFormat ( " %lld " , operand . mImmediate ) ;
/*else
str + = StrFormat ( " 0x%llX " , operand . mImmediate ) ; */
return str ;
}
if ( operand . mKind = = BeMCOperandKind_Block )
return " % " + operand . mBlock - > mName ;
if ( operand . mKind = = BeMCOperandKind_Label )
return StrFormat ( " %%label%d " , operand . mLabelIdx ) ;
if ( operand . mKind = = BeMCOperandKind_CmpKind )
return BeDumpContext : : ToString ( operand . mCmpKind ) ;
if ( operand . mKind = = BeMCOperandKind_CmpResult )
{
String result = StrFormat ( " %%CmpResult%d " , operand . mCmpResultIdx ) ;
auto & cmpResult = mCmpResults [ operand . mCmpResultIdx ] ;
if ( cmpResult . mResultVRegIdx ! = - 1 )
result + = StrFormat ( " <vreg%d> " , cmpResult . mResultVRegIdx ) ;
result + = " " ;
result + = BeDumpContext : : ToString ( cmpResult . mCmpKind ) ;
return result ;
}
if ( operand . mKind = = BeMCOperandKind_NotResult )
{
auto mcResult = GetOperand ( operand . mNotResult - > mValue , true , true ) ;
String result = " NOT " ;
result + = ToString ( mcResult ) ;
return result ;
}
if ( operand . mKind = = BeMCOperandKind_Symbol )
return mCOFFObject - > mSymbols [ operand . mSymbolIdx ] - > mName ;
if ( operand . mKind = = BeMCOperandKind_SymbolAddr )
return " & " + mCOFFObject - > mSymbols [ operand . mSymbolIdx ] - > mName ;
if ( operand . mKind = = BeMCOperandKind_MemSetInfo )
return StrFormat ( " size=%d val=%d align=%d " , operand . mMemSetInfo . mSize , operand . mMemSetInfo . mValue , operand . mMemSetInfo . mAlign ) ;
if ( operand . mKind = = BeMCOperandKind_MemCpyInfo )
return StrFormat ( " size=%d align=%d " , operand . mMemCpyInfo . mSize , operand . mMemCpyInfo . mAlign ) ;
if ( operand . mKind = = BeMCOperandKind_VRegPair )
{
String str = " ( " ;
str + = ToString ( BeMCOperand : : FromEncoded ( operand . mVRegPair . mVRegIdx0 ) ) ;
str + = " , " ;
str + = ToString ( BeMCOperand : : FromEncoded ( operand . mVRegPair . mVRegIdx1 ) ) ;
str + = " ) " ;
return str ;
}
if ( operand . mKind = = BeMCOperandKind_Phi )
return StrFormat ( " %%PHI%d " , operand . mPhi - > mIdx ) ;
if ( operand . mKind = = BeMCOperandKind_PreserveFlag )
{
if ( operand . mPreserveFlag = = BeMCPreserveFlag_NoRestore )
return " PreserveFlag:NoReturn " ;
}
if ( operand . mKind = = BeMCOperandKind_ConstAgg )
{
BeDumpContext dumpContext ;
dumpContext . mModule = mBeFunction - > mModule ;
String str = " const " ;
str + = dumpContext . ToString ( operand . mConstant ) ;
return str ;
}
return " ??? " ;
}
BeMCOperand BeMCContext : : GetOperand ( BeValue * value , bool allowMetaResult , bool allowFail )
{
if ( value = = NULL )
return BeMCOperand ( ) ;
switch ( value - > GetTypeId ( ) )
{
case BeGlobalVariable : : TypeId :
{
auto globalVar = ( BeGlobalVariable * ) value ;
if ( ( globalVar - > mIsTLS ) & & ( mTLSVRegIdx = = - 1 ) )
{
auto tlsVReg = AllocVirtualReg ( mNativeIntType ) ;
auto vregInfo = GetVRegInfo ( tlsVReg ) ;
vregInfo - > mMustExist = true ;
vregInfo - > mForceReg = true ;
mTLSVRegIdx = tlsVReg . mVRegIdx ;
}
auto sym = mCOFFObject - > GetSymbol ( globalVar ) ;
if ( sym ! = NULL )
{
BeMCOperand mcOperand ;
mcOperand . mKind = BeMCOperandKind_SymbolAddr ;
mcOperand . mSymbolIdx = sym - > mIdx ;
return mcOperand ;
}
}
break ;
case BeCastConstant : : TypeId :
{
auto constant = ( BeCastConstant * ) value ;
BeMCOperand mcOperand ;
auto relTo = GetOperand ( constant - > mTarget ) ;
if ( relTo . mKind = = BeMCOperandKind_Immediate_Null )
{
mcOperand . mKind = BeMCOperandKind_Immediate_Null ;
mcOperand . mType = constant - > mType ;
return mcOperand ;
}
mcOperand = AllocVirtualReg ( constant - > mType ) ;
auto vregInfo = GetVRegInfo ( mcOperand ) ;
vregInfo - > mDefOnFirstUse = true ;
vregInfo - > mRelTo = relTo ;
vregInfo - > mIsExpr = true ;
return mcOperand ;
}
break ;
case BeConstant : : TypeId :
{
auto constant = ( BeConstant * ) value ;
BeMCOperand mcOperand ;
switch ( constant - > mType - > mTypeCode )
{
case BeTypeCode_Boolean :
case BeTypeCode_Int8 : mcOperand . mKind = BeMCOperandKind_Immediate_i8 ; break ;
case BeTypeCode_Int16 : mcOperand . mKind = BeMCOperandKind_Immediate_i16 ; break ;
case BeTypeCode_Int32 : mcOperand . mKind = BeMCOperandKind_Immediate_i32 ; break ;
case BeTypeCode_Int64 : mcOperand . mKind = BeMCOperandKind_Immediate_i64 ; break ;
case BeTypeCode_Float :
mcOperand . mImmFloat = constant - > mDouble ;
mcOperand . mKind = BeMCOperandKind_Immediate_f32 ;
return mcOperand ;
case BeTypeCode_Double :
mcOperand . mImmFloat = constant - > mDouble ;
mcOperand . mKind = BeMCOperandKind_Immediate_f64 ;
return mcOperand ;
case BeTypeCode_Pointer :
{
if ( constant - > mTarget = = NULL )
{
mcOperand . mKind = BeMCOperandKind_Immediate_Null ;
mcOperand . mType = constant - > mType ;
return mcOperand ;
}
else
{
auto relTo = GetOperand ( constant - > mTarget ) ;
if ( relTo . mKind = = BeMCOperandKind_Immediate_Null )
{
mcOperand . mKind = BeMCOperandKind_Immediate_Null ;
mcOperand . mType = constant - > mType ;
return mcOperand ;
}
mcOperand = AllocVirtualReg ( constant - > mType ) ;
auto vregInfo = GetVRegInfo ( mcOperand ) ;
vregInfo - > mDefOnFirstUse = true ;
vregInfo - > mRelTo = relTo ;
vregInfo - > mIsExpr = true ;
return mcOperand ;
}
}
break ;
case BeTypeCode_Struct :
case BeTypeCode_SizedArray :
mcOperand . mImmediate = constant - > mInt64 ;
mcOperand . mKind = BeMCOperandKind_Immediate_i64 ;
break ;
default :
NotImpl ( ) ;
}
mcOperand . mImmediate = constant - > mInt64 ;
return mcOperand ;
}
break ;
case BeStructConstant : : TypeId :
{
auto structConstant = ( BeStructConstant * ) value ;
BeMCOperand mcOperand ;
mcOperand . mKind = BeMCOperandKind_ConstAgg ;
mcOperand . mConstant = structConstant ;
return mcOperand ;
}
case BeGEPConstant : : TypeId :
{
auto gepConstant = ( BeGEPConstant * ) value ;
auto mcVal = GetOperand ( gepConstant - > mTarget ) ;
BePointerType * ptrType = ( BePointerType * ) GetType ( mcVal ) ;
BF_ASSERT ( ptrType - > mTypeCode = = BeTypeCode_Pointer ) ;
auto result = mcVal ;
// We assume we never do both an idx0 and idx1 at once. Fix if we change that.
int byteOffset = 0 ;
BeType * elementType = NULL ;
byteOffset + = gepConstant - > mIdx0 * ptrType - > mElementType - > mSize ;
if ( ptrType - > mElementType - > mTypeCode = = BeTypeCode_Struct )
{
BeStructType * structType = ( BeStructType * ) ptrType - > mElementType ;
auto & structMember = structType - > mMembers [ gepConstant - > mIdx1 ] ;
elementType = structMember . mType ;
byteOffset = structMember . mByteOffset ;
}
else
{
BF_ASSERT ( ptrType - > mElementType - > mTypeCode = = BeTypeCode_SizedArray ) ;
auto arrayType = ( BeSizedArrayType * ) ptrType - > mElementType ;
elementType = arrayType - > mElementType ;
byteOffset = gepConstant - > mIdx1 * elementType - > mSize ;
}
auto elementPtrType = mModule - > mContext - > GetPointerTo ( elementType ) ;
result = AllocRelativeVirtualReg ( elementPtrType , result , GetImmediate ( byteOffset ) , 1 ) ;
// The def is primary to create a single 'master location' for the GEP vreg to become legalized before use
auto vregInfo = GetVRegInfo ( result ) ;
vregInfo - > mDefOnFirstUse = true ;
result . mKind = BeMCOperandKind_VReg ;
return result ;
}
break ;
case BeFunction : : TypeId :
{
auto sym = mCOFFObject - > GetSymbol ( value ) ;
BF_ASSERT ( sym ! = NULL ) ;
if ( sym ! = NULL )
{
BeMCOperand mcOperand ;
mcOperand . mKind = BeMCOperandKind_SymbolAddr ;
mcOperand . mSymbolIdx = sym - > mIdx ;
return mcOperand ;
}
}
break ;
case BeCallInst : : TypeId :
{
auto callInst = ( BeCallInst * ) value ;
if ( callInst - > mInlineResult ! = NULL )
return GetOperand ( callInst - > mInlineResult ) ;
}
break ;
case BeDbgVariable : : TypeId :
{
}
}
BeMCOperand * operandPtr = NULL ;
mValueToOperand . TryGetValue ( value , & operandPtr ) ;
//auto itr = mValueToOperand.find(value);
if ( ! allowFail )
{
BF_ASSERT ( operandPtr ! = NULL ) ;
}
if ( operandPtr = = NULL )
{
if ( allowFail )
return BeMCOperand ( ) ;
BeMCOperand mcOperand ;
mcOperand . mKind = BeMCOperandKind_Immediate_i64 ;
mcOperand . mImmediate = 0 ;
return mcOperand ;
}
auto operand = * operandPtr ;
if ( ( operand . mKind = = BeMCOperandKind_Phi ) & & ( ! allowMetaResult ) )
{
auto phi = operand . mPhi ;
int phiInstIdx = 0 ;
auto mcBlock = phi - > mBlock ;
for ( auto instIdx = 0 ; instIdx < mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( inst - > mKind = = BeMCInstKind_DefPhi )
{
BF_ASSERT ( inst - > mArg0 . mPhi = = phi ) ;
phiInstIdx = instIdx ;
RemoveInst ( mcBlock , phiInstIdx ) ;
break ;
}
}
SetAndRestoreValue < BeMCBlock * > prevBlock ( mActiveBlock , mcBlock ) ;
SetAndRestoreValue < int * > prevInstIdxRef ( mInsertInstIdxRef , & phiInstIdx ) ;
auto resultType = value - > GetType ( ) ;
auto result = AllocVirtualReg ( resultType ) ;
auto vregInfo = GetVRegInfo ( result ) ;
vregInfo - > mHasDynLife = true ; // No specific 'def' location
mValueToOperand [ value ] = result ;
if ( resultType - > mTypeCode = = BeTypeCode_Boolean )
{
CreateDefineVReg ( result ) ;
BeMCOperand falseLabel = BeMCOperand : : FromLabel ( mCurLabelIdx + + ) ;
BeMCOperand trueLabel = BeMCOperand : : FromLabel ( mCurLabelIdx + + ) ;
BeMCOperand endLabel = BeMCOperand : : FromLabel ( mCurLabelIdx + + ) ;
CreateCondBr ( mActiveBlock , operand , trueLabel , falseLabel ) ;
AllocInst ( BeMCInstKind_Label , falseLabel ) ;
AllocInst ( BeMCInstKind_Mov , result , BeMCOperand : : FromImmediate ( 0 ) ) ;
AllocInst ( BeMCInstKind_Br , endLabel ) ;
AllocInst ( BeMCInstKind_Label , trueLabel ) ;
AllocInst ( BeMCInstKind_Mov , result , BeMCOperand : : FromImmediate ( 1 ) ) ;
AllocInst ( BeMCInstKind_Label , endLabel ) ;
}
else
{
// Attempt to find common ancestor to insert a 'def' at
SizedArray < BeMCBlock * , 16 > blockSearch ;
blockSearch . reserve ( phi - > mValues . size ( ) ) ;
BeMCBlock * lowestBlock = NULL ;
for ( auto & phiValue : phi - > mValues )
{
if ( ( lowestBlock = = NULL ) | | ( phiValue . mBlockFrom - > mBlockIdx < lowestBlock - > mBlockIdx ) )
lowestBlock = phiValue . mBlockFrom ;
blockSearch . push_back ( phiValue . mBlockFrom ) ;
}
while ( true )
{
bool allMatched = true ;
bool didWork = false ;
for ( int searchIdx = 0 ; searchIdx < ( int ) blockSearch . size ( ) ; searchIdx + + )
{
auto & blockRef = blockSearch [ searchIdx ] ;
if ( blockRef ! = lowestBlock )
{
allMatched = false ;
for ( auto & pred : blockRef - > mPreds )
{
// Try find a block closer to start, but not below the current lowestBlock
if ( ( pred - > mBlockIdx > = lowestBlock - > mBlockIdx ) & & ( pred - > mBlockIdx < blockRef - > mBlockIdx ) )
{
blockRef = pred ;
didWork = true ;
}
}
}
}
if ( allMatched )
{
SetAndRestoreValue < BeMCBlock * > prevActiveBlock ( mActiveBlock , lowestBlock ) ;
SetAndRestoreValue < int * > prevInstIdxRef ( mInsertInstIdxRef , NULL ) ;
auto inst = CreateDefineVReg ( result ) ;
inst - > mVRegsInitialized = NULL ;
inst - > mDbgLoc = NULL ;
break ;
}
if ( ! didWork )
{
BeMCBlock * nextLowestBlock = NULL ;
// Find the next candidate block
for ( auto & blockRef : blockSearch )
{
for ( auto & pred : blockRef - > mPreds )
{
if ( pred - > mBlockIdx < lowestBlock - > mBlockIdx )
{
if ( ( nextLowestBlock = = NULL ) | | ( pred - > mBlockIdx > nextLowestBlock - > mBlockIdx ) )
nextLowestBlock = pred ;
}
}
}
if ( nextLowestBlock = = NULL )
break ;
lowestBlock = nextLowestBlock ;
}
}
BeMCOperand doneLabel = BeMCOperand : : FromLabel ( mCurLabelIdx + + ) ;
CreatePhiAssign ( mActiveBlock , operand , result , doneLabel ) ;
// Don't use an explicit dbgLoc
SetAndRestoreValue < BeDbgLoc * > prevDbgLoc ( mCurDbgLoc , NULL ) ;
AllocInst ( BeMCInstKind_Label , doneLabel ) ;
}
return result ;
}
if ( ( operand . mKind = = BeMCOperandKind_CmpResult ) & & ( ! allowMetaResult ) )
{
auto & cmpResult = mCmpResults [ operand . mCmpResultIdx ] ;
if ( cmpResult . mResultVRegIdx = = - 1 )
{
// Create the vreg now, and insert the CmpToBool during legalization
BeType * boolType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Boolean ) ;
operand = AllocVirtualReg ( boolType ) ;
cmpResult . mResultVRegIdx = operand . mVRegIdx ;
auto vregInfo = GetVRegInfo ( operand ) ;
vregInfo - > mDefOnFirstUse = true ;
}
operand = BeMCOperand : : FromVReg ( cmpResult . mResultVRegIdx ) ;
}
if ( ( operand . mKind = = BeMCOperandKind_NotResult ) & & ( ! allowMetaResult ) )
{
auto mcValue = GetOperand ( operand . mNotResult - > mValue , false , allowFail ) ;
operand = AllocVirtualReg ( GetType ( mcValue ) ) ;
CreateDefineVReg ( operand ) ;
AllocInst ( BeMCInstKind_Mov , operand , mcValue ) ;
BeMCOperand xorVal ;
xorVal . mKind = BeMCOperandKind_Immediate_i8 ;
xorVal . mImmediate = 0x1 ;
AllocInst ( BeMCInstKind_Xor , operand , xorVal ) ;
}
return operand ;
}
BeType * BeMCContext : : GetType ( const BeMCOperand & operand )
{
if ( operand . mKind = = BeMCOperandKind_NativeReg )
{
if ( ( operand . mReg > = X64Reg_RAX ) & & ( operand . mReg < = X64Reg_EFL ) )
return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
if ( ( operand . mReg > = X64Reg_EAX ) & & ( operand . mReg < = X64Reg_R15D ) )
return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int32 ) ;
if ( ( operand . mReg > = X64Reg_AX ) & & ( operand . mReg < = X64Reg_R15W ) )
return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int16 ) ;
if ( ( operand . mReg > = X64Reg_AL ) & & ( operand . mReg < = X64Reg_R15B ) )
return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int8 ) ;
if ( ( operand . mReg > = X64Reg_XMM0_f64 ) & & ( operand . mReg < = X64Reg_XMM15_f64 ) )
return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Double ) ;
if ( ( operand . mReg > = X64Reg_XMM0_f32 ) & & ( operand . mReg < = X64Reg_XMM15_f32 ) )
return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Float ) ;
}
if ( operand . mKind = = BeMCOperandKind_VReg )
return mVRegInfo [ operand . mVRegIdx ] - > mType ;
if ( operand . mKind = = BeMCOperandKind_VRegAddr )
return mModule - > mContext - > GetPointerTo ( mVRegInfo [ operand . mVRegIdx ] - > mType ) ;
if ( operand . mKind = = BeMCOperandKind_VRegLoad )
{
auto type = mVRegInfo [ operand . mVRegIdx ] - > mType ;
BF_ASSERT ( type - > IsPointer ( ) ) ;
return ( ( BePointerType * ) type ) - > mElementType ;
}
switch ( operand . mKind )
{
case BeMCOperandKind_Immediate_i8 : return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int8 ) ; break ;
case BeMCOperandKind_Immediate_i16 : return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int16 ) ; break ;
case BeMCOperandKind_Immediate_i32 : return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int32 ) ; break ;
case BeMCOperandKind_Immediate_i64 : return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ; break ;
case BeMCOperandKind_Immediate_Null : return operand . mType ; break ;
case BeMCOperandKind_Immediate_f32 :
case BeMCOperandKind_Immediate_f32_Packed128 : return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Float ) ; break ;
case BeMCOperandKind_Immediate_f64 :
case BeMCOperandKind_Immediate_f64_Packed128 : return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Double ) ; break ;
}
if ( operand . mKind = = BeMCOperandKind_SymbolAddr )
{
auto symbol = mCOFFObject - > mSymbols [ operand . mSymbolIdx ] ;
if ( symbol - > mType = = NULL )
return NULL ;
return mModule - > mContext - > GetPointerTo ( symbol - > mType ) ;
}
if ( operand . mKind = = BeMCOperandKind_Symbol )
{
auto symbol = mCOFFObject - > mSymbols [ operand . mSymbolIdx ] ;
return symbol - > mType ;
}
if ( operand . mKind = = BeMCOperandKind_CmpResult )
{
return mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Boolean ) ;
}
if ( operand . mKind = = BeMCOperandKind_ConstAgg )
{
return operand . mConstant - > mType ;
}
return NULL ;
}
bool BeMCContext : : AreTypesEquivalent ( BeType * type0 , BeType * type1 )
{
if ( ( type0 - > IsFloat ( ) ) ! = ( type1 - > IsFloat ( ) ) )
return false ;
return type0 - > mSize = = type1 - > mSize ;
}
void BeMCContext : : AddRelRefs ( BeMCOperand & operand , int refCount )
{
if ( ! operand . IsVRegAny ( ) )
return ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
vregInfo - > mRefCount + = refCount ;
if ( vregInfo - > mRelTo )
AddRelRefs ( vregInfo - > mRelTo , vregInfo - > mRefCount + refCount ) ;
if ( vregInfo - > mRelOffset )
AddRelRefs ( vregInfo - > mRelOffset , vregInfo - > mRefCount + refCount ) ;
}
int BeMCContext : : FindSafeInstInsertPos ( int instIdx , bool forceSafeCheck )
{
auto inst = mActiveBlock - > mInstructions [ instIdx ] ;
bool doSafeCheck = false ;
if ( forceSafeCheck )
doSafeCheck = true ;
if ( ( inst - > mKind = = BeMCInstKind_Call ) | |
( inst - > mKind = = BeMCInstKind_MemSet ) | |
( inst - > mKind = = BeMCInstKind_MemCpy ) )
doSafeCheck = true ;
if ( ( inst - > IsMov ( ) ) & & ( inst - > mArg0 . IsNativeReg ( ) ) )
doSafeCheck = true ;
if ( ! doSafeCheck )
return instIdx ;
// If we're in a CALL then move this Def before the param loads
int bestIdx = instIdx ;
while ( bestIdx > 0 )
{
inst = mActiveBlock - > mInstructions [ bestIdx - 1 ] ;
bestIdx - - ;
if ( inst - > mKind = = BeMCInstKind_PreserveVolatiles )
return bestIdx ;
}
return bestIdx ;
}
BeMCInst * BeMCContext : : AllocInst ( int insertIdx )
{
auto mcInst = mAlloc . Alloc < BeMCInst > ( ) ;
mcInst - > mKind = BeMCInstKind_None ;
mcInst - > mDbgLoc = mCurDbgLoc ;
mcInst - > mVRegsInitialized = mCurVRegsInit ;
mcInst - > mLiveness = mCurVRegsLive ;
if ( ( insertIdx = = - 1 ) & & ( mInsertInstIdxRef ! = NULL ) )
insertIdx = ( * mInsertInstIdxRef ) + + ;
if ( insertIdx = = - 1 )
mActiveBlock - > mInstructions . push_back ( mcInst ) ;
else
mActiveBlock - > mInstructions . Insert ( insertIdx , mcInst ) ;
return mcInst ;
}
BeMCInst * BeMCContext : : AllocInst ( BeMCInstKind instKind , int insertIdx )
{
auto mcInst = AllocInst ( insertIdx ) ;
mcInst - > mKind = instKind ;
return mcInst ;
}
BeMCInst * BeMCContext : : AllocInst ( BeMCInstKind instKind , const BeMCOperand & arg0 , int insertIdx )
{
auto mcInst = AllocInst ( insertIdx ) ;
mcInst - > mKind = instKind ;
mcInst - > mArg0 = arg0 ;
return mcInst ;
}
BeMCInst * BeMCContext : : AllocInst ( BeMCInstKind instKind , const BeMCOperand & arg0 , const BeMCOperand & arg1 , int insertIdx )
{
auto mcInst = AllocInst ( insertIdx ) ;
mcInst - > mKind = instKind ;
mcInst - > mArg0 = arg0 ;
mcInst - > mArg1 = arg1 ;
return mcInst ;
}
void BeMCContext : : RemoveInst ( BeMCBlock * block , int instIdx , bool needChangesMerged )
{
// If neither the instruction before or after this one shares the vregsInitialized flags, then we need to
// merge down our Changes to the next instruction
auto inst = block - > mInstructions [ instIdx ] ;
if ( instIdx > 0 )
{
auto prevInst = block - > mInstructions [ instIdx - 1 ] ;
if ( prevInst - > mVRegsInitialized = = inst - > mVRegsInitialized )
needChangesMerged = false ;
}
if ( needChangesMerged )
{
if ( instIdx < ( int ) block - > mInstructions . size ( ) - 1 )
{
auto nextInst = block - > mInstructions [ instIdx + 1 ] ;
if ( ( inst - > mVRegsInitialized ! = NULL ) & & ( nextInst - > mVRegsInitialized ! = NULL ) )
{
nextInst - > mVRegsInitialized = mVRegInitializedContext . MergeChanges ( nextInst - > mVRegsInitialized , inst - > mVRegsInitialized ) ;
}
}
}
block - > mInstructions . RemoveAt ( instIdx ) ;
}
BeMCOperand BeMCContext : : GetCallArgVReg ( int argIdx , BeTypeCode typeCode )
{
int pIdx = argIdx ;
BeMCNativeTypeCode vregType = BeMCNativeTypeCode_Int64 ;
switch ( typeCode )
{
case BeTypeCode_Int8 :
vregType = BeMCNativeTypeCode_Int8 ;
break ;
case BeTypeCode_Int16 :
vregType = BeMCNativeTypeCode_Int16 ;
break ;
case BeTypeCode_Int32 :
vregType = BeMCNativeTypeCode_Int32 ;
break ;
case BeTypeCode_Int64 :
vregType = BeMCNativeTypeCode_Int64 ;
break ;
case BeTypeCode_Float :
vregType = BeMCNativeTypeCode_Float ;
break ;
case BeTypeCode_Double :
vregType = BeMCNativeTypeCode_Double ;
break ;
default :
typeCode = BeTypeCode_Int64 ;
vregType = BeMCNativeTypeCode_Int64 ;
break ;
}
Array < int > & callArgVRegs = mCallArgVRegs [ vregType ] ;
while ( pIdx > = ( int ) callArgVRegs . size ( ) )
callArgVRegs . push_back ( - 1 ) ;
if ( callArgVRegs [ pIdx ] = = - 1 )
{
auto nativeType = mModule - > mContext - > GetPrimitiveType ( typeCode ) ;
auto nativePtrType = mModule - > mContext - > GetPointerTo ( nativeType ) ;
auto mcArg = AllocVirtualReg ( nativePtrType ) ;
auto vregInfo = mVRegInfo [ mcArg . mVRegIdx ] ;
vregInfo - > mMustExist = true ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = BeMCOperand : : FromReg ( X64Reg_RSP ) ;
vregInfo - > mRelOffset = BeMCOperand : : FromImmediate ( argIdx * 8 ) ;
callArgVRegs [ pIdx ] = mcArg . mVRegIdx ;
mcArg . mKind = BeMCOperandKind_VRegLoad ;
return mcArg ;
}
else
{
auto mcArg = BeMCOperand : : FromVReg ( callArgVRegs [ pIdx ] ) ;
mcArg . mKind = BeMCOperandKind_VRegLoad ;
return mcArg ;
}
}
BeMCOperand BeMCContext : : CreateCall ( const BeMCOperand & func , const SizedArrayImpl < BeValue * > & args , BeType * retType , BfIRCallingConv callingConv , bool structRet , bool noReturn , bool isVarArg )
{
SizedArray < BeMCOperand , 4 > opArgs ;
for ( auto itr = args . begin ( ) ; itr ! = args . end ( ) ; + + itr )
{
auto & arg = * itr ;
opArgs . push_back ( GetOperand ( arg ) ) ;
}
return CreateCall ( func , opArgs , retType , callingConv , structRet , noReturn , isVarArg ) ;
}
BeMCOperand BeMCContext : : CreateLoad ( const BeMCOperand & mcTarget )
{
BeMCOperand result ;
auto loadedTarget = BeMCOperand : : ToLoad ( mcTarget ) ;
auto targetType = GetType ( loadedTarget ) ;
result = AllocVirtualReg ( targetType ) ;
auto vregInfo = GetVRegInfo ( result ) ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = loadedTarget ;
result . mKind = BeMCOperandKind_VReg ;
AllocInst ( BeMCInstKind_DefLoad , result ) ;
return result ;
}
void BeMCContext : : CreateStore ( BeMCInstKind instKind , const BeMCOperand & val , const BeMCOperand & ptr )
{
BeMCOperand mcVal = val ;
BeMCOperand mcPtr = ptr ;
if ( mcVal . mKind = = BeMCOperandKind_ConstAgg )
{
bool needsDecompose = false ;
if ( auto arrayConst = BeValueDynCast < BeStructConstant > ( mcVal . mConstant ) )
{
for ( auto & val : arrayConst - > mMemberValues )
{
if ( auto globalVar = BeValueDynCast < BeGlobalVariable > ( val ) )
{
needsDecompose = true ;
}
else if ( auto castConst = BeValueDynCast < BeCastConstant > ( val ) )
{
needsDecompose = true ;
}
}
if ( needsDecompose )
{
int offset = 0 ;
auto arrayType = arrayConst - > GetType ( ) ;
BF_ASSERT ( arrayType - > IsSizedArray ( ) ) ;
auto sizedArrayType = ( BeSizedArrayType * ) arrayType ;
for ( auto & val : arrayConst - > mMemberValues )
{
auto destOperand = AllocVirtualReg ( mModule - > mContext - > GetPointerTo ( sizedArrayType - > mElementType ) ) ;
auto vregInfo = GetVRegInfo ( destOperand ) ;
vregInfo - > mDefOnFirstUse = true ;
vregInfo - > mRelTo = mcPtr ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelOffset = BeMCOperand : : FromImmediate ( offset ) ;
destOperand . mKind = BeMCOperandKind_VRegLoad ;
auto elementVal = GetOperand ( val ) ;
AllocInst ( instKind , destOperand , elementVal ) ;
offset + = sizedArrayType - > mElementType - > mSize ;
}
return ;
}
}
}
// Addr mov infers something like a "int* intPtr = &intVal"
if ( mcVal . mKind = = BeMCOperandKind_VRegAddr )
{
auto vregInfo = GetVRegInfo ( mcVal ) ;
vregInfo - > mForceMem = true ;
CheckForce ( vregInfo ) ;
}
if ( mcPtr . mKind = = BeMCOperandKind_VRegAddr )
{
mcPtr . mKind = BeMCOperandKind_VReg ;
AllocInst ( instKind , mcPtr , mcVal ) ;
}
else if ( mcPtr . mKind = = BeMCOperandKind_VReg )
{
mcPtr . mKind = BeMCOperandKind_VRegLoad ;
AllocInst ( instKind , mcPtr , mcVal ) ;
}
else if ( mcPtr . mKind = = BeMCOperandKind_SymbolAddr )
{
mcPtr . mKind = BeMCOperandKind_Symbol ;
AllocInst ( instKind , mcPtr , mcVal ) ;
}
else
{
mcPtr = CreateLoad ( mcPtr ) ;
AllocInst ( instKind , mcPtr , mcVal ) ;
}
}
BeMCOperand BeMCContext : : CreateCall ( const BeMCOperand & func , const SizedArrayImpl < BeMCOperand > & args , BeType * retType , BfIRCallingConv callingConv , bool structRet , bool noReturn , bool isVarArg )
{
BeMCOperand mcResult ;
//TODO: Allow user to directly specify ret addr with "sret" attribute
int argOfs = 0 ;
X64CPURegister compositeRetReg = X64Reg_None ;
bool flipFirstRegs = ( structRet ) & & ( callingConv = = BfIRCallingConv_ThisCall ) ;
if ( ( retType ! = NULL ) & & ( retType - > IsComposite ( ) ) )
{
mcResult = AllocVirtualReg ( retType ) ;
auto vregInfo = GetVRegInfo ( mcResult ) ;
vregInfo - > mMustExist = true ;
CreateDefineVReg ( mcResult ) ;
// 'this' always goes in RCX, so push compositeRetReg out by one
compositeRetReg = ( callingConv = = BfIRCallingConv_ThisCall ) ? X64Reg_RDX : X64Reg_RCX ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( compositeRetReg ) , BeMCOperand : : FromVRegAddr ( mcResult . mVRegIdx ) ) ;
argOfs = 1 ;
}
bool didPreserveRegs = false ;
auto _AddPreserveRegs = [ & ] ( )
{
if ( noReturn )
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromPreserveFlag ( BeMCPreserveFlag_NoRestore ) ) ;
else
AllocInst ( BeMCInstKind_PreserveVolatiles ) ;
didPreserveRegs = true ;
} ;
int argCount = ( int ) args . size ( ) + argOfs ;
int dynStackSize = 0 ;
// if (mUseBP)
// {
// dynStackSize = BF_MAX(4, argCount) * 8;
// dynStackSize = BF_ALIGN(dynStackSize, 16);
// }
if ( dynStackSize > 0 )
AllocInst ( BeMCInstKind_Sub , BeMCOperand : : FromReg ( X64Reg_RSP ) , BeMCOperand : : FromImmediate ( dynStackSize ) ) ;
struct _ShadowReg
{
X64CPURegister mFReg ;
X64CPURegister mIReg ;
} ;
SizedArray < _ShadowReg , 8 > shadowRegs ;
2019-11-26 13:11:17 -08:00
BF_ASSERT ( mMaxCallParamCount > = argCount ) ;
2019-08-23 11:56:54 -07:00
mMaxCallParamCount = BF_MAX ( mMaxCallParamCount , argCount ) ;
for ( int argIdx = args . size ( ) - 1 ; argIdx > = 0 ; argIdx - - )
{
if ( ( argIdx = = 0 ) & & ( compositeRetReg = = X64Reg_RDX ) )
argOfs = 0 ;
auto mcValue = args [ argIdx ] ;
auto argType = GetType ( mcValue ) ;
X64CPURegister useReg = X64Reg_None ;
int useArgIdx = argIdx + argOfs ;
if ( argType - > IsFloat ( ) )
{
switch ( useArgIdx )
{
case 0 :
useReg = ( argType - > mTypeCode = = BeTypeCode_Float ) ? X64Reg_XMM0_f32 : X64Reg_XMM0_f64 ;
break ;
case 1 :
useReg = ( argType - > mTypeCode = = BeTypeCode_Float ) ? X64Reg_XMM1_f32 : X64Reg_XMM1_f64 ;
break ;
case 2 :
useReg = ( argType - > mTypeCode = = BeTypeCode_Float ) ? X64Reg_XMM2_f32 : X64Reg_XMM2_f64 ;
break ;
case 3 :
useReg = ( argType - > mTypeCode = = BeTypeCode_Float ) ? X64Reg_XMM3_f32 : X64Reg_XMM3_f64 ;
break ;
}
if ( isVarArg )
{
X64CPURegister shadowReg = X64Reg_None ;
switch ( useArgIdx )
{
case 0 :
shadowReg = ! flipFirstRegs ? X64Reg_RCX : X64Reg_RDX ;
break ;
case 1 :
shadowReg = ! flipFirstRegs ? X64Reg_RDX : X64Reg_RCX ;
break ;
case 2 :
shadowReg = X64Reg_R8 ;
break ;
case 3 :
shadowReg = X64Reg_R9 ;
break ;
}
if ( ( shadowReg ! = X64Reg_None ) & & ( useReg ! = X64Reg_None ) )
{
shadowRegs . push_back ( _ShadowReg { useReg , shadowReg } ) ;
}
}
}
else
{
switch ( useArgIdx )
{
case 0 :
useReg = ! flipFirstRegs ? X64Reg_RCX : X64Reg_RDX ;
break ;
case 1 :
useReg = ! flipFirstRegs ? X64Reg_RDX : X64Reg_RCX ;
break ;
case 2 :
useReg = X64Reg_R8 ;
break ;
case 3 :
useReg = X64Reg_R9 ;
break ;
}
}
if ( ( ! argType - > IsComposite ( ) ) & & ( ! isVarArg ) ) // VarArg uses full 64 bits
useReg = ResizeRegister ( useReg , argType - > mSize ) ;
if ( mcValue . mKind = = BeMCOperandKind_VRegAddr )
{
auto vregInfo = GetVRegInfo ( mcValue ) ;
vregInfo - > mForceMem = true ;
CheckForce ( vregInfo ) ;
}
if ( useReg ! = X64Reg_None )
{
// We want to do the non-register params before the PreserveRegs call,
// because those may be a memory-to-memory mov, which will result in
// a temporary variable which will allocate a register which may
// conflict with our param regs
if ( ! didPreserveRegs )
_AddPreserveRegs ( ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( useReg ) , mcValue ) ;
}
else
{
auto useTypeCode = argType - > mTypeCode ;
if ( isVarArg )
{
if ( argType - > IsFloat ( ) )
useTypeCode = BeTypeCode_Double ;
else
useTypeCode = BeTypeCode_Int64 ;
}
auto callArgVReg = GetCallArgVReg ( useArgIdx , useTypeCode ) ;
// Do a 'def' for every usage, to clear out the 'init' at all paths
CreateDefineVReg ( BeMCOperand : : FromVReg ( callArgVReg . mVRegIdx ) ) ;
if ( argType - > IsComposite ( ) )
{
BF_ASSERT ( mcValue . mKind = = BeMCOperandKind_VReg ) ;
AllocInst ( BeMCInstKind_Mov , callArgVReg , BeMCOperand : : FromVRegAddr ( mcValue . mVRegIdx ) ) ;
}
else
AllocInst ( BeMCInstKind_Mov , callArgVReg , mcValue ) ;
}
}
if ( ! didPreserveRegs )
_AddPreserveRegs ( ) ;
auto mcFunc = func ;
for ( auto & shadowReg : shadowRegs ) // Do float shadowing
{
AllocInst ( BeMCInstKind_MovRaw , BeMCOperand : : FromReg ( shadowReg . mIReg ) , BeMCOperand : : FromReg ( shadowReg . mFReg ) ) ;
}
AllocInst ( BeMCInstKind_Call , mcFunc ) ;
if ( dynStackSize > 0 )
AllocInst ( BeMCInstKind_Add , BeMCOperand : : FromReg ( X64Reg_RSP ) , BeMCOperand : : FromImmediate ( dynStackSize ) ) ;
if ( ( ! mcResult ) & & ( retType ! = NULL ) & & ( retType - > mTypeCode ! = BeTypeCode_None ) )
{
mcResult = AllocVirtualReg ( retType ) ;
CreateDefineVReg ( mcResult ) ;
X64CPURegister resultReg ;
if ( retType - > IsFloat ( ) )
{
resultReg = ResizeRegister ( X64Reg_XMM0_f64 , retType - > mSize ) ;
}
else
{
BF_ASSERT ( retType - > IsIntable ( ) ) ;
resultReg = ResizeRegister ( X64Reg_RAX , retType - > mSize ) ;
}
AllocInst ( BeMCInstKind_Mov , mcResult , BeMCOperand : : FromReg ( resultReg ) ) ;
}
if ( noReturn )
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromPreserveFlag ( BeMCPreserveFlag_NoRestore ) ) ;
else
AllocInst ( BeMCInstKind_RestoreVolatiles ) ;
return mcResult ;
}
void BeMCContext : : CreateMemSet ( const BeMCOperand & addr , uint8 val , int size , int align )
{
if ( ( size = = 8 ) | | ( size = = 4 ) | | ( size = = 2 ) | | ( size = = 1 ) )
{
BeType * type = NULL ;
BeMCOperand zeroVal ;
zeroVal . mImmediate = 0 ;
switch ( size )
{
case 8 :
type = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
zeroVal . mKind = BeMCOperandKind_Immediate_i64 ;
zeroVal . mImmediate = ( ( int64 ) val < < 56 ) | ( ( int64 ) val < < 48 ) | ( ( int64 ) val < < 40 ) | ( ( int64 ) val < < 32 ) |
( val < < 24 ) | ( val < < 16 ) | ( val < < 8 ) | val ;
break ;
case 4 :
type = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int32 ) ;
zeroVal . mKind = BeMCOperandKind_Immediate_i32 ;
zeroVal . mImmediate = ( val < < 24 ) | ( val < < 16 ) | ( val < < 8 ) | val ;
break ;
case 2 :
type = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int16 ) ;
zeroVal . mKind = BeMCOperandKind_Immediate_i16 ;
zeroVal . mImmediate = ( val < < 8 ) | val ;
break ;
case 1 :
type = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int8 ) ;
zeroVal . mKind = BeMCOperandKind_Immediate_i8 ;
zeroVal . mImmediate = val ;
break ;
}
auto ptrType = mModule - > mContext - > GetPointerTo ( type ) ;
auto result = AllocVirtualReg ( ptrType ) ;
CreateDefineVReg ( result ) ;
auto vregInfo = GetVRegInfo ( result ) ;
vregInfo - > mRelTo = addr ;
vregInfo - > mIsExpr = true ;
BeMCOperand dest ;
dest . mKind = BeMCOperandKind_VRegLoad ;
dest . mVRegIdx = result . mVRegIdx ;
AllocInst ( BeMCInstKind_Mov , dest , zeroVal ) ;
return ;
}
if ( addr . IsVRegAny ( ) )
{
auto vregInfo = GetVRegInfo ( addr ) ;
if ( ! vregInfo - > mIsRetVal )
vregInfo - > mForceMem = true ;
CheckForce ( vregInfo ) ;
}
if ( size < = 256 )
{
auto temp = addr ;
BeMCOperand mcMemSetInfo ;
mcMemSetInfo . mKind = BeMCOperandKind_MemSetInfo ;
mcMemSetInfo . mMemSetInfo . mSize = size ;
mcMemSetInfo . mMemSetInfo . mAlign = align ;
mcMemSetInfo . mMemSetInfo . mValue = val ;
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_R11 ) ) ;
AllocInst ( BeMCInstKind_MemSet , mcMemSetInfo , temp ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_R11 ) ) ;
return ;
}
SizedArray < BeMCOperand , 3 > args = { addr , BeMCOperand : : FromImmediate ( val ) , BeMCOperand : : FromImmediate ( size ) } ;
auto mcFunc = BeMCOperand : : FromSymbolAddr ( mCOFFObject - > GetSymbolRef ( " memset " ) - > mIdx ) ;
CreateCall ( mcFunc , args ) ;
}
void BeMCContext : : CreateMemCpy ( const BeMCOperand & dest , const BeMCOperand & src , int size , int align )
{
auto destVRegInfo = GetVRegInfo ( dest ) ;
auto srcVRegInfo = GetVRegInfo ( src ) ;
if ( ( destVRegInfo ! = NULL ) & & ( srcVRegInfo ! = NULL ) & & ( destVRegInfo - > mIsRetVal ) & & ( srcVRegInfo - > mIsRetVal ) )
return ;
if ( size < = 256 )
{
int destIdx = - 1 ;
if ( ! GetEncodedOperand ( dest , destIdx ) )
{
BeMCOperand tempDest = AllocVirtualReg ( GetType ( dest ) , 1 ) ;
CreateDefineVReg ( tempDest ) ;
AllocInst ( BeMCInstKind_Mov , tempDest , dest ) ;
auto vregInfo = mVRegInfo [ tempDest . mVRegIdx ] ;
vregInfo - > mForceReg = true ;
vregInfo - > mDisableR11 = true ;
destIdx = tempDest . mVRegIdx ;
}
int srcIdx = - 1 ;
if ( ! GetEncodedOperand ( src , srcIdx ) )
{
BeMCOperand tempSrc = AllocVirtualReg ( GetType ( src ) , 1 ) ;
CreateDefineVReg ( tempSrc ) ;
AllocInst ( BeMCInstKind_Mov , tempSrc , src ) ;
auto vregInfo = mVRegInfo [ tempSrc . mVRegIdx ] ;
vregInfo - > mForceReg = true ;
vregInfo - > mDisableR11 = true ;
srcIdx = tempSrc . mVRegIdx ;
}
BeMCOperand mcMemCpyInfo ;
mcMemCpyInfo . mKind = BeMCOperandKind_MemCpyInfo ;
mcMemCpyInfo . mMemCpyInfo . mSize = size ;
mcMemCpyInfo . mMemCpyInfo . mAlign = align ;
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_R11 ) ) ;
BeMCOperand mcVRegPair ;
mcVRegPair . mKind = BeMCOperandKind_VRegPair ;
mcVRegPair . mVRegPair . mVRegIdx0 = destIdx ;
mcVRegPair . mVRegPair . mVRegIdx1 = srcIdx ;
AllocInst ( BeMCInstKind_MemCpy , mcMemCpyInfo , mcVRegPair ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_R11 ) ) ;
return ;
}
SizedArray < BeMCOperand , 3 > args = { dest , src , BeMCOperand : : FromImmediate ( size ) } ;
auto mcFunc = BeMCOperand : : FromSymbolAddr ( mCOFFObject - > GetSymbolRef ( " memcpy " ) - > mIdx ) ;
CreateCall ( mcFunc , args ) ;
}
void BeMCContext : : CreateTableSwitchSection ( BeSwitchInst * switchInst , int startIdx , int endIdx )
{
auto & sect = mCOFFObject - > mRDataSect ;
auto defaultBlock = GetOperand ( switchInst - > mDefaultBlock ) ;
auto int32Type = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int32 ) ;
auto int32PtrType = mModule - > mContext - > GetPointerTo ( int32Type ) ;
auto nativeType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
auto nativePtrType = mModule - > mContext - > GetPointerTo ( nativeType ) ;
int64 loVal = switchInst - > mCases . front ( ) . mValue - > mInt64 ;
int64 hiVal = switchInst - > mCases . back ( ) . mValue - > mInt64 ;
int numVals = switchInst - > mCases . size ( ) ;
BeMCSymbol * sym = mCOFFObject - > mSymbols . Alloc ( ) ;
sym - > mType = int32Type ;
sym - > mName = StrFormat ( " @jumpTab%d " , mCOFFObject - > mCurJumpTableIdx + + ) ;
sym - > mIsStatic = true ;
sym - > mSymKind = BeMCSymbolKind_External ;
sym - > mIdx = ( int ) mCOFFObject - > mSymbols . size ( ) - 1 ;
mCOFFObject - > MarkSectionUsed ( sect ) ;
sym - > mSectionNum = sect . mSectionIdx + 1 ;
sect . mData . Align ( 4 ) ;
sect . mAlign = BF_MAX ( sect . mAlign , 4 ) ;
sym - > mValue = sect . mData . GetSize ( ) ;
auto mcValue = GetOperand ( switchInst - > mValue ) ;
auto beType = GetType ( mcValue ) ;
auto mcOfsValue = AllocVirtualReg ( nativeType ) ;
CreateDefineVReg ( mcOfsValue ) ;
if ( beType - > mSize < 8 )
AllocInst ( BeMCInstKind_MovSX , mcOfsValue , mcValue ) ;
else
AllocInst ( BeMCInstKind_Mov , mcOfsValue , mcValue ) ;
AllocInst ( BeMCInstKind_Sub , mcOfsValue , BeMCOperand : : FromImmediate ( loVal ) ) ;
AllocInst ( BeMCInstKind_CondBr , defaultBlock , BeMCOperand : : FromCmpKind ( BeCmpKind_SLT ) ) ;
AllocInst ( BeMCInstKind_Cmp , mcOfsValue , BeMCOperand : : FromImmediate ( hiVal - loVal ) ) ;
AllocInst ( BeMCInstKind_CondBr , defaultBlock , BeMCOperand : : FromCmpKind ( BeCmpKind_SGT ) ) ;
auto jumpVReg = AllocVirtualReg ( nativeType ) ;
CreateDefineVReg ( jumpVReg ) ;
AllocInst ( BeMCInstKind_Mov , jumpVReg , BeMCOperand : : FromSymbolAddr ( sym - > mIdx ) ) ;
auto jumpRelVReg = AllocVirtualReg ( int32PtrType ) ;
CreateDefineVReg ( jumpRelVReg ) ;
auto vregInfo = mVRegInfo [ jumpRelVReg . mVRegIdx ] ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = jumpVReg ;
vregInfo - > mRelOffset = mcOfsValue ;
vregInfo - > mRelOffsetScale = 4 ;
jumpRelVReg . mKind = BeMCOperandKind_VRegLoad ;
AllocInst ( BeMCInstKind_Mov , jumpVReg , jumpRelVReg ) ;
auto imageBaseSym = mCOFFObject - > GetSymbolRef ( " __ImageBase " ) ;
imageBaseSym - > mType = nativeType ;
auto baseVReg = AllocVirtualReg ( nativeType ) ;
CreateDefineVReg ( baseVReg ) ;
AllocInst ( BeMCInstKind_Mov , baseVReg , BeMCOperand : : FromSymbolAddr ( imageBaseSym - > mIdx ) ) ;
AllocInst ( BeMCInstKind_Add , jumpVReg , baseVReg ) ;
AllocInst ( BeMCInstKind_Br , jumpVReg ) ;
int64 lastVal = loVal - 1 ;
defaultBlock . mBlock - > AddPred ( mActiveBlock ) ;
for ( int caseIdx = 0 ; caseIdx < ( int ) switchInst - > mCases . size ( ) ; caseIdx + + )
{
auto & switchCase = switchInst - > mCases [ caseIdx ] ;
int64 newVal = switchCase . mValue - > mInt64 ;
for ( int64 val = lastVal + 1 ; val < = newVal ; val + + )
{
BeMCSwitchEntry switchEntry ;
switchEntry . mOfs = sect . mData . GetSize ( ) ;
if ( val = = newVal )
{
auto switchBlock = GetOperand ( switchCase . mBlock ) ;
switchBlock . mBlock - > AddPred ( mActiveBlock ) ;
switchEntry . mBlock = switchBlock . mBlock ;
}
else
{
switchEntry . mBlock = defaultBlock . mBlock ;
}
mSwitchEntries . push_back ( switchEntry ) ;
// Placeholder
sect . mData . Write ( ( int32 ) 0 ) ;
}
lastVal = newVal ;
}
}
void BeMCContext : : CreateBinarySwitchSection ( BeSwitchInst * switchInst , int startIdx , int endIdx )
{
// This is an empirically determined binary switching limit
if ( endIdx - startIdx > = 18 )
{
int gteLabel = mCurLabelIdx + + ;
auto mcDefaultBlock = GetOperand ( switchInst - > mDefaultBlock ) ;
int midIdx = startIdx + ( endIdx - startIdx ) / 2 ;
auto & switchCase = switchInst - > mCases [ midIdx ] ;
auto switchBlock = GetOperand ( switchCase . mBlock ) ;
auto mcValue = GetOperand ( switchInst - > mValue ) ;
auto valueType = GetType ( mcValue ) ;
AllocInst ( BeMCInstKind_Cmp , mcValue , GetOperand ( switchCase . mValue ) ) ;
AllocInst ( BeMCInstKind_CondBr , BeMCOperand : : FromLabel ( gteLabel ) , BeMCOperand : : FromCmpKind ( BeCmpKind_SGE ) ) ;
switchBlock . mBlock - > AddPred ( mActiveBlock ) ;
CreateBinarySwitchSection ( switchInst , startIdx , midIdx ) ;
AllocInst ( BeMCInstKind_Br , mcDefaultBlock ) ;
CreateLabel ( - 1 , gteLabel ) ;
CreateBinarySwitchSection ( switchInst , midIdx , endIdx ) ;
return ;
}
for ( int caseIdx = startIdx ; caseIdx < endIdx ; caseIdx + + )
{
auto & switchCase = switchInst - > mCases [ caseIdx ] ;
auto switchBlock = GetOperand ( switchCase . mBlock ) ;
auto mcValue = GetOperand ( switchInst - > mValue ) ;
AllocInst ( BeMCInstKind_Cmp , mcValue , GetOperand ( switchCase . mValue ) ) ;
AllocInst ( BeMCInstKind_CondBr , switchBlock , BeMCOperand : : FromCmpKind ( BeCmpKind_EQ ) ) ;
switchBlock . mBlock - > AddPred ( mActiveBlock ) ;
}
}
void BeMCContext : : CreateCondBr ( BeMCBlock * mcBlock , BeMCOperand & testVal , const BeMCOperand & trueBlock , const BeMCOperand & falseBlock )
{
if ( mDebugging )
{
NOP ;
}
if ( testVal . IsImmediate ( ) )
{
if ( testVal . mImmediate ! = 0 )
AllocInst ( BeMCInstKind_Br , trueBlock ) ;
else
AllocInst ( BeMCInstKind_Br , falseBlock ) ;
}
else if ( testVal . mKind = = BeMCOperandKind_CmpResult )
{
// Beef-specific: assuming CMP results aren't stomped
auto & cmpResult = mCmpResults [ testVal . mCmpResultIdx ] ;
AllocInst ( BeMCInstKind_CondBr , trueBlock , BeMCOperand : : FromCmpKind ( cmpResult . mCmpKind ) ) ;
AllocInst ( BeMCInstKind_Br , falseBlock ) ;
}
else if ( testVal . mKind = = BeMCOperandKind_Phi )
{
auto phi = testVal . mPhi ;
auto phiBlock = phi - > mBlock ;
if ( ( phi - > mBrTrue ! = NULL ) & & ( phi - > mBrFalse ! = NULL ) )
{
// Redirect branches
for ( auto instIdx = 0 ; instIdx < phiBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = phiBlock - > mInstructions [ instIdx ] ;
if ( inst - > mKind = = BeMCInstKind_Br )
{
if ( inst - > mArg0 = = phi - > mBrTrue )
inst - > mArg0 = trueBlock ;
else if ( inst - > mArg0 = = phi - > mBrFalse )
inst - > mArg0 = falseBlock ;
}
}
phi - > mBrTrue = trueBlock ;
phi - > mBrFalse = falseBlock ;
return ;
}
phi - > mBrTrue = trueBlock ;
phi - > mBrFalse = falseBlock ;
// Using a Phi for a CondBr in a different block is not supported
if ( phiBlock ! = mcBlock )
{
// Special case if our using block directly leads into us
BF_ASSERT ( mcBlock - > mPreds . size ( ) = = 1 ) ;
BF_ASSERT ( mcBlock - > mPreds [ 0 ] = = phiBlock ) ;
}
for ( auto instIdx = 0 ; instIdx < phiBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = phiBlock - > mInstructions [ instIdx ] ;
if ( inst - > mKind = = BeMCInstKind_DefPhi )
{
BF_ASSERT ( inst - > mArg0 . mPhi = = phi ) ;
RemoveInst ( phiBlock , instIdx ) ;
break ;
}
}
for ( auto & phiVal : phi - > mValues )
{
BeMCOperand landinglabel ;
if ( phiVal . mValue . mKind ! = BeMCOperandKind_Phi )
{
landinglabel = BeMCOperand : : FromLabel ( mCurLabelIdx + + ) ;
AllocInst ( BeMCInstKind_Label , landinglabel ) ;
}
int brInstIdx = - 1 ;
bool isFalseCmpResult = false ;
if ( landinglabel )
{
bool found = false ;
auto _CheckBlock = [ & ] ( BeMCBlock * block )
{
for ( int checkIdx = ( int ) block - > mInstructions . size ( ) - 1 ; checkIdx > = 0 ; checkIdx - - )
{
auto checkInst = block - > mInstructions [ checkIdx ] ;
if ( ( checkInst - > mArg0 . mKind = = BeMCOperandKind_Block ) & &
( checkInst - > mArg0 . mBlock = = phi - > mBlock ) )
{
brInstIdx = checkIdx ;
checkInst - > mArg0 = landinglabel ;
found = true ;
// Don't break, if we're are chained to another PHI then we need to modify all the labels
isFalseCmpResult = false ;
if ( ( checkIdx > = 2 ) & & ( checkInst - > mKind = = BeMCInstKind_Br ) )
{
auto prevInst = block - > mInstructions [ checkIdx - 1 ] ;
auto prevPrevInst = block - > mInstructions [ checkIdx - 2 ] ;
if ( ( prevPrevInst - > mKind = = BeMCInstKind_Cmp ) & & ( prevPrevInst - > mResult = = phiVal . mValue ) & &
( prevInst - > mKind = = BeMCInstKind_CondBr ) & & ( prevInst - > mArg1 . mCmpKind = = BeCmpKind_EQ ) )
{
isFalseCmpResult = true ;
}
}
}
}
} ;
_CheckBlock ( phiVal . mBlockFrom ) ;
BF_ASSERT ( found ) ;
}
if ( isFalseCmpResult )
{
BF_ASSERT ( phiVal . mValue . mKind = = BeMCOperandKind_CmpResult ) ;
AllocInst ( BeMCInstKind_Br , falseBlock ) ;
}
else if ( ( phiVal . mValue . IsImmediate ( ) ) | |
( phiVal . mValue . mKind = = BeMCOperandKind_CmpResult ) | |
( phiVal . mValue . mKind = = BeMCOperandKind_Phi ) | |
( phiVal . mValue . mKind = = BeMCOperandKind_NotResult ) )
{
CreateCondBr ( phiVal . mBlockFrom , phiVal . mValue , trueBlock , falseBlock ) ;
}
else
{
// Do the 'test' in the preceding block. This is needed for liveness checking. Using a vreg in this
// block would require the def to be hoisted
SetAndRestoreValue < BeMCBlock * > prevActiveBlock ( mActiveBlock , phiVal . mBlockFrom ) ;
BeMCOperand testImm ;
testImm . mKind = BeMCOperandKind_Immediate_i8 ;
testImm . mImmediate = 1 ;
auto mcInst = AllocInst ( BeMCInstKind_Test , phiVal . mValue , testImm , brInstIdx ) ;
prevActiveBlock . Restore ( ) ;
mcInst = AllocInst ( BeMCInstKind_CondBr , trueBlock , BeMCOperand : : FromCmpKind ( BeCmpKind_NE ) ) ;
mcInst = AllocInst ( BeMCInstKind_Br , falseBlock ) ;
}
}
}
else if ( testVal . mKind = = BeMCOperandKind_NotResult )
{
// Just get the original block and swap the true/false blocks
auto invResult = GetOperand ( testVal . mNotResult - > mValue , true ) ;
if ( invResult )
{
CreateCondBr ( mcBlock , invResult , falseBlock , trueBlock ) ;
}
}
else
{
BeMCOperand testImm ;
testImm . mKind = BeMCOperandKind_Immediate_i8 ;
testImm . mImmediate = 1 ;
auto mcInst = AllocInst ( BeMCInstKind_Test , testVal , testImm ) ;
mcInst = AllocInst ( BeMCInstKind_CondBr , trueBlock , BeMCOperand : : FromCmpKind ( BeCmpKind_NE ) ) ;
mcInst = AllocInst ( BeMCInstKind_Br , falseBlock ) ;
}
}
void BeMCContext : : CreatePhiAssign ( BeMCBlock * mcBlock , const BeMCOperand & testVal , const BeMCOperand & result , const BeMCOperand & doneLabel )
{
if ( testVal . mKind = = BeMCOperandKind_Phi )
{
auto phi = testVal . mPhi ;
for ( auto & phiVal : phi - > mValues )
{
BeMCOperand landinglabel ;
if ( phiVal . mValue . mKind = = BeMCOperandKind_Phi )
{
// Remove PhiDef
auto phi = phiVal . mValue . mPhi ;
auto phiBlock = phi - > mBlock ;
for ( auto instIdx = 0 ; instIdx < phiBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = phiBlock - > mInstructions [ instIdx ] ;
if ( inst - > mKind = = BeMCInstKind_DefPhi )
{
BF_ASSERT ( inst - > mArg0 . mPhi = = phi ) ;
RemoveInst ( phiBlock , instIdx ) ;
break ;
}
}
CreatePhiAssign ( phiVal . mBlockFrom , phiVal . mValue , result , doneLabel ) ;
}
else
{
bool found = false ;
auto _CheckBlock = [ & ] ( BeMCBlock * block )
{
SetAndRestoreValue < BeMCBlock * > prevActiveBlock ( mActiveBlock , block ) ;
for ( int checkIdx = ( int ) block - > mInstructions . size ( ) - 1 ; checkIdx > = 0 ; checkIdx - - )
{
auto checkInst = block - > mInstructions [ checkIdx ] ;
if ( ( checkInst - > mArg0 . mKind = = BeMCOperandKind_Block ) & &
( checkInst - > mArg0 . mBlock = = phi - > mBlock ) )
{
// Don't use an explicit dbgLoc
SetAndRestoreValue < BeDbgLoc * > prevDbgLoc ( mCurDbgLoc , NULL ) ;
if ( checkInst - > mKind = = BeMCInstKind_CondBr )
{
auto falseLabel = BeMCOperand : : FromLabel ( mCurLabelIdx + + ) ;
auto prevDest = checkInst - > mArg0 ;
checkInst - > mArg0 = falseLabel ;
checkInst - > mArg1 . mCmpKind = BeModule : : InvertCmp ( checkInst - > mArg1 . mCmpKind ) ;
AllocInst ( BeMCInstKind_Mov , result , phiVal . mValue , checkIdx + 1 ) ;
AllocInst ( BeMCInstKind_Br , prevDest , checkIdx + 2 ) ;
AllocInst ( BeMCInstKind_Label , falseLabel , checkIdx + 3 ) ;
}
else
{
AllocInst ( BeMCInstKind_Mov , result , phiVal . mValue , checkIdx ) ;
}
found = true ;
}
}
} ;
_CheckBlock ( phiVal . mBlockFrom ) ;
BF_ASSERT ( found ) ;
}
if ( landinglabel )
{
AllocInst ( BeMCInstKind_Br , doneLabel ) ;
}
}
}
else
{
NotImpl ( ) ;
}
}
BeMCOperand BeMCContext : : GetImmediate ( int64 val )
{
BeMCOperand operand ;
operand . mKind = BeMCOperandKind_Immediate_i64 ;
operand . mImmediate = val ;
return operand ;
}
BeMCOperand BeMCContext : : GetVReg ( int regNum )
{
auto vregInfo = mVRegInfo [ regNum ] ;
if ( vregInfo - > mReg ! = X64Reg_None )
return BeMCOperand : : FromReg ( vregInfo - > mReg ) ;
BeMCOperand operand ;
operand . mKind = BeMCOperandKind_VReg ;
operand . mVRegIdx = regNum ;
return operand ;
}
BeMCOperand BeMCContext : : AllocVirtualReg ( BeType * type , int refCount , bool mustBeReg )
{
BF_ASSERT ( type - > mTypeCode ! = BeTypeCode_Function ) ; // We can only have pointers to these
if ( mustBeReg )
{
BF_ASSERT ( ! type - > IsComposite ( ) ) ;
BF_ASSERT ( type - > mSize ! = 0 ) ;
}
int vregIdx = ( int ) mVRegInfo . size ( ) ;
BeMCVRegInfo * vregInfo = mAlloc . Alloc < BeMCVRegInfo > ( ) ;
vregInfo - > mType = type ;
2019-11-26 13:11:17 -08:00
vregInfo - > mAlign = type - > mAlign ;
2019-08-23 11:56:54 -07:00
vregInfo - > mRefCount = refCount ;
vregInfo - > mForceReg = mustBeReg ;
mVRegInfo . push_back ( vregInfo ) ;
BeMCOperand mcOperand ;
mcOperand . mKind = BeMCOperandKind_VReg ;
mcOperand . mVRegIdx = vregIdx + + ;
if ( mDebugging )
{
2019-11-26 13:11:17 -08:00
if ( mcOperand . mVRegIdx = = 3 )
2019-08-23 11:56:54 -07:00
{
NOP ;
}
}
return mcOperand ;
}
int BeMCContext : : GetUnderlyingVReg ( int vregIdx )
{
while ( true )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( ! vregInfo - > mRelTo . IsVRegAny ( ) )
return vregIdx ;
if ( vregInfo - > mRelOffset )
return vregIdx ;
vregIdx = vregInfo - > mRelTo . mVRegIdx ;
}
}
bool BeMCContext : : HasForceRegs ( const BeMCOperand & operand )
{
if ( ! operand . IsVRegAny ( ) )
return false ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( vregInfo - > mForceReg )
return true ;
return HasForceRegs ( vregInfo - > mRelTo ) | | HasForceRegs ( vregInfo - > mRelOffset ) ;
}
bool BeMCContext : : OperandsEqual ( const BeMCOperand & op0 , const BeMCOperand & op1 , bool exact )
{
bool isVReg0 = op0 . mKind = = BeMCOperandKind_VReg ;
bool isVReg1 = op1 . mKind = = BeMCOperandKind_VReg ;
if ( ! isVReg0 | | ! isVReg1 )
{
return op0 = = op1 ;
}
if ( exact )
{
int vregIdx0 = op0 . mVRegIdx ;
int vregIdx1 = op1 . mVRegIdx ;
while ( true )
{
auto vregInfo = mVRegInfo [ vregIdx0 ] ;
if ( ! vregInfo - > IsDirectRelTo ( ) )
break ;
if ( vregInfo - > mRelTo . mKind ! = BeMCOperandKind_VReg )
return false ;
vregIdx0 = vregInfo - > mRelTo . mVRegIdx ;
}
while ( true )
{
auto vregInfo = mVRegInfo [ vregIdx1 ] ;
if ( ! vregInfo - > IsDirectRelTo ( ) )
break ;
if ( vregInfo - > mRelTo . mKind ! = BeMCOperandKind_VReg )
return false ;
vregIdx1 = vregInfo - > mRelTo . mVRegIdx ;
}
return vregIdx0 = = vregIdx1 ;
}
int vregIdx0 = GetUnderlyingVReg ( op0 . mVRegIdx ) ;
int vregIdx1 = GetUnderlyingVReg ( op1 . mVRegIdx ) ;
return vregIdx0 = = vregIdx1 ;
}
bool BeMCContext : : ContainsNonOffsetRef ( const BeMCOperand & checkOperand , const BeMCOperand & findOperand )
{
if ( ! checkOperand . IsVRegAny ( ) )
return false ;
if ( checkOperand = = findOperand )
return true ;
auto vregInfo = GetVRegInfo ( checkOperand ) ;
if ( ContainsNonOffsetRef ( vregInfo - > mRelTo , findOperand ) )
return true ;
return false ;
}
// For all values that we are certain we will immediately use, we directly do a Def preceding its first use.
// For Allocas in the head, however, we may not use that memory for a long time so we imply the Def location
// in DoDefPass. That allows us to limit how long that vreg will hold onto a register, reducing register
// contention.
BeMCInst * BeMCContext : : CreateDefineVReg ( const BeMCOperand & vreg , int insertIdx )
{
auto mcInst = AllocInst ( insertIdx ) ;
mcInst - > mKind = BeMCInstKind_Def ;
mcInst - > mArg0 = vreg ;
return mcInst ;
}
int BeMCContext : : CreateLabel ( int insertIdx , int labelIdx )
{
auto inst = AllocInst ( insertIdx ) ;
inst - > mKind = BeMCInstKind_Label ;
inst - > mArg0 . mKind = BeMCOperandKind_Label ;
if ( labelIdx ! = - 1 )
inst - > mArg0 . mLabelIdx = labelIdx ;
else
inst - > mArg0 . mLabelIdx = mCurLabelIdx + + ;
return inst - > mArg0 . mLabelIdx ;
}
bool BeMCContext : : FindTarget ( const BeMCOperand & loc , BeMCBlock * & outBlock , int & outInstIdx )
{
if ( loc . mKind = = BeMCOperandKind_Block )
{
outBlock = loc . mBlock ;
outInstIdx = - 1 ;
return true ;
}
if ( loc . mKind = = BeMCOperandKind_Label )
{
// Linear search :-(
for ( auto mcBlock : mBlocks )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( ( inst - > mKind = = BeMCInstKind_Label ) & & ( inst - > mArg0 . mLabelIdx = = loc . mLabelIdx ) )
{
outBlock = mcBlock ;
outInstIdx = instIdx ;
return true ;
}
}
}
}
return false ;
}
BeMCOperand BeMCContext : : AllocRelativeVirtualReg ( BeType * type , const BeMCOperand & relTo , const BeMCOperand & relOffset , int relScale )
{
if ( ! relTo . IsVRegAny ( ) )
{
auto relToType = GetType ( relTo ) ;
auto tempRelTo = AllocVirtualReg ( relToType , 1 ) ;
CreateDefineVReg ( tempRelTo ) ;
AllocInst ( BeMCInstKind_Mov , tempRelTo , relTo ) ;
tempRelTo . mKind = BeMCOperandKind_VReg ;
return AllocRelativeVirtualReg ( type , tempRelTo , relOffset , relScale ) ;
}
BF_ASSERT ( relTo . IsVRegAny ( ) ) ;
auto relToVRegInfo = GetVRegInfo ( relTo ) ;
if ( ( mDebugging ) & & ( mVRegInfo . size ( ) = = 5 ) )
{
NOP ;
}
if ( ( relToVRegInfo - > mRelTo ) & & ( relToVRegInfo - > mRelOffsetScale = = 1 ) & & ( relToVRegInfo - > mRelOffset . IsImmediate ( ) ) & &
( relOffset . IsImmediate ( ) ) & & ( relScale = = 1 ) )
{
if ( relTo . mKind = = BeMCOperandKind_VReg )
{
int offset = ( int ) relOffset . mImmediate ;
if ( relToVRegInfo - > mRelOffset )
offset + = relToVRegInfo - > mRelOffset . mImmediate ;
return AllocRelativeVirtualReg ( type , relToVRegInfo - > mRelTo , GetImmediate ( offset ) , 1 ) ;
}
}
if ( ( relTo . mKind = = BeMCOperandKind_VRegAddr ) & & ( ! relToVRegInfo - > mRelOffset ) & & ( relOffset . IsZero ( ) ) & & ( relScale = = 1 ) )
{
auto vregInfo = GetVRegInfo ( relTo ) ;
if ( vregInfo - > IsDirectRelToAny ( ) )
{
if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_Symbol )
{
return AllocRelativeVirtualReg ( type , BeMCOperand : : FromSymbolAddr ( vregInfo - > mRelTo . mSymbolIdx ) , BeMCOperand ( ) , 1 ) ;
}
}
}
auto mcVReg = AllocVirtualReg ( type ) ;
auto vregInfo = GetVRegInfo ( mcVReg ) ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = relTo ;
if ( ! relOffset . IsZero ( ) )
vregInfo - > mRelOffset = relOffset ;
vregInfo - > mRelOffsetScale = relScale ;
mcVReg . mKind = BeMCOperandKind_VReg ;
return mcVReg ;
}
BeMCVRegInfo * BeMCContext : : GetVRegInfo ( const BeMCOperand & operand )
{
if ( ! operand . IsVRegAny ( ) )
return NULL ;
return mVRegInfo [ operand . mVRegIdx ] ;
}
bool BeMCContext : : HasSymbolAddr ( const BeMCOperand & operand )
{
if ( operand . mKind = = BeMCOperandKind_SymbolAddr )
return true ;
auto vregInfo = GetVRegInfo ( operand ) ;
if ( vregInfo = = NULL )
return false ;
if ( ( vregInfo - > mRelTo ) & & ( HasSymbolAddr ( vregInfo - > mRelTo ) ) )
return true ;
if ( ( vregInfo - > mRelOffset ) & & ( HasSymbolAddr ( vregInfo - > mRelOffset ) ) )
return true ;
return false ;
}
BeMCOperand BeMCContext : : ReplaceWithNewVReg ( BeMCOperand & operand , int & instIdx , bool isInput , bool mustBeReg )
{
BeMCOperand scratchReg = AllocVirtualReg ( GetType ( operand ) , 2 , mustBeReg ) ;
CreateDefineVReg ( scratchReg , instIdx + + ) ;
if ( isInput )
AllocInst ( BeMCInstKind_Mov , scratchReg , operand , instIdx + + ) ;
else
AllocInst ( BeMCInstKind_Mov , operand , scratchReg , instIdx + + + 1 ) ;
operand = scratchReg ;
return scratchReg ;
}
BeMCOperand BeMCContext : : RemapOperand ( BeMCOperand & operand , BeMCRemapper & regRemaps )
{
if ( ! operand . IsVRegAny ( ) )
return operand ;
int regIdx = regRemaps . GetHead ( operand . mVRegIdx ) ;
if ( regIdx < 0 )
{
BeMCOperand newOperand ;
newOperand . mKind = operand . mKind ;
if ( newOperand . mKind = = BeMCOperandKind_VRegLoad )
newOperand . mKind = BeMCOperandKind_VReg ;
else
NotImpl ( ) ;
newOperand . mVRegIdx = - regIdx ;
return newOperand ;
}
else
{
BeMCOperand newOperand ;
newOperand . mKind = operand . mKind ;
newOperand . mVRegIdx = regIdx ;
return newOperand ;
}
}
bool BeMCContext : : IsLive ( BeVTrackingList * liveRegs , int origVRegIdx , BeMCRemapper & regRemaps )
{
// Check the whole remap chain to determine liveness
int vregIdx = regRemaps . GetHead ( origVRegIdx ) ;
while ( vregIdx ! = - 1 )
{
if ( mLivenessContext . IsSet ( liveRegs , vregIdx ) )
return true ;
vregIdx = regRemaps . GetNext ( vregIdx ) ;
}
return false ;
}
void BeMCContext : : AddRegRemap ( int from , int to , BeMCRemapper & regRemaps , bool allowRemapToDbgVar )
{
auto vregInfoFrom = mVRegInfo [ from ] ;
auto vregInfoTo = mVRegInfo [ to ] ;
BF_ASSERT ( vregInfoFrom - > mDbgVariable = = NULL ) ;
if ( vregInfoTo - > mDbgVariable ! = NULL )
{
// We can't do a direct remap due to lifetime issues, so do an indirect one
vregInfoFrom - > mIsExpr = true ;
if ( ! vregInfoFrom - > mIsRetVal )
vregInfoFrom - > mForceReg = false ;
vregInfoFrom - > mRelTo = BeMCOperand : : FromVReg ( to ) ;
vregInfoFrom - > mRelOffset = BeMCOperand ( ) ;
vregInfoFrom - > mRelOffsetScale = 1 ;
return ;
}
regRemaps . Add ( from , to ) ;
}
bool BeMCContext : : GetEncodedOperand ( const BeMCOperand & operand , int & encodedVal )
{
if ( operand . mKind = = BeMCOperandKind_VReg )
{
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
encodedVal = operand . mVRegIdx ;
return true ;
}
if ( operand . mKind ! = BeMCOperandKind_VRegAddr )
return false ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( vregInfo - > IsDirectRelTo ( ) )
{
if ( ! GetEncodedOperand ( vregInfo - > mRelTo , encodedVal ) )
return false ;
encodedVal | = BeMCOperand : : EncodeFlag_Addr ;
return true ;
}
if ( vregInfo - > mIsExpr )
return false ;
encodedVal = operand . mVRegIdx | BeMCOperand : : EncodeFlag_Addr ;
return true ;
}
bool BeMCContext : : HasPointerDeref ( const BeMCOperand & operand )
{
if ( operand . mKind = = BeMCOperandKind_VRegLoad )
return true ;
auto vregInfo = GetVRegInfo ( operand ) ;
if ( vregInfo = = NULL )
return false ;
if ( ! vregInfo - > mRelTo )
return false ;
if ( ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_VRegLoad ) | |
( vregInfo - > mRelTo . mKind = = BeMCOperandKind_VReg ) )
return true ;
return false ;
}
bool BeMCContext : : AreOperandsEquivalent ( const BeMCOperand & origOp0 , const BeMCOperand & origOp1 , BeMCRemapper & regRemaps )
{
auto op0 = origOp0 ;
RemapOperand ( op0 , regRemaps ) ;
auto op1 = origOp1 ;
RemapOperand ( op1 , regRemaps ) ;
if ( op0 . mKind ! = op1 . mKind )
return false ;
if ( op0 . mKind = = BeMCOperandKind_None )
return true ;
if ( op0 . IsSymbol ( ) )
return op0 . mSymbolIdx = = op1 . mSymbolIdx ;
if ( op0 . IsImmediate ( ) )
return op0 . mImmediate = = op1 . mImmediate ;
if ( op0 . IsNativeReg ( ) )
return op0 . mReg = = op1 . mReg ;
if ( op0 . IsVRegAny ( ) )
{
if ( op0 . mVRegIdx = = op1 . mVRegIdx )
return true ;
auto vregInfo0 = mVRegInfo [ op0 . mVRegIdx ] ;
auto vregInfo1 = mVRegInfo [ op1 . mVRegIdx ] ;
if ( vregInfo0 - > mType ! = vregInfo1 - > mType )
return false ;
if ( ( ! vregInfo0 - > mIsExpr ) | | ( ! vregInfo1 - > mIsExpr ) )
return false ;
return
( vregInfo0 - > mRelOffsetScale = = vregInfo1 - > mRelOffsetScale ) & &
( AreOperandsEquivalent ( vregInfo0 - > mRelTo , vregInfo1 - > mRelTo , regRemaps ) ) & &
( AreOperandsEquivalent ( vregInfo0 - > mRelOffset , vregInfo1 - > mRelOffset , regRemaps ) ) ;
}
return false ;
}
bool BeMCContext : : CouldBeReg ( const BeMCOperand & operand )
{
if ( operand . mKind ! = BeMCOperandKind_VReg )
return false ;
auto vregInfo = GetVRegInfo ( operand ) ;
if ( vregInfo - > mForceMem )
return false ;
if ( vregInfo - > mIsExpr )
{
if ( vregInfo - > mRelOffset )
return false ;
}
return true ;
}
bool BeMCContext : : CheckForce ( BeMCVRegInfo * vregInfo )
{
if ( ! vregInfo - > mIsRetVal )
{
if ( vregInfo - > mForceMem & & vregInfo - > mForceReg )
Fail ( " vreg forceMem/forceReg collision " ) ;
}
return false ;
}
void BeMCContext : : MarkLive ( BeVTrackingList * liveRegs , SizedArrayImpl < int > & newRegs , BeVTrackingList * vregsInitialized , const BeMCOperand & operand )
{
int vregIdx = operand . mVRegIdx ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( mLivenessContext . IsSet ( liveRegs , operand . mVRegIdx ) )
return ;
for ( auto newReg : newRegs )
if ( newReg = = operand . mVRegIdx )
return ;
if ( ! mColorizer . mNodes . empty ( ) )
{
// Is new
for ( int i = 0 ; i < liveRegs - > mSize ; i + + )
{
int checkReg = liveRegs - > mEntries [ i ] ;
if ( checkReg > = mLivenessContext . mNumItems )
continue ;
mColorizer . AddEdge ( checkReg , operand . mVRegIdx ) ;
}
for ( auto checkReg : newRegs )
mColorizer . AddEdge ( checkReg , operand . mVRegIdx ) ;
}
if ( vregInfo - > mHasDynLife )
{
if ( ! mLivenessContext . IsSet ( vregsInitialized , vregIdx ) )
{
// This indicates that this is a 'def' usage, meaning the value wasn't actually set yet
// so don't propagate this index upward
return ;
}
}
if ( ( vregInfo - > mRelTo ) & & ( vregInfo - > mRelTo . IsVRegAny ( ) ) )
MarkLive ( liveRegs , newRegs , vregsInitialized , vregInfo - > mRelTo ) ;
if ( ( vregInfo - > mRelOffset ) & & ( vregInfo - > mRelOffset . IsVRegAny ( ) ) )
MarkLive ( liveRegs , newRegs , vregsInitialized , vregInfo - > mRelOffset ) ;
newRegs . push_back ( operand . mVRegIdx ) ;
}
BeVTrackingList * BeMCContext : : MergeLiveRegs ( BeVTrackingList * prevDestEntry , BeVTrackingList * mergeFrom )
{
int vregIdx = - 1 ;
SizedArray < int , 16 > newNodes ;
SizedArray < int , 16 > prevExclusiveNodes ;
// Take nodes that were exclusive to the new set and add edges to nodes that were exclusive the old set
/*while (true)
{
vregIdx = mLivenessContext . GetNextDiffSetIdx ( prevDestEntry . mBits , mergeFrom , vregIdx ) ;
if ( vregIdx = = - 1 )
break ;
newNodes . push_back ( vregIdx ) ;
if ( ! mColorizer . mNodes . empty ( ) )
{
int checkReg = - 1 ;
while ( true )
{
checkReg = mLivenessContext . GetNextDiffSetIdx ( mergeFrom , prevDestEntry . mBits , checkReg ) ;
if ( checkReg = = - 1 )
break ;
mColorizer . AddEdge ( checkReg , vregIdx ) ;
}
}
} */
auto prevItr = prevDestEntry - > begin ( ) ;
auto prevEnd = prevDestEntry - > end ( ) ;
auto mergeFromItr = mergeFrom - > begin ( ) ;
auto mergeFromEnd = mergeFrom - > end ( ) ;
while ( ( prevItr ! = prevEnd ) & & ( mergeFromItr ! = mergeFromEnd ) )
{
int prevIdx = * prevItr ;
int mergeIdx = * mergeFromItr ;
bool done = false ;
while ( mergeIdx < prevIdx )
{
newNodes . push_back ( mergeIdx ) ;
+ + mergeFromItr ;
if ( mergeFromItr = = mergeFromEnd )
{
done = true ;
break ;
}
mergeIdx = * mergeFromItr ;
}
if ( done )
break ;
while ( prevIdx < mergeIdx )
{
prevExclusiveNodes . push_back ( prevIdx ) ;
+ + prevItr ;
if ( prevItr = = prevEnd )
{
done = true ;
break ;
}
prevIdx = * prevItr ;
}
if ( done )
break ;
if ( prevIdx = = mergeIdx )
{
+ + prevItr ;
+ + mergeFromItr ;
}
}
while ( prevItr ! = prevEnd )
{
prevExclusiveNodes . push_back ( * prevItr ) ;
+ + prevItr ;
}
while ( mergeFromItr ! = mergeFromEnd )
{
newNodes . push_back ( * mergeFromItr ) ;
+ + mergeFromItr ;
}
if ( ! mColorizer . mNodes . empty ( ) )
{
for ( int newIdx : newNodes )
{
for ( int prevExclusiveIdx : prevExclusiveNodes )
{
mColorizer . AddEdge ( newIdx , prevExclusiveIdx ) ;
}
}
}
return mLivenessContext . Add ( prevDestEntry , newNodes , false ) ;
}
static void DedupPushBack ( SizedArrayImpl < int > & vec , int val )
{
if ( vec . Contains ( val ) )
return ;
vec . push_back ( val ) ;
}
static int genLivenessIdx = 0 ;
void BeMCContext : : GenerateLiveness ( BeMCBlock * block , BeVTrackingGenContext * genCtx , bool & modifiedBlockBefore , bool & modifiedBlockAfter )
{
genCtx - > mBlocks [ block - > mBlockIdx ] . mGenerateCount + + ;
modifiedBlockBefore = false ;
modifiedBlockAfter = false ;
genCtx - > mCalls + + ;
bool debugging = false ;
//if (mDebugging) debugging = true;
//if (mBeFunction->mName == "?Draw@DarkEditWidgetContent@dark@theme@Beefy@@UEAAXPEAVGraphics@gfx@4@@Z")
//debugging = true;
//"?DrawEntry@DrawContext@PerfView@BeefPerf@@QEAAXPEAVGraphics@gfx@Beefy@@PEAVTrackNodeEntry@23@MM@Z")
//debugging &= mcColorizer != NULL;
if ( debugging )
{
if ( block - > mBlockIdx = = 220 )
{
//BF_ASSERT(mLivenessContext.IsSet(succLiveRegs, 36));
}
}
if ( debugging )
{
OutputDebugStrF ( " GenerateLiveness %s(%d) \n " , ToString ( BeMCOperand : : FromBlock ( block ) ) . c_str ( ) , block - > mBlockIdx ) ;
}
/*bool isFirstEntry = block->mSuccLiveness.mBits == NULL;
if ( isFirstEntry )
{
mLivenessContext . mStats . mSuccBytes + = mLivenessContext . GetBitsBytes ( ) * 2 ;
block - > mSuccLiveness . mBits = mLivenessContext . Duplicate ( succLiveRegs . mBits ) ;
block - > mSuccLiveness . mList = succLiveRegs . mList ;
block - > mSuccVRegsInitialized . mBits = mLivenessContext . Duplicate ( succInitRegs . mBits ) ;
block - > mSuccVRegsInitialized . mList = succInitRegs . mList ;
}
else
{
if ( debugging )
{
OutputDebugStrF ( " New Succ Regs: " ) ;
int vregIdx = - 1 ;
while ( true )
{
vregIdx = mLivenessContext . GetNextDiffSetIdx ( block - > mSuccLiveness . mBits , succLiveRegs . mBits , vregIdx ) ;
if ( vregIdx = = - 1 )
break ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mDbgVariable ! = NULL )
OutputDebugStrF ( " #%s/%d " , vregInfo - > mDbgVariable - > mName . c_str ( ) , vregIdx ) ;
else
OutputDebugStrF ( " %%vreg%d " , vregIdx ) ;
}
OutputDebugStrF ( " \n " ) ;
}
if ( mDebugging )
{
}
MergeLiveRegs ( block - > mSuccLiveness , succLiveRegs . mBits ) ;
mVRegInitializedContext . MergeInplace ( block - > mSuccVRegsInitialized . mBits , succInitRegs . mBits ) ;
} */
genCtx - > mHandledCalls + + ;
BeMDNode * curDbgScope = NULL ;
// Don't use 'ref' here, it's important that mSuccLiveRegs actually means 'successor', otherwise
// we could miss a later-occurring liveness overlap on a different successor
BeVTrackingList * liveRegs = block - > mSuccLiveness ;
BeVTrackingList * vregsInitialized = block - > mSuccVRegsInitialized ;
// When we process our first vregsInitialized (at the bottom of the block), there won't be Change entries
// for us to use to determine the delta from mSuccVRegsInitialized to inst->mVRegsInitialized
bool needsManualVRegInitDiff = true ;
for ( int instIdx = ( int ) block - > mInstructions . size ( ) - 1 ; instIdx > = 0 ; instIdx - - )
{
genLivenessIdx + + ;
auto inst = block - > mInstructions [ instIdx ] ;
auto prevLiveness = inst - > mLiveness ;
genCtx - > mInstructions + + ;
SizedArray < int , 16 > removeVec ;
SizedArray < int , 16 > addVec ;
SizedArray < int , 16 > filteredRemoveVec ;
SizedArray < int , 16 > filteredAddVec ;
inst - > mLiveness = liveRegs ;
if ( ( inst - > mVRegsInitialized ! = NULL ) & & ( inst - > mVRegsInitialized ! = vregsInitialized ) )
{
/*int vregIdxEx = -1;
while ( true )
{
vregIdxEx = mVRegInitializedContext . GetNextDiffSetIdx ( vregsInitialized . mBits , inst - > mVRegsInitialized , vregIdxEx , false , false ) ;
if ( vregIdxEx = = - 1 )
break ; */
auto _VRegUninit = [ & ] ( int vregIdxEx )
{
int vregIdx = vregIdxEx % mVRegInitializedContext . mNumItems ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( ! vregInfo - > mHasDynLife )
return ;
bool doClear = false ;
if ( vregInfo - > mDoConservativeLife )
{
// Only do liveness clear when both 'init' and 'non-init' is set
int otherVRegIdxEx = vregIdx ;
if ( vregIdxEx < mVRegInitializedContext . mNumItems )
otherVRegIdxEx + = mVRegInitializedContext . mNumItems ;
if ( ! mVRegInitializedContext . IsSet ( inst - > mVRegsInitialized , otherVRegIdxEx ) )
doClear = true ;
}
else
{
// Only do liveness clear on 'init' clearing out
if ( vregIdx < mVRegInitializedContext . mNumItems )
doClear = true ;
}
if ( doClear )
{
if ( vregIdx > = mVRegInitializedContext . mNumItems )
DedupPushBack ( removeVec , vregIdx ) ;
else
removeVec . push_back ( vregIdx ) ;
}
} ;
if ( needsManualVRegInitDiff )
{
BF_ASSERT ( vregsInitialized = = block - > mSuccVRegsInitialized ) ;
// Manually compare
auto vregsInit0 = vregsInitialized ;
auto vregsInit1 = inst - > mVRegsInitialized ;
if ( vregsInit0 ! = vregsInit1 )
{
int idx0 = 0 ;
int idx1 = 0 ;
while ( ( idx0 ! = vregsInit0 - > mSize ) & & ( idx1 ! = vregsInit1 - > mSize ) )
{
int val0 = vregsInit0 - > mEntries [ idx0 ] ;
int val1 = vregsInit1 - > mEntries [ idx1 ] ;
if ( val0 = = val1 )
{
idx0 + + ;
idx1 + + ;
continue ;
}
bool done = false ;
while ( val0 < val1 )
{
_VRegUninit ( val0 ) ;
idx0 + + ;
if ( idx0 > = vregsInit0 - > mSize )
{
done = true ;
break ;
}
val0 = vregsInit0 - > mEntries [ idx0 ] ;
}
if ( done )
break ;
while ( val1 < val0 )
{
idx1 + + ;
if ( idx1 > = vregsInit1 - > mSize )
{
done = true ;
break ;
}
val1 = vregsInit1 - > mEntries [ idx1 ] ;
}
if ( done )
break ;
}
while ( idx0 < vregsInit0 - > mSize )
{
_VRegUninit ( vregsInit0 - > mEntries [ idx0 + + ] ) ;
}
}
}
else
{
for ( int changeIdx = 0 ; changeIdx < vregsInitialized - > mNumChanges ; changeIdx + + )
{
int vregIdxEx = vregsInitialized - > GetChange ( changeIdx ) ;
if ( vregIdxEx < 0 )
continue ;
// It's possible this vregsInitialized is equivalent to the one in 'inst', but also merged with another 'inst'
// during legalization. We need to avoid uninitializing vregs if that's the case.
// the entry above this
if ( inst - > mVRegsInitialized - > ContainsChange ( vregIdxEx ) )
continue ;
_VRegUninit ( vregIdxEx ) ;
}
}
vregsInitialized = inst - > mVRegsInitialized ;
}
if ( inst - > mVRegsInitialized ! = NULL )
needsManualVRegInitDiff = false ;
if ( inst - > mKind = = BeMCInstKind_DbgDecl )
{
if ( ! mVRegInitializedContext . IsSet ( inst - > mVRegsInitialized , inst - > mArg0 . mVRegIdx ) )
{
// There are some rare cases with conditional branches where one branch will have a vreg marked as
// initialized, which causes the variable to be marked as live, which propagates upward into the block
// containing a variable declaration, before the actual def point
DedupPushBack ( removeVec , inst - > mArg0 . mVRegIdx ) ;
}
liveRegs = mLivenessContext . Modify ( liveRegs , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
continue ;
}
// This is used for clearing out things like usage of inline return values, which will be accessed after their
// lifetime end (the lifetime ends inside the inlined method but the value is used afterward, in the inlining
// function. This will emit as a load of a dbgVar, so we need to drill down into the relTo values
if ( inst - > mKind = = BeMCInstKind_LifetimeStart )
{
BF_ASSERT ( inst - > mArg0 . IsVRegAny ( ) ) ;
int vregIdx = inst - > mArg0 . mVRegIdx ;
while ( true )
{
DedupPushBack ( removeVec , vregIdx ) ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( ! vregInfo - > IsDirectRelTo ( ) )
break ;
BF_ASSERT ( vregInfo - > mRelTo . IsVReg ( ) ) ;
vregIdx = vregInfo - > mRelTo . mVRegIdx ;
}
liveRegs = mLivenessContext . Modify ( liveRegs , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
continue ;
}
if ( inst - > mKind = = BeMCInstKind_LifetimeEnd )
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo - > mDbgVariable = = NULL )
{
// Only extend lifetime down through LifetimeEnd when we have a debug variable we want to keep alive,
// otherwise constrict lifetime to actual usage
liveRegs = mLivenessContext . Modify ( liveRegs , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
continue ;
}
}
if ( inst - > IsDef ( ) )
{
DedupPushBack ( removeVec , inst - > mArg0 . mVRegIdx ) ;
liveRegs = mLivenessContext . Modify ( liveRegs , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
continue ;
}
auto operands = { & inst - > mResult , & inst - > mArg0 , & inst - > mArg1 } ;
for ( auto operand : operands )
{
if ( operand - > IsSymbol ( ) )
{
auto sym = mCOFFObject - > mSymbols [ operand - > mSymbolIdx ] ;
if ( sym - > mIsTLS )
{
MarkLive ( liveRegs , addVec , vregsInitialized , BeMCOperand : : FromVReg ( mTLSVRegIdx ) ) ;
}
}
if ( operand - > mKind = = BeMCOperandKind_VRegPair )
{
auto mcOperand = BeMCOperand : : FromEncoded ( operand - > mVRegPair . mVRegIdx0 ) ;
if ( mcOperand . IsVRegAny ( ) )
MarkLive ( liveRegs , addVec , vregsInitialized , mcOperand ) ;
mcOperand = BeMCOperand : : FromEncoded ( operand - > mVRegPair . mVRegIdx1 ) ;
if ( mcOperand . IsVRegAny ( ) )
MarkLive ( liveRegs , addVec , vregsInitialized , mcOperand ) ;
}
if ( operand - > IsVRegAny ( ) )
{
MarkLive ( liveRegs , addVec , vregsInitialized , * operand ) ;
}
}
liveRegs = mLivenessContext . Modify ( liveRegs , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
/*if ((!isFirstEntry) && (liveRegs == prevLiveness))
{
// We've already been here before and nothing changed
return ;
} */
inst - > mLiveness = liveRegs ;
}
if ( block = = mBlocks [ 0 ] )
{
BF_ASSERT ( block - > mBlockIdx = = 0 ) ;
if ( ! mLivenessContext . IsEmpty ( liveRegs ) )
{
for ( int vregIdx = 0 ; vregIdx < mLivenessContext . mNumEntries ; vregIdx + + )
{
if ( mLivenessContext . IsSet ( liveRegs , vregIdx ) )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
// If we are still alive then the only valid reason is because we have mHasDynLife and our 'init' flag is still set
if ( vregInfo - > mHasDynLife )
{
if ( ! mVRegInitializedContext . IsSet ( vregsInitialized , vregIdx ) )
{
if ( vregInfo - > mDoConservativeLife )
BF_ASSERT ( mVRegInitializedContext . IsSet ( vregsInitialized , vregIdx , BeTrackKind_Uninitialized ) ) ;
else
Fail ( " VReg lifetime error " ) ;
}
}
else
{
Fail ( " VReg lifetime error " ) ;
}
}
}
}
}
for ( auto pred : block - > mPreds )
{
auto & entry = genCtx - > mBlocks [ pred - > mBlockIdx ] ;
BF_ASSERT ( pred = = mBlocks [ pred - > mBlockIdx ] ) ;
auto newSuccLiveness = MergeLiveRegs ( pred - > mSuccLiveness , liveRegs ) ;
if ( newSuccLiveness = = pred - > mSuccLiveness )
continue ;
pred - > mSuccLiveness = newSuccLiveness ;
/*if (mVRegInitializedContext.HasExtraBitsSet(pred->mSuccVRegsInitialized.mBits, vregsInitialized.mBits))
{
mVRegInitializedContext . MergeInplace ( pred - > mSuccVRegsInitialized . mBits , vregsInitialized . mBits ) ;
} */
//if (mVRegInitializedContext.HasExtraEntries(pred->mSuccVRegsInitialized.mList, vregsInitialized.mList))
pred - > mSuccVRegsInitialized = mVRegInitializedContext . Merge ( pred - > mSuccVRegsInitialized , vregsInitialized ) ;
if ( pred - > mBlockIdx > block - > mBlockIdx )
modifiedBlockAfter = true ;
else
modifiedBlockBefore = true ;
entry . mGenerateQueued = true ;
//GenerateLiveness(pred, liveRegs, vregsInitialized, mcColorizer, livenessStats);
}
}
void BeMCContext : : GenerateLiveness ( )
{
//GenerateLiveness_OLD(mcColorizer);
//return;
mLivenessContext . mStats = BeVTrackingContext : : Stats ( ) ;
# ifdef _DEBUG
// So we can Print() while generating liveness (the old values would have been unallocated)
for ( auto mcBlock : mBlocks )
{
for ( auto mcInst : mcBlock - > mInstructions )
{
mcInst - > mLiveness = NULL ;
}
}
# endif
BP_ZONE ( " BeMCContext::GenerateLiveness " ) ;
mLivenessContext . Clear ( ) ;
mLivenessContext . Init ( ( int ) mVRegInfo . size ( ) ) ;
auto emptyList = mLivenessContext . AllocEmptyList ( ) ;
BeVTrackingGenContext genCtx ;
genCtx . mEmptyList = emptyList ;
genCtx . mBlocks . Resize ( mBlocks . size ( ) ) ;
for ( auto mcBlock : mBlocks )
{
mcBlock - > mSuccLiveness = emptyList ;
mcBlock - > mSuccVRegsInitialized = emptyList ;
if ( mTLSVRegIdx ! = - 1 )
{
// Keep TLS alive
SizedArray < int , 1 > vec = { mTLSVRegIdx } ;
mcBlock - > mSuccLiveness = mLivenessContext . Add ( mcBlock - > mSuccLiveness , vec , false ) ;
}
}
while ( true )
{
bool didWork = false ;
// Handle any outstanding pred entries
for ( int blockIdx = ( int ) mBlocks . size ( ) - 1 ; blockIdx > = 0 ; blockIdx - - )
{
auto & entry = genCtx . mBlocks [ blockIdx ] ;
if ( entry . mGenerateQueued )
{
entry . mGenerateQueued = false ;
didWork = true ;
auto block = mBlocks [ blockIdx ] ;
bool modifiedBlockBefore ;
bool modifiedBlockAfter ;
GenerateLiveness ( block , & genCtx , modifiedBlockBefore , modifiedBlockAfter ) ;
if ( modifiedBlockAfter )
break ;
}
}
// If no pred entries, find blocks that haven't been processed yet
if ( ! didWork )
{
for ( int blockIdx = ( int ) mBlocks . size ( ) - 1 ; blockIdx > = 0 ; blockIdx - - )
{
auto & entry = genCtx . mBlocks [ blockIdx ] ;
if ( entry . mGenerateCount = = 0 )
{
didWork = true ;
auto block = mBlocks [ blockIdx ] ;
bool modifiedBlockBefore ;
bool modifiedBlockAfter ;
GenerateLiveness ( block , & genCtx , modifiedBlockBefore , modifiedBlockAfter ) ;
if ( modifiedBlockBefore | | modifiedBlockAfter )
break ;
}
}
}
if ( ! didWork )
break ;
}
int instCount = 0 ;
for ( auto block : mBlocks )
instCount + = ( int ) block - > mInstructions . size ( ) ;
BpEvent ( " GenerateLiveness Results " ,
StrFormat ( " Blocks: %d \n Instructions: %d \n VRegs: %d \n Calls: %d \n Handled Calls: %d \n Processed Instructions: %d \n Liveness Bytes: %d \n "
" Temp Bytes: %d \n Bits Bytes: %d \n List Bytes: %d \n Succ Bytes: %d " ,
mBlocks . size ( ) , instCount , mVRegInfo . size ( ) , genCtx . mCalls , genCtx . mHandledCalls , genCtx . mInstructions , mLivenessContext . mAlloc . GetAllocSize ( ) ,
genCtx . mAlloc . GetAllocSize ( ) , mLivenessContext . mStats . mBitsBytes , mLivenessContext . mStats . mListBytes , mLivenessContext . mStats . mSuccBytes ) . c_str ( ) ) ;
}
void BeMCContext : : IntroduceVRegs ( const BeMCOperand & newVReg , BeMCBlock * block , int startInstIdx , int lastInstIdx )
{
return ;
/*BF_ASSERT((block->mInstructions[startInstIdx]->mKind == BeMCInstKind_Def) && (block->mInstructions[startInstIdx]->mArg0 == newVReg));
BF_ASSERT ( mColorizer . mNodes . size ( ) = = newVReg . mVRegIdx ) ;
mColorizer . mNodes . resize ( mColorizer . mNodes . size ( ) + 1 ) ;
BeVTrackingBits * lastLiveness = NULL ;
for ( int instIdx = startInstIdx + 1 ; instIdx < = lastInstIdx ; instIdx + + )
{
auto inst = block - > mInstructions [ instIdx ] ;
if ( lastLiveness = = NULL )
{
for ( int vregIdx : * inst - > mLiveness )
{
if ( vregIdx > = mLivenessContext . mNumItems )
continue ;
mColorizer . AddEdge ( newVReg . mVRegIdx , vregIdx ) ;
}
}
else
{
int vregIdx = - 1 ;
while ( true )
{
vregIdx = mLivenessContext . GetNextDiffSetIdx ( lastLiveness , inst - > mLiveness , vregIdx ) ;
if ( vregIdx = = - 1 )
break ;
mColorizer . AddEdge ( newVReg . mVRegIdx , vregIdx ) ;
}
}
if ( inst - > mVRegsInitializedEx ! = NULL )
{
auto vregArray = ( BeMCVRegArray * ) mAlloc . AllocBytes ( sizeof ( int ) * ( inst - > mVRegsInitializedEx - > mSize + 1 + 1 ) , sizeof ( int ) ) ;
vregArray - > mSize = inst - > mVRegsInitializedEx - > mSize + 1 ;
for ( int listIdx = 0 ; listIdx < inst - > mVRegsInitializedEx - > mSize ; listIdx + + )
{
mColorizer . AddEdge ( newVReg . mVRegIdx , inst - > mVRegsInitializedEx - > mVRegIndices [ listIdx ] ) ;
vregArray - > mVRegIndices [ listIdx ] = inst - > mVRegsInitializedEx - > mVRegIndices [ listIdx ] ;
}
vregArray - > mVRegIndices [ vregArray - > mSize - 1 ] = newVReg . mVRegIdx ;
inst - > mVRegsInitializedEx = vregArray ;
}
else
{
auto vregArray = ( BeMCVRegArray * ) mAlloc . AllocBytes ( sizeof ( int ) * 2 , sizeof ( int ) ) ;
vregArray - > mSize = 1 ;
vregArray - > mVRegIndices [ 0 ] = newVReg . mVRegIdx ;
inst - > mVRegsInitializedEx = vregArray ;
}
lastLiveness = inst - > mLiveness ;
} */
}
bool BeMCContext : : IsVolatileReg ( X64CPURegister reg )
{
switch ( ResizeRegister ( reg , 8 ) )
{
case X64Reg_RAX :
case X64Reg_RCX :
case X64Reg_RDX :
case X64Reg_R8 :
case X64Reg_R9 :
case X64Reg_R10 :
case X64Reg_R11 :
case X64Reg_XMM0_f64 :
case X64Reg_XMM1_f64 :
case X64Reg_XMM2_f64 :
case X64Reg_XMM3_f64 :
case X64Reg_XMM4_f64 :
case X64Reg_XMM5_f64 :
return true ;
default :
return false ;
}
}
bool BeMCContext : : IsXMMReg ( X64CPURegister reg )
{
return ( reg > = X64Reg_XMM0_f64 ) & & ( reg < = X64Reg_XMM15_f32 ) ;
}
X64CPURegister BeMCContext : : ResizeRegister ( X64CPURegister reg , int numBytes )
{
if ( numBytes = = 8 )
{
switch ( reg )
{
case X64Reg_DIL :
case X64Reg_DI :
case X64Reg_EDI :
case X64Reg_RDI : return X64Reg_RDI ;
case X64Reg_SIL :
case X64Reg_SI :
case X64Reg_ESI :
case X64Reg_RSI : return X64Reg_RSI ;
case X64Reg_AL :
case X64Reg_AH :
case X64Reg_AX :
case X64Reg_EAX :
case X64Reg_RAX : return X64Reg_RAX ;
case X64Reg_DL :
case X64Reg_DH :
case X64Reg_DX :
case X64Reg_EDX :
case X64Reg_RDX : return X64Reg_RDX ;
case X64Reg_CL :
case X64Reg_CH :
case X64Reg_CX :
case X64Reg_ECX :
case X64Reg_RCX : return X64Reg_RCX ;
case X64Reg_BL :
case X64Reg_BH :
case X64Reg_BX :
case X64Reg_EBX :
case X64Reg_RBX : return X64Reg_RBX ;
case X64Reg_R8B :
case X64Reg_R8W :
case X64Reg_R8D :
case X64Reg_R8 : return X64Reg_R8 ;
case X64Reg_R9B :
case X64Reg_R9W :
case X64Reg_R9D :
case X64Reg_R9 : return X64Reg_R9 ;
case X64Reg_R10B :
case X64Reg_R10W :
case X64Reg_R10D :
case X64Reg_R10 : return X64Reg_R10 ;
case X64Reg_R11B :
case X64Reg_R11W :
case X64Reg_R11D :
case X64Reg_R11 : return X64Reg_R11 ;
case X64Reg_R12B :
case X64Reg_R12W :
case X64Reg_R12D :
case X64Reg_R12 : return X64Reg_R12 ;
case X64Reg_R13B :
case X64Reg_R13W :
case X64Reg_R13D :
case X64Reg_R13 : return X64Reg_R13 ;
case X64Reg_R14B :
case X64Reg_R14W :
case X64Reg_R14D :
case X64Reg_R14 : return X64Reg_R14 ;
case X64Reg_R15B :
case X64Reg_R15W :
case X64Reg_R15D :
case X64Reg_R15 : return X64Reg_R15 ;
case X64Reg_XMM0_f32 :
case X64Reg_XMM0_f64 : return X64Reg_XMM0_f64 ;
case X64Reg_XMM1_f32 :
case X64Reg_XMM1_f64 : return X64Reg_XMM1_f64 ;
case X64Reg_XMM2_f32 :
case X64Reg_XMM2_f64 : return X64Reg_XMM2_f64 ;
case X64Reg_XMM3_f32 :
case X64Reg_XMM3_f64 : return X64Reg_XMM3_f64 ;
case X64Reg_XMM4_f32 :
case X64Reg_XMM4_f64 : return X64Reg_XMM4_f64 ;
case X64Reg_XMM5_f32 :
case X64Reg_XMM5_f64 : return X64Reg_XMM5_f64 ;
case X64Reg_XMM6_f32 :
case X64Reg_XMM6_f64 : return X64Reg_XMM6_f64 ;
case X64Reg_XMM7_f32 :
case X64Reg_XMM7_f64 : return X64Reg_XMM7_f64 ;
case X64Reg_XMM8_f32 :
case X64Reg_XMM8_f64 : return X64Reg_XMM8_f64 ;
case X64Reg_XMM9_f32 :
case X64Reg_XMM9_f64 : return X64Reg_XMM9_f64 ;
case X64Reg_XMM10_f32 :
case X64Reg_XMM10_f64 : return X64Reg_XMM10_f64 ;
case X64Reg_XMM11_f32 :
case X64Reg_XMM11_f64 : return X64Reg_XMM11_f64 ;
case X64Reg_XMM12_f32 :
case X64Reg_XMM12_f64 : return X64Reg_XMM12_f64 ;
case X64Reg_XMM13_f32 :
case X64Reg_XMM13_f64 : return X64Reg_XMM13_f64 ;
case X64Reg_XMM14_f32 :
case X64Reg_XMM14_f64 : return X64Reg_XMM14_f64 ;
case X64Reg_XMM15_f32 :
case X64Reg_XMM15_f64 : return X64Reg_XMM15_f64 ;
}
return reg ;
}
if ( numBytes = = 4 )
{
switch ( reg )
{
case X64Reg_DIL :
case X64Reg_DI :
case X64Reg_EDI :
case X64Reg_RDI : return X64Reg_EDI ;
case X64Reg_SIL :
case X64Reg_SI :
case X64Reg_ESI :
case X64Reg_RSI : return X64Reg_ESI ;
case X64Reg_AL :
case X64Reg_AH :
case X64Reg_AX :
case X64Reg_EAX :
case X64Reg_RAX : return X64Reg_EAX ;
case X64Reg_DL :
case X64Reg_DH :
case X64Reg_DX :
case X64Reg_EDX :
case X64Reg_RDX : return X64Reg_EDX ;
case X64Reg_CL :
case X64Reg_CH :
case X64Reg_CX :
case X64Reg_ECX :
case X64Reg_RCX : return X64Reg_ECX ;
case X64Reg_BL :
case X64Reg_BH :
case X64Reg_BX :
case X64Reg_EBX :
case X64Reg_RBX : return X64Reg_EBX ;
case X64Reg_R8B :
case X64Reg_R8W :
case X64Reg_R8D :
case X64Reg_R8 : return X64Reg_R8D ;
case X64Reg_R9B :
case X64Reg_R9W :
case X64Reg_R9D :
case X64Reg_R9 : return X64Reg_R9D ;
case X64Reg_R10B :
case X64Reg_R10W :
case X64Reg_R10D :
case X64Reg_R10 : return X64Reg_R10D ;
case X64Reg_R11B :
case X64Reg_R11W :
case X64Reg_R11D :
case X64Reg_R11 : return X64Reg_R11D ;
case X64Reg_R12B :
case X64Reg_R12W :
case X64Reg_R12D :
case X64Reg_R12 : return X64Reg_R12D ;
case X64Reg_R13B :
case X64Reg_R13W :
case X64Reg_R13D :
case X64Reg_R13 : return X64Reg_R13D ;
case X64Reg_R14B :
case X64Reg_R14W :
case X64Reg_R14D :
case X64Reg_R14 : return X64Reg_R14D ;
case X64Reg_R15B :
case X64Reg_R15W :
case X64Reg_R15D :
case X64Reg_R15 : return X64Reg_R15D ;
case X64Reg_XMM0_f32 :
case X64Reg_XMM0_f64 : return X64Reg_XMM0_f32 ;
case X64Reg_XMM1_f32 :
case X64Reg_XMM1_f64 : return X64Reg_XMM1_f32 ;
case X64Reg_XMM2_f32 :
case X64Reg_XMM2_f64 : return X64Reg_XMM2_f32 ;
case X64Reg_XMM3_f32 :
case X64Reg_XMM3_f64 : return X64Reg_XMM3_f32 ;
case X64Reg_XMM4_f32 :
case X64Reg_XMM4_f64 : return X64Reg_XMM4_f32 ;
case X64Reg_XMM5_f32 :
case X64Reg_XMM5_f64 : return X64Reg_XMM5_f32 ;
case X64Reg_XMM6_f32 :
case X64Reg_XMM6_f64 : return X64Reg_XMM6_f32 ;
case X64Reg_XMM7_f32 :
case X64Reg_XMM7_f64 : return X64Reg_XMM7_f32 ;
case X64Reg_XMM8_f32 :
case X64Reg_XMM8_f64 : return X64Reg_XMM8_f32 ;
case X64Reg_XMM9_f32 :
case X64Reg_XMM9_f64 : return X64Reg_XMM9_f32 ;
case X64Reg_XMM10_f32 :
case X64Reg_XMM10_f64 : return X64Reg_XMM10_f32 ;
case X64Reg_XMM11_f32 :
case X64Reg_XMM11_f64 : return X64Reg_XMM11_f32 ;
case X64Reg_XMM12_f32 :
case X64Reg_XMM12_f64 : return X64Reg_XMM12_f32 ;
case X64Reg_XMM13_f32 :
case X64Reg_XMM13_f64 : return X64Reg_XMM13_f32 ;
case X64Reg_XMM14_f32 :
case X64Reg_XMM14_f64 : return X64Reg_XMM14_f32 ;
case X64Reg_XMM15_f32 :
case X64Reg_XMM15_f64 : return X64Reg_XMM15_f32 ;
}
}
if ( numBytes = = 2 )
{
switch ( reg )
{
case X64Reg_DIL :
case X64Reg_DI :
case X64Reg_EDI :
case X64Reg_RDI : return X64Reg_DI ;
case X64Reg_SIL :
case X64Reg_SI :
case X64Reg_ESI :
case X64Reg_RSI : return X64Reg_SI ;
case X64Reg_AL :
case X64Reg_AH :
case X64Reg_AX :
case X64Reg_EAX :
case X64Reg_RAX : return X64Reg_AX ;
case X64Reg_DL :
case X64Reg_DH :
case X64Reg_DX :
case X64Reg_EDX :
case X64Reg_RDX : return X64Reg_DX ;
case X64Reg_CL :
case X64Reg_CH :
case X64Reg_CX :
case X64Reg_ECX :
case X64Reg_RCX : return X64Reg_CX ;
case X64Reg_BL :
case X64Reg_BH :
case X64Reg_BX :
case X64Reg_EBX :
case X64Reg_RBX : return X64Reg_BX ;
case X64Reg_R8B :
case X64Reg_R8W :
case X64Reg_R8D :
case X64Reg_R8 : return X64Reg_R8W ;
case X64Reg_R9B :
case X64Reg_R9W :
case X64Reg_R9D :
case X64Reg_R9 : return X64Reg_R9W ;
case X64Reg_R10B :
case X64Reg_R10W :
case X64Reg_R10D :
case X64Reg_R10 : return X64Reg_R10W ;
case X64Reg_R11B :
case X64Reg_R11W :
case X64Reg_R11D :
case X64Reg_R11 : return X64Reg_R11W ;
case X64Reg_R12B :
case X64Reg_R12W :
case X64Reg_R12D :
case X64Reg_R12 : return X64Reg_R12W ;
case X64Reg_R13B :
case X64Reg_R13W :
case X64Reg_R13D :
case X64Reg_R13 : return X64Reg_R13W ;
case X64Reg_R14B :
case X64Reg_R14W :
case X64Reg_R14D :
case X64Reg_R14 : return X64Reg_R14W ;
case X64Reg_R15B :
case X64Reg_R15W :
case X64Reg_R15D :
case X64Reg_R15 : return X64Reg_R15W ;
}
}
if ( numBytes = = 1 )
{
switch ( reg )
{
case X64Reg_DIL :
case X64Reg_DI :
case X64Reg_EDI :
case X64Reg_RDI : return X64Reg_DIL ;
case X64Reg_SIL :
case X64Reg_SI :
case X64Reg_ESI :
case X64Reg_RSI : return X64Reg_SIL ;
case X64Reg_AH : return X64Reg_AH ;
case X64Reg_AL :
case X64Reg_AX :
case X64Reg_EAX :
case X64Reg_RAX : return X64Reg_AL ;
case X64Reg_DH : return X64Reg_DH ;
case X64Reg_DL :
case X64Reg_DX :
case X64Reg_EDX :
case X64Reg_RDX : return X64Reg_DL ;
case X64Reg_CH : return X64Reg_CH ;
case X64Reg_CL :
case X64Reg_CX :
case X64Reg_ECX :
case X64Reg_RCX : return X64Reg_CL ;
case X64Reg_BH : return X64Reg_BH ;
case X64Reg_BL :
case X64Reg_BX :
case X64Reg_EBX :
case X64Reg_RBX : return X64Reg_BL ;
case X64Reg_R8B :
case X64Reg_R8W :
case X64Reg_R8D :
case X64Reg_R8 : return X64Reg_R8B ;
case X64Reg_R9B :
case X64Reg_R9W :
case X64Reg_R9D :
case X64Reg_R9 : return X64Reg_R9B ;
case X64Reg_R10B :
case X64Reg_R10W :
case X64Reg_R10D :
case X64Reg_R10 : return X64Reg_R10B ;
case X64Reg_R11B :
case X64Reg_R11W :
case X64Reg_R11D :
case X64Reg_R11 : return X64Reg_R11B ;
case X64Reg_R12B :
case X64Reg_R12W :
case X64Reg_R12D :
case X64Reg_R12 : return X64Reg_R12B ;
case X64Reg_R13B :
case X64Reg_R13W :
case X64Reg_R13D :
case X64Reg_R13 : return X64Reg_R13B ;
case X64Reg_R14B :
case X64Reg_R14W :
case X64Reg_R14D :
case X64Reg_R14 : return X64Reg_R14B ;
case X64Reg_R15B :
case X64Reg_R15W :
case X64Reg_R15D :
case X64Reg_R15 : return X64Reg_R15B ;
}
}
return X64Reg_None ;
}
bool BeMCContext : : IsAddress ( BeMCOperand & operand )
{
if ( operand . mKind = = BeMCOperandKind_VRegAddr )
return true ;
if ( operand . mKind ! = BeMCOperandKind_VReg )
return false ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
return IsAddress ( vregInfo - > mRelTo ) ;
}
bool BeMCContext : : IsAddressable ( BeMCOperand & operand )
{
if ( operand . mKind = = BeMCOperandKind_Symbol )
return true ;
if ( operand . mKind = = BeMCOperandKind_NativeReg )
return true ;
if ( operand . mKind = = BeMCOperandKind_VRegAddr )
return true ;
if ( operand . mKind ! = BeMCOperandKind_VReg )
return false ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
return IsAddressable ( vregInfo - > mRelTo ) ;
}
bool BeMCContext : : IsVRegExpr ( BeMCOperand & operand )
{
if ( ! operand . IsVRegAny ( ) )
return false ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
return vregInfo - > mIsExpr ;
}
void BeMCContext : : FixOperand ( BeMCOperand & operand )
{
// We don't want to check for VRegLoad, that would erase the dereference
if ( ( operand . mKind ! = BeMCOperandKind_VReg ) & & ( operand . mKind ! = BeMCOperandKind_VRegAddr ) )
return ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( vregInfo - > mReg ! = X64Reg_None )
{
operand . mKind = BeMCOperandKind_NativeReg ;
operand . mReg = vregInfo - > mReg ;
return ;
}
if ( ( vregInfo - > mIsRetVal ) & & ( mCompositeRetVRegIdx ! = - 1 ) & & ( mCompositeRetVRegIdx ! = operand . mVRegIdx ) )
{
BF_ASSERT ( mCompositeRetVRegIdx ! = - 1 ) ;
BeMCOperand origOperand = operand ;
operand = BeMCOperand : : FromVReg ( mCompositeRetVRegIdx ) ;
if ( ( origOperand . mKind = = BeMCOperandKind_VReg ) & & ( vregInfo - > mType - > IsComposite ( ) ) )
operand . mKind = BeMCOperandKind_VRegLoad ;
FixOperand ( operand ) ;
//auto retVRegInfo = mVRegInfo[mCompositeRetVRegIdx];
//operand = BeMCOperand::FromReg(retVRegInfo->mReg);
}
if ( vregInfo - > IsDirectRelToAny ( ) )
{
auto checkOperand = vregInfo - > mRelTo ;
if ( checkOperand . IsVReg ( ) )
FixOperand ( checkOperand ) ;
if ( checkOperand . IsNativeReg ( ) )
{
auto resizedReg = ResizeRegister ( checkOperand . mReg , vregInfo - > mType - > mSize ) ;
if ( resizedReg ! = X64Reg_None )
operand = BeMCOperand : : FromReg ( resizedReg ) ;
}
else if ( checkOperand . mKind = = BeMCOperandKind_Symbol )
{
auto symbol = mCOFFObject - > mSymbols [ checkOperand . mSymbolIdx ] ;
if ( AreTypesEquivalent ( vregInfo - > mType , symbol - > mType ) )
{
if ( checkOperand . mKind = = BeMCOperandKind_VRegAddr )
{
operand = BeMCOperand : : ToAddr ( checkOperand ) ;
}
else
operand = checkOperand ;
}
}
else if ( checkOperand . mKind = = BeMCOperandKind_SymbolAddr )
{
operand = checkOperand ;
}
}
}
BeMCOperand BeMCContext : : GetFixedOperand ( const BeMCOperand & operand )
{
BeMCOperand copyOp = operand ;
FixOperand ( copyOp ) ;
return copyOp ;
}
uint8 BeMCContext : : GetREX ( const BeMCOperand & r , const BeMCOperand & rm , bool is64Bit )
{
//bool is64Bit = false;
bool is64BitExR = false ;
bool is64BitExRM = false ;
bool forceRex = false ;
if ( r . mKind = = BeMCOperandKind_NativeReg )
{
/*if ((r.mReg >= X64Reg_RAX) && (r.mReg <= X64Reg_EFL))
BF_ASSERT ( is64Bit ) ; */
switch ( r . mReg )
{
case X64Reg_SIL :
case X64Reg_DIL :
forceRex = true ;
break ;
case X64Reg_R8 : case X64Reg_R9 : case X64Reg_R10 : case X64Reg_R11 : case X64Reg_R12 : case X64Reg_R13 : case X64Reg_R14 : case X64Reg_R15 :
case X64Reg_R8D : case X64Reg_R9D : case X64Reg_R10D : case X64Reg_R11D : case X64Reg_R12D : case X64Reg_R13D : case X64Reg_R14D : case X64Reg_R15D :
case X64Reg_R8W : case X64Reg_R9W : case X64Reg_R10W : case X64Reg_R11W : case X64Reg_R12W : case X64Reg_R13W : case X64Reg_R14W : case X64Reg_R15W :
case X64Reg_R8B : case X64Reg_R9B : case X64Reg_R10B : case X64Reg_R11B : case X64Reg_R12B : case X64Reg_R13B : case X64Reg_R14B : case X64Reg_R15B :
case X64Reg_XMM8_f64 : case X64Reg_XMM9_f64 : case X64Reg_XMM10_f64 : case X64Reg_XMM11_f64 :
case X64Reg_XMM12_f64 : case X64Reg_XMM13_f64 : case X64Reg_XMM14_f64 : case X64Reg_XMM15_f64 :
case X64Reg_XMM8_f32 : case X64Reg_XMM9_f32 : case X64Reg_XMM10_f32 : case X64Reg_XMM11_f32 :
case X64Reg_XMM12_f32 : case X64Reg_XMM13_f32 : case X64Reg_XMM14_f32 : case X64Reg_XMM15_f32 :
is64BitExR = true ;
}
}
bool hasSibExRM = false ;
if ( rm . mKind = = BeMCOperandKind_NativeReg )
{
switch ( rm . mReg )
{
case X64Reg_SIL :
case X64Reg_DIL :
forceRex = true ;
break ;
case X64Reg_R8 : case X64Reg_R9 : case X64Reg_R10 : case X64Reg_R11 : case X64Reg_R12 : case X64Reg_R13 : case X64Reg_R14 : case X64Reg_R15 :
case X64Reg_R8D : case X64Reg_R9D : case X64Reg_R10D : case X64Reg_R11D : case X64Reg_R12D : case X64Reg_R13D : case X64Reg_R14D : case X64Reg_R15D :
case X64Reg_R8W : case X64Reg_R9W : case X64Reg_R10W : case X64Reg_R11W : case X64Reg_R12W : case X64Reg_R13W : case X64Reg_R14W : case X64Reg_R15W :
case X64Reg_R8B : case X64Reg_R9B : case X64Reg_R10B : case X64Reg_R11B : case X64Reg_R12B : case X64Reg_R13B : case X64Reg_R14B : case X64Reg_R15B :
case X64Reg_XMM8_f64 : case X64Reg_XMM9_f64 : case X64Reg_XMM10_f64 : case X64Reg_XMM11_f64 :
case X64Reg_XMM12_f64 : case X64Reg_XMM13_f64 : case X64Reg_XMM14_f64 : case X64Reg_XMM15_f64 :
case X64Reg_XMM8_f32 : case X64Reg_XMM9_f32 : case X64Reg_XMM10_f32 : case X64Reg_XMM11_f32 :
case X64Reg_XMM12_f32 : case X64Reg_XMM13_f32 : case X64Reg_XMM14_f32 : case X64Reg_XMM15_f32 :
is64BitExRM = true ;
}
}
else if ( rm . IsVRegAny ( ) )
{
auto vregInfo = mVRegInfo [ rm . mVRegIdx ] ;
if ( vregInfo - > IsDirectRelTo ( ) )
return GetREX ( r , vregInfo - > mRelTo , is64Bit ) ;
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
GetRMParams ( rm , regA , regB , bScale , disp ) ;
is64BitExRM | = ( ( regA > = X64Reg_R8 ) & & ( regA < = X64Reg_R15 ) ) | | ( ( regA > = X64Reg_R8D ) & & ( regA < = X64Reg_R15D ) ) ;
hasSibExRM | = ( ( regB > = X64Reg_R8 ) & & ( regB < = X64Reg_R15 ) ) | | ( ( regB > = X64Reg_R8D ) & & ( regB < = X64Reg_R15D ) ) ;
}
else if ( rm . IsSymbol ( ) )
{
auto sym = mCOFFObject - > mSymbols [ rm . mSymbolIdx ] ;
if ( sym - > mIsTLS )
{
auto tlsReg = mVRegInfo [ mTLSVRegIdx ] - > mReg ;
is64BitExRM | = ( tlsReg > = X64Reg_R8 ) & & ( tlsReg < = X64Reg_R15 ) ;
}
}
uint8 flags = 0 ;
if ( is64Bit )
flags | = 8 ;
if ( is64BitExR )
flags | = 4 ;
if ( hasSibExRM )
flags | = 2 ;
if ( is64BitExRM )
flags | = 1 ;
if ( ( flags ! = 0 ) | | ( forceRex ) )
return ( uint8 ) ( 0x40 | flags ) ;
return 0 ;
}
void BeMCContext : : EmitREX ( const BeMCOperand & r , const BeMCOperand & rm , bool is64Bit )
{
uint8 rex = GetREX ( r , rm , is64Bit ) ;
if ( rex ! = 0 )
mOut . Write ( rex ) ;
}
uint8 BeMCContext : : EncodeRegNum ( X64CPURegister regNum )
{
switch ( regNum )
{
case X64Reg_AL :
case X64Reg_AX :
case X64Reg_EAX :
case X64Reg_RAX :
case X64Reg_R8 :
case X64Reg_R8D :
case X64Reg_R8W :
case X64Reg_R8B :
case X64Reg_MM0 :
case X64Reg_XMM0_f32 :
case X64Reg_XMM0_f64 :
case X64Reg_XMM8_f32 :
case X64Reg_XMM8_f64 :
return 0 ;
case X64Reg_CL :
case X64Reg_CX :
case X64Reg_ECX :
case X64Reg_RCX :
case X64Reg_R9 :
case X64Reg_R9D :
case X64Reg_R9W :
case X64Reg_R9B :
case X64Reg_MM1 :
case X64Reg_XMM1_f32 :
case X64Reg_XMM1_f64 :
case X64Reg_XMM9_f32 :
case X64Reg_XMM9_f64 :
return 1 ;
case X64Reg_DL :
case X64Reg_DX :
case X64Reg_EDX :
case X64Reg_RDX :
case X64Reg_R10 :
case X64Reg_R10D :
case X64Reg_R10W :
case X64Reg_R10B :
case X64Reg_MM2 :
case X64Reg_XMM2_f32 :
case X64Reg_XMM2_f64 :
case X64Reg_XMM10_f32 :
case X64Reg_XMM10_f64 :
return 2 ;
case X64Reg_BL :
case X64Reg_BX :
case X64Reg_EBX :
case X64Reg_RBX :
case X64Reg_R11 :
case X64Reg_R11D :
case X64Reg_R11W :
case X64Reg_R11B :
case X64Reg_MM3 :
case X64Reg_XMM3_f32 :
case X64Reg_XMM3_f64 :
case X64Reg_XMM11_f32 :
case X64Reg_XMM11_f64 :
return 3 ;
case X64Reg_None : // Useful for SIB/RM addr encodings
case X64Reg_AH :
//case X64Reg_SP:
//case X64Reg_ESP:
case X64Reg_RSP :
case X64Reg_R12 :
case X64Reg_R12D :
case X64Reg_R12W :
case X64Reg_R12B :
case X64Reg_MM4 :
case X64Reg_XMM4_f32 :
case X64Reg_XMM4_f64 :
case X64Reg_XMM12_f32 :
case X64Reg_XMM12_f64 :
return 4 ;
case X64Reg_CH :
//case X64Reg_BP:
//case X64Reg_EBP:
case X64Reg_RBP :
case X64Reg_R13 :
case X64Reg_R13D :
case X64Reg_R13W :
case X64Reg_R13B :
case X64Reg_MM5 :
case X64Reg_XMM5_f32 :
case X64Reg_XMM5_f64 :
case X64Reg_XMM13_f32 :
case X64Reg_XMM13_f64 :
return 5 ;
case X64Reg_DH :
case X64Reg_SIL :
case X64Reg_SI :
case X64Reg_ESI :
case X64Reg_RSI :
case X64Reg_R14 :
case X64Reg_R14D :
case X64Reg_R14W :
case X64Reg_R14B :
case X64Reg_MM6 :
case X64Reg_XMM6_f32 :
case X64Reg_XMM6_f64 :
case X64Reg_XMM14_f32 :
case X64Reg_XMM14_f64 :
return 6 ;
case X64Reg_BH :
case X64Reg_DIL :
case X64Reg_DI :
case X64Reg_EDI :
case X64Reg_RDI :
case X64Reg_R15 :
case X64Reg_R15D :
case X64Reg_R15W :
case X64Reg_R15B :
case X64Reg_MM7 :
case X64Reg_XMM7_f32 :
case X64Reg_XMM7_f64 :
case X64Reg_XMM15_f32 :
case X64Reg_XMM15_f64 :
return 7 ;
}
Fail ( " Invalid reg " ) ;
return - 1 ;
}
int BeMCContext : : GetRegSize ( int regNum )
{
if ( ( regNum > = X64Reg_EAX ) & & ( regNum < = X64Reg_EDI ) )
return 4 ;
if ( ( regNum > = X64Reg_AX ) & & ( regNum < = X64Reg_BX ) )
return 2 ;
if ( ( regNum > = X64Reg_AL ) & & ( regNum < = X64Reg_BH ) )
return 1 ;
return 8 ;
}
BeMCRMMode BeMCContext : : ValidateRMResult ( const BeMCOperand & operand , X64CPURegister & regA , X64CPURegister & regB , int & bScale , int & disp , int * errorVReg , BeMCRMMode result , bool doValidate )
{
if ( ! doValidate )
return result ;
//TODO: WTF- this previous version just seems to be wrong! Why did think this was true? the REX.X and REX.B flags fix these
// in a SIB, the base can't be R13 (which is RBP+REX), and the scaled index can't be R12 (which is RSP+REX)
//if ((regB != X64Reg_None) &&
// ((regA == X64Reg_R13) || (regB == X64Reg_R12)))
//{
// // We can't just swap the regs if we have a scale applied
// if (bScale != 1)
// {
// if (errorVReg != NULL)
// *errorVReg = -2; // Scale error
// return BeMCRMMode_Invalid;
// }
// BF_SWAP(regA, regB);
//}
// In a SIB, the base can't be RBP, and the scaled index can't be RSP
if ( ( regB ! = X64Reg_None ) & &
( ( regA = = X64Reg_RBP ) | | ( regB = = X64Reg_RSP ) ) )
{
// We can't just swap the regs if we have a scale applied
if ( bScale ! = 1 )
{
if ( errorVReg ! = NULL )
* errorVReg = - 2 ; // Scale error
return BeMCRMMode_Invalid ;
}
BF_SWAP ( regA , regB ) ;
}
return result ;
}
BeMCRMMode BeMCContext : : GetRMParams ( const BeMCOperand & operand , X64CPURegister & regA , X64CPURegister & regB , int & bScale , int & disp , int * errorVReg , bool doValidate )
{
BeMCRMMode rmMode = BeMCRMMode_Invalid ;
if ( operand . mKind = = BeMCOperandKind_NativeReg )
{
if ( regA = = X64Reg_None )
regA = operand . mReg ;
else if ( regB = = X64Reg_None )
regB = operand . mReg ;
else
return BeMCRMMode_Invalid ;
return ValidateRMResult ( operand , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Direct , doValidate ) ;
}
else if ( operand . IsImmediateInt ( ) )
{
disp + = ( int ) operand . mImmediate ;
return ValidateRMResult ( operand , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Direct , doValidate ) ;
}
if ( operand . mKind = = BeMCOperandKind_VReg )
{
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( ! vregInfo - > mIsExpr )
{
auto reg = vregInfo - > mReg ;
if ( reg ! = X64Reg_None )
{
if ( regA = = X64Reg_None )
regA = reg ;
else if ( regB = = X64Reg_None )
regB = reg ;
else
return BeMCRMMode_Invalid ;
return ValidateRMResult ( operand , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Direct , doValidate ) ;
}
auto result = GetRMParams ( BeMCOperand : : ToAddr ( operand ) , regA , regB , bScale , disp , errorVReg , doValidate ) ;
if ( result = = BeMCRMMode_Invalid )
return BeMCRMMode_Invalid ;
BF_ASSERT ( result = = BeMCRMMode_Direct ) ;
return ValidateRMResult ( BeMCOperand : : ToAddr ( operand ) , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Deref , doValidate ) ;
}
// Fall through
}
else if ( operand . mKind = = BeMCOperandKind_VRegAddr )
{
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( vregInfo - > mIsExpr )
{
if ( vregInfo - > IsDirectRelToAny ( ) )
{
if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_VReg )
return GetRMParams ( BeMCOperand : : FromVRegAddr ( vregInfo - > mRelTo . mVRegIdx ) , regA , regB , bScale , disp , errorVReg , doValidate ) ;
else if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_VRegLoad )
return GetRMParams ( BeMCOperand : : FromVReg ( vregInfo - > mRelTo . mVRegIdx ) , regA , regB , bScale , disp , errorVReg , doValidate ) ;
}
* errorVReg = operand . mVRegIdx ;
return BeMCRMMode_Invalid ;
}
BF_ASSERT ( ! vregInfo - > mIsExpr ) ;
BF_ASSERT ( vregInfo - > mReg = = X64Reg_None ) ;
X64CPURegister reg = X64Reg_None ;
if ( ( vregInfo - > mIsRetVal ) & & ( mCompositeRetVRegIdx ! = - 1 ) & & ( mCompositeRetVRegIdx ! = operand . mVRegIdx ) )
{
return GetRMParams ( BeMCOperand : : FromVReg ( mCompositeRetVRegIdx ) , regA , regB , bScale , disp , errorVReg , doValidate ) ;
}
reg = mUseBP ? X64Reg_RBP : X64Reg_RSP ;
disp = mStackSize + vregInfo - > mFrameOffset ;
if ( regA = = X64Reg_None )
regA = reg ;
else if ( regB = = X64Reg_None )
regB = reg ;
return ValidateRMResult ( operand , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Direct , doValidate ) ;
}
else if ( operand . mKind = = BeMCOperandKind_VRegLoad )
{
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( ! vregInfo - > mIsExpr )
{
auto reg = vregInfo - > mReg ;
if ( reg = = X64Reg_None )
{
if ( errorVReg ! = NULL )
* errorVReg = operand . mVRegIdx ;
return BeMCRMMode_Invalid ;
}
if ( regA = = X64Reg_None )
regA = reg ;
else if ( regB = = X64Reg_None )
regB = reg ;
return ValidateRMResult ( operand , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Deref , doValidate ) ;
}
}
else
{
return BeMCRMMode_Invalid ;
}
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
BF_ASSERT ( vregInfo - > mIsExpr ) ;
if ( vregInfo - > mRelTo )
{
auto oldRegA = regA ;
auto result = GetRMParams ( vregInfo - > mRelTo , regA , regB , bScale , disp , errorVReg , false ) ;
if ( result = = BeMCRMMode_Invalid )
{
if ( ( errorVReg ! = NULL ) & & ( * errorVReg = = - 1 ) )
{
* errorVReg = operand . mVRegIdx ;
}
return BeMCRMMode_Invalid ;
}
if ( result = = BeMCRMMode_Deref )
{
// A deref can only stand alone, and no double-derefs
if ( ( vregInfo - > mRelOffset ) | | ( vregInfo - > mRelOffsetScale ! = 1 ) | | ( operand . mKind = = BeMCOperandKind_VRegLoad ) )
{
if ( errorVReg ! = NULL )
{
BF_ASSERT ( vregInfo - > mRelTo . IsVRegAny ( ) ) ;
* errorVReg = vregInfo - > mRelTo . mVRegIdx ;
// For some reason we had changed this to:
//*errorVReg = operand.mVRegIdx;
// This doesn't work, it's the deref that we want to isolate, otherwise we just end up creating another invalid expression
}
return BeMCRMMode_Invalid ;
}
if ( operand . mKind = = BeMCOperandKind_VRegAddr )
return ValidateRMResult ( vregInfo - > mRelTo , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Direct , doValidate ) ;
else if ( operand . mKind = = BeMCOperandKind_VReg )
return ValidateRMResult ( vregInfo - > mRelTo , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Deref , doValidate ) ;
else
NotImpl ( ) ;
}
}
if ( vregInfo - > mRelOffset )
{
bool relToComplicated = ( regB ! = X64Reg_None ) | | ( bScale ! = 1 ) ;
auto result = GetRMParams ( vregInfo - > mRelOffset , regA , regB , bScale , disp , errorVReg , false ) ;
if ( result = = BeMCRMMode_Invalid )
{
if ( errorVReg ! = NULL )
{
// Pick the "most complicated" between relOffset and relTo?
if ( relToComplicated )
{
BF_ASSERT ( vregInfo - > mRelTo . IsVRegAny ( ) ) ;
* errorVReg = vregInfo - > mRelTo . mVRegIdx ;
}
else
{
BF_ASSERT ( vregInfo - > mRelOffset . IsVRegAny ( ) ) ;
* errorVReg = vregInfo - > mRelOffset . mVRegIdx ;
}
}
return BeMCRMMode_Invalid ;
}
if ( result = = BeMCRMMode_Deref ) // Deref only allowed on relTo
{
if ( errorVReg ! = NULL )
{
BF_ASSERT ( vregInfo - > mRelOffset . IsVRegAny ( ) ) ;
* errorVReg = vregInfo - > mRelOffset . mVRegIdx ;
}
return BeMCRMMode_Invalid ;
}
}
bool success = true ;
if ( vregInfo - > mRelOffsetScale ! = 1 )
{
if ( bScale ! = 1 )
success = false ;
bScale = vregInfo - > mRelOffsetScale ;
if ( ( bScale ! = 2 ) & & ( bScale ! = 4 ) & & ( bScale ! = 8 ) )
success = false ;
if ( regB = = X64Reg_None )
{
regB = regA ;
regA = X64Reg_None ;
}
}
if ( ! success )
{
if ( ( errorVReg ! = NULL ) & & ( * errorVReg = = - 1 ) )
* errorVReg = operand . mVRegIdx ;
}
if ( success )
{
if ( operand . mKind = = BeMCOperandKind_VRegLoad )
return ValidateRMResult ( vregInfo - > mRelOffset , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Deref , doValidate ) ;
else
return ValidateRMResult ( vregInfo - > mRelOffset , regA , regB , bScale , disp , errorVReg , BeMCRMMode_Direct , doValidate ) ;
}
else
return BeMCRMMode_Invalid ;
}
void BeMCContext : : DisableRegister ( const BeMCOperand & operand , X64CPURegister reg )
{
auto vregInfo = GetVRegInfo ( operand ) ;
if ( vregInfo = = NULL )
return ;
switch ( reg )
{
case X64Reg_RAX :
vregInfo - > mDisableRAX = true ;
break ;
case X64Reg_SIL :
vregInfo - > mDisableEx = true ;
break ;
default :
NotImpl ( ) ;
}
if ( vregInfo - > mRelTo )
DisableRegister ( vregInfo - > mRelTo , reg ) ;
if ( vregInfo - > mRelOffset )
DisableRegister ( vregInfo - > mRelOffset , reg ) ;
}
void BeMCContext : : MarkInvalidRMRegs ( const BeMCOperand & operand )
{
if ( ! operand . IsVRegAny ( ) )
return ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( vregInfo - > mReg = = X64Reg_R12 ) // Can't allow a scaled R12, it's illegal in ModRM
vregInfo - > mDisableR12 = true ;
if ( vregInfo - > mReg = = X64Reg_R13 ) // Can't allow a base R13, it's illegal in ModRM
vregInfo - > mDisableR13 = true ;
MarkInvalidRMRegs ( vregInfo - > mRelTo ) ;
MarkInvalidRMRegs ( vregInfo - > mRelOffset ) ;
}
void BeMCContext : : GetUsedRegs ( const BeMCOperand & operand , X64CPURegister & regA , X64CPURegister & regB )
{
int scale = 1 ;
int offset = 0 ;
GetRMParams ( operand , regA , regB , scale , offset , NULL ) ;
if ( regA ! = X64Reg_None )
regA = ResizeRegister ( regA , 8 ) ;
if ( regB ! = X64Reg_None )
regB = ResizeRegister ( regB , 8 ) ;
}
BeMCRMMode BeMCContext : : GetRMForm ( const BeMCOperand & operand , bool & isMulti )
{
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
auto result = GetRMParams ( operand , regA , regB , bScale , disp ) ;
isMulti = ( regB ! = X64Reg_None ) | | ( disp ! = 0 ) ;
return result ;
}
void BeMCContext : : GetValAddr ( const BeMCOperand & operand , X64CPURegister & reg , int & offset )
{
if ( operand . IsNativeReg ( ) )
{
reg = operand . mReg ;
return ;
}
BF_ASSERT ( operand . IsVRegAny ( ) ) ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( operand . mKind = = BeMCOperandKind_VReg )
{
if ( vregInfo - > mRelTo )
{
GetValAddr ( vregInfo - > mRelTo , reg , offset ) ;
if ( vregInfo - > mRelOffset )
{
BF_ASSERT ( vregInfo - > mRelOffset . IsImmediateInt ( ) ) ;
offset + = vregInfo - > mRelOffset . mImmediate ;
}
return ;
}
BF_ASSERT ( vregInfo - > mType - > IsPointer ( ) ) ;
BF_ASSERT ( vregInfo - > mReg ! = X64Reg_None ) ;
reg = vregInfo - > mReg ;
return ;
}
if ( ( vregInfo - > mIsRetVal ) & & ( mCompositeRetVRegIdx ! = - 1 ) & & ( mCompositeRetVRegIdx ! = operand . mVRegIdx ) )
{
BF_ASSERT ( mCompositeRetVRegIdx ! = - 1 ) ;
GetValAddr ( BeMCOperand : : FromVRegAddr ( mCompositeRetVRegIdx ) , reg , offset ) ;
return ;
}
while ( vregInfo - > IsDirectRelTo ( ) )
{
vregInfo = GetVRegInfo ( vregInfo - > mRelTo ) ;
}
reg = mUseBP ? X64Reg_RBP : X64Reg_RSP ;
offset = mStackSize + vregInfo - > mFrameOffset ;
}
int BeMCContext : : GetHighestVRegRef ( const BeMCOperand & operand )
{
if ( ! operand . IsVRegAny ( ) )
return - 1 ;
int highestIdx = operand . mVRegIdx ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( vregInfo - > mRelTo )
highestIdx = std : : max ( highestIdx , GetHighestVRegRef ( vregInfo - > mRelTo ) ) ;
if ( vregInfo - > mRelOffset )
highestIdx = std : : max ( highestIdx , GetHighestVRegRef ( vregInfo - > mRelOffset ) ) ;
return highestIdx ;
}
uint8 BeMCContext : : GetJumpOpCode ( BeCmpKind cmpKind , bool isLong )
{
if ( isLong )
{
switch ( cmpKind )
{
case BeCmpKind_None : // JMP
return 0xE9 ;
case BeCmpKind_SLT : // JL
return 0x8C ;
case BeCmpKind_ULT : // JB
return 0x82 ;
case BeCmpKind_SLE : // JLE
return 0x8E ;
case BeCmpKind_ULE : // JBE
return 0x86 ;
case BeCmpKind_EQ : // JE
return 0x84 ;
case BeCmpKind_NE : // JNE
return 0x85 ;
case BeCmpKind_SGT : // JG
return 0x8F ;
case BeCmpKind_UGT : // JA
return 0x87 ;
case BeCmpKind_SGE : // JGE
return 0x8D ;
case BeCmpKind_UGE : // JAE
return 0x83 ;
}
}
else
{
switch ( cmpKind )
{
case BeCmpKind_None : // JMP
return 0xEB ;
case BeCmpKind_SLT : // JL
return 0x7C ;
case BeCmpKind_ULT : // JB
return 0x72 ;
case BeCmpKind_SLE : // JLE
return 0x7E ;
case BeCmpKind_ULE : // JBE
return 0x76 ;
case BeCmpKind_EQ : // JE
return 0x74 ;
case BeCmpKind_NE : // JNE
return 0x75 ;
case BeCmpKind_SGT : // JG
return 0x7F ;
case BeCmpKind_UGT : // JA
return 0x77 ;
case BeCmpKind_SGE : // JGE
return 0x7D ;
case BeCmpKind_UGE : // JAE
return 0x73 ;
}
}
return 0 ;
}
void BeMCContext : : Emit ( uint8 val )
{
mOut . Write ( val ) ;
}
void BeMCContext : : EmitModRM ( int mod , int reg , int rm )
{
mOut . Write ( ( uint8 ) ( ( mod < < 6 ) | ( reg < < 3 ) | rm ) ) ;
}
void BeMCContext : : EmitModRMRel ( int rx , X64CPURegister regA , X64CPURegister regB , int bScale , int relOffset )
{
if ( regB ! = X64Reg_None )
{
// We can't encode regA as RBP in the SIB base
BF_ASSERT ( regA ! = X64Reg_RBP ) ;
// We can't encode RegB as RSP in the SIB index
BF_ASSERT ( regB ! = X64Reg_RSP ) ;
}
uint8 modRM = ( rx < < 3 ) ;
if ( ( regB = = X64Reg_None ) & & ( regA ! = X64Reg_RSP ) & & ( regA ! = X64Reg_R12 ) ) // RSP/R12 can't be encoded without a SIB
{
if ( ( relOffset = = 0 ) & & ( regA ! = X64Reg_RBP ) & & ( regA ! = X64Reg_R13 ) ) // RBP/R13 can't be encoded with Mod0
{
modRM | = ( 0x0 < < 6 ) | ( EncodeRegNum ( regA ) ) ; // [regA]
mOut . Write ( modRM ) ;
return ;
}
else if ( ( relOffset > = - 0x80 ) & & ( relOffset < = 0x7F ) )
{
modRM | = ( 0x1 < < 6 ) | ( EncodeRegNum ( regA ) ) ; // [regA]+disp8
mOut . Write ( modRM ) ;
mOut . Write ( ( uint8 ) relOffset ) ;
return ;
}
else
{
modRM | = ( 0x2 < < 6 ) | ( EncodeRegNum ( regA ) ) ; // [regA]+disp32
mOut . Write ( modRM ) ;
mOut . Write ( ( int32 ) relOffset ) ;
return ;
}
}
else if ( regA = = X64Reg_None ) // The only option is disp32
{
modRM | = ( 0x0 < < 6 ) | ( 0x4 ) ; // [--][--]+disp32
mOut . Write ( modRM ) ;
uint8 sib = ( ( bScale = = 2 ) ? 1 : ( bScale = = 4 ) ? 2 : ( bScale = = 8 ) ? 3 : 0 ) < < 6 ;
sib | = ( EncodeRegNum ( regB ) < < 3 ) | 5 ;
mOut . Write ( sib ) ;
mOut . Write ( ( int32 ) relOffset ) ;
return ;
}
else
{
// Do no-offset version UNLESS we have a base of R13, which has its representation stolen by '[--][--]+disp32',
// so we must use the longer +disp8 version in that case
if ( ( relOffset = = 0 ) & & ( regA ! = X64Reg_R13 ) )
{
modRM | = ( 0x0 < < 6 ) | ( 0x4 ) ; // [--][--]
mOut . Write ( modRM ) ;
uint8 sib = ( ( bScale = = 2 ) ? 1 : ( bScale = = 4 ) ? 2 : ( bScale = = 8 ) ? 3 : 0 ) < < 6 ;
sib | = ( EncodeRegNum ( regB ) < < 3 ) | EncodeRegNum ( regA ) ;
mOut . Write ( sib ) ;
return ;
}
else if ( ( relOffset > = - 0x80 ) & & ( relOffset < = 0x7F ) )
{
modRM | = ( 0x1 < < 6 ) | ( 0x4 ) ; // [--][--]+disp8
mOut . Write ( modRM ) ;
uint8 sib = ( ( bScale = = 2 ) ? 1 : ( bScale = = 4 ) ? 2 : ( bScale = = 8 ) ? 3 : 0 ) < < 6 ;
sib | = ( EncodeRegNum ( regB ) < < 3 ) | EncodeRegNum ( regA ) ;
mOut . Write ( sib ) ;
mOut . Write ( ( uint8 ) relOffset ) ;
return ;
}
else
{
modRM | = ( 0x2 < < 6 ) | ( 0x4 ) ; // [--][--]+disp32
mOut . Write ( modRM ) ;
uint8 sib = ( ( bScale = = 2 ) ? 1 : ( bScale = = 4 ) ? 2 : ( bScale = = 8 ) ? 3 : 0 ) < < 6 ;
sib | = ( EncodeRegNum ( regB ) < < 3 ) | EncodeRegNum ( regA ) ;
mOut . Write ( sib ) ;
mOut . Write ( ( int32 ) relOffset ) ;
return ;
}
}
}
void BeMCContext : : EmitModRMRelStack ( int rx , int regOffset , int scale )
{
EmitModRMRel ( rx , mUseBP ? X64Reg_RBP : X64Reg_RSP , X64Reg_None , scale , regOffset ) ;
}
void BeMCContext : : EmitModRM ( int rx , BeMCOperand rm , int relocOfs )
{
uint8 modRM = ( rx < < 3 ) ;
//int relocIdx = -1;
if ( rm . IsImmediateFloat ( ) )
{
EmitModRM_XMM_IMM ( rx , rm ) ;
return ;
}
else if ( ( rm . mKind = = BeMCOperandKind_Symbol ) | | ( rm . mKind = = BeMCOperandKind_SymbolAddr ) )
{
BeMCRelocation reloc ;
auto sym = mCOFFObject - > mSymbols [ rm . mSymbolIdx ] ;
if ( sym - > mIsTLS )
{
auto vregInfo = mVRegInfo [ mTLSVRegIdx ] ;
modRM | = ( 2 < < 6 ) | EncodeRegNum ( vregInfo - > mReg ) ;
reloc . mKind = BeMCRelocationKind_SECREL ;
relocOfs = 0 ;
}
else
{
modRM | = 0x5 ; // RIP + <X>
reloc . mKind = BeMCRelocationKind_REL32 ;
}
Emit ( modRM ) ;
reloc . mOffset = mOut . GetPos ( ) ;
reloc . mSymTableIdx = rm . mSymbolIdx ;
mCOFFObject - > mTextSect . mRelocs . push_back ( reloc ) ;
mTextRelocs . push_back ( ( int ) mCOFFObject - > mTextSect . mRelocs . size ( ) - 1 ) ;
//relocIdx = (int)mOut.GetSize();
mOut . Write ( ( int32 ) relocOfs ) ;
return ;
}
else if ( rm . mKind = = BeMCOperandKind_NativeReg )
{
modRM | = ( 0x3 < < 6 ) | ( EncodeRegNum ( rm . mReg ) ) ;
}
else if ( rm . IsVRegAny ( ) )
{
auto vregInfo = mVRegInfo [ rm . mVRegIdx ] ;
if ( ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_SymbolAddr ) & &
( vregInfo - > mRelOffset . IsImmediateInt ( ) ) & &
( vregInfo - > mRelOffsetScale = = 1 ) )
{
return EmitModRM ( rx , vregInfo - > mRelTo , relocOfs + vregInfo - > mRelOffset . mImmediate ) ;
}
if ( ( rm . IsVReg ( ) ) & & ( vregInfo - > IsDirectRelToAny ( ) ) )
return EmitModRM ( rx , vregInfo - > mRelTo , relocOfs ) ;
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
auto resultType = GetRMParams ( rm , regA , regB , bScale , disp ) ;
//BF_ASSERT(resultType != BeMCRMMode_Invalid);
BF_ASSERT ( resultType = = BeMCRMMode_Deref ) ;
EmitModRMRel ( rx , regA , regB , bScale , disp ) ;
return ;
}
else
{
Fail ( " Invalid rm " ) ;
}
mOut . Write ( modRM ) ;
}
void BeMCContext : : EmitModRM ( BeMCOperand r , BeMCOperand rm , int relocOfs )
{
uint8 modRM = 0 ;
BF_ASSERT ( r . mKind = = BeMCOperandKind_NativeReg ) ;
EmitModRM ( EncodeRegNum ( r . mReg ) , rm , relocOfs ) ;
}
void BeMCContext : : EmitModRM_Addr ( BeMCOperand r , BeMCOperand rm )
{
uint8 modRM = 0 ;
BF_ASSERT ( r . mKind = = BeMCOperandKind_NativeReg ) ;
modRM = EncodeRegNum ( r . mReg ) < < 3 ;
if ( rm . mKind = = BeMCOperandKind_NativeReg )
{
modRM | = ( 0x0 < < 6 ) | ( EncodeRegNum ( rm . mReg ) ) ;
}
else if ( ( rm . mKind = = BeMCOperandKind_Symbol ) | | ( rm . mKind = = BeMCOperandKind_SymbolAddr ) )
{
EmitModRM ( r , rm ) ;
return ;
}
else if ( rm . mKind = = BeMCOperandKind_VReg )
{
auto vregInfo = mVRegInfo [ rm . mVRegIdx ] ;
if ( vregInfo - > mIsExpr )
{
if ( vregInfo - > mRelOffsetScale = = 1 )
{
X64CPURegister relToReg = X64Reg_None ;
int regOffset = 0 ;
if ( vregInfo - > mRelOffset )
{
BF_ASSERT ( vregInfo - > mRelOffset . IsImmediate ( ) ) ;
regOffset = ( int ) vregInfo - > mRelOffset . mImmediate ;
}
if ( vregInfo - > mRelTo . IsNativeReg ( ) )
{
relToReg = vregInfo - > mRelTo . mReg ;
}
else if ( vregInfo - > mRelTo . IsVRegAny ( ) )
{
auto relVRegInfo = GetVRegInfo ( vregInfo - > mRelTo ) ;
if ( relVRegInfo - > mRelTo )
{
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
auto resultType = GetRMParams ( rm , regA , regB , bScale , disp ) ;
BF_ASSERT ( resultType ! = BeMCRMMode_Invalid ) ;
EmitModRMRel ( EncodeRegNum ( r . mReg ) , regA , regB , bScale , disp ) ;
return ;
}
else
NotImpl ( ) ;
}
else
NotImpl ( ) ;
EmitModRMRel ( EncodeRegNum ( r . mReg ) , relToReg , X64Reg_None , 1 , regOffset ) ;
return ;
}
else
{
if ( vregInfo - > mRelOffset )
{
BF_ASSERT ( vregInfo - > mRelOffset . IsImmediate ( ) ) ;
int regOffset = vregInfo - > mRelOffset . mImmediate ;
int scaleVal = 0 ;
if ( vregInfo - > mRelOffsetScale = = 2 )
scaleVal = 0x1 ;
else if ( vregInfo - > mRelOffsetScale = = 4 )
scaleVal = 0x2 ;
else if ( vregInfo - > mRelOffsetScale = = 8 )
scaleVal = 0x3 ;
modRM | = ( 0x0 < < 6 ) | ( 0x4 ) ; // [--][--]
mOut . Write ( modRM ) ;
uint8 sib = ( scaleVal < < 6 ) | ( EncodeRegNum ( vregInfo - > mRelTo . mReg ) < < 3 ) | ( 0x5 ) ; // [<reg>*<scale> + imm32]
mOut . Write ( sib ) ;
mOut . Write ( ( int32 ) regOffset ) ;
return ;
}
}
}
else
{
Fail ( " Illegal " ) ;
}
}
else
{
Fail ( " Invalid rm " ) ;
}
mOut . Write ( modRM ) ;
}
void BeMCContext : : EmitModRM_XMM_IMM ( int rx , BeMCOperand & imm )
{
Emit ( ( rx < < 3 ) | ( 0x5 ) ) ; // RIP + <X>
BeMCSymbol * sym = NULL ;
if ( imm . mKind = = BeMCOperandKind_Immediate_f32 )
{
float floatVal = ( float ) imm . mImmFloat ;
String name = StrFormat ( " __real@%08x " , * ( int * ) & floatVal ) ;
sym = mCOFFObject - > GetCOMDAT ( name , & floatVal , 4 , 4 ) ;
}
else if ( imm . mKind = = BeMCOperandKind_Immediate_f64 )
{
double floatVal = imm . mImmFloat ;
String name = StrFormat ( " __real@%016llx " , * ( int64 * ) & floatVal ) ;
sym = mCOFFObject - > GetCOMDAT ( name , & floatVal , 8 , 8 ) ;
}
else if ( imm . mKind = = BeMCOperandKind_Immediate_f32_Packed128 )
{
float floatVal = ( float ) imm . mImmFloat ;
String name = StrFormat ( " __real@%08x_packed " , * ( int * ) & floatVal ) ;
float data [ 4 ] = { floatVal , 0 , 0 , 0 } ;
sym = mCOFFObject - > GetCOMDAT ( name , data , 16 , 16 ) ;
}
else if ( imm . mKind = = BeMCOperandKind_Immediate_f64_Packed128 )
{
double floatVal = imm . mImmFloat ;
String name = StrFormat ( " __real@%016llx_packed " , * ( int64 * ) & floatVal ) ;
double data [ 2 ] = { floatVal , 0 } ;
sym = mCOFFObject - > GetCOMDAT ( name , data , 16 , 16 ) ;
}
else
NotImpl ( ) ;
BeMCRelocation reloc ;
reloc . mKind = BeMCRelocationKind_REL32 ;
reloc . mOffset = mOut . GetPos ( ) ;
reloc . mSymTableIdx = sym - > mIdx ;
mCOFFObject - > mTextSect . mRelocs . push_back ( reloc ) ;
mTextRelocs . push_back ( ( int ) mCOFFObject - > mTextSect . mRelocs . size ( ) - 1 ) ;
mOut . Write ( ( int32 ) 0 ) ;
}
void BeMCContext : : VRegSetInitialized ( BeMCBlock * mcBlock , BeMCInst * inst , const BeMCOperand & operand , SizedArrayImpl < int > & addVec , SizedArrayImpl < int > & removeVec , bool deepSet , bool doSet )
{
if ( operand . IsSymbol ( ) )
{
auto sym = mCOFFObject - > mSymbols [ operand . mSymbolIdx ] ;
if ( sym - > mIsTLS )
{
VRegSetInitialized ( mcBlock , inst , BeMCOperand : : FromVReg ( mTLSVRegIdx ) , addVec , removeVec , deepSet , doSet ) ;
return ;
}
}
if ( operand . mKind = = BeMCOperandKind_CmpResult )
{
auto & cmpResult = mCmpResults [ operand . mCmpResultIdx ] ;
if ( cmpResult . mResultVRegIdx ! = - 1 )
VRegSetInitialized ( mcBlock , inst , BeMCOperand : : FromVReg ( cmpResult . mResultVRegIdx ) , addVec , removeVec , deepSet , doSet ) ;
return ;
}
if ( operand . mKind = = BeMCOperandKind_VRegPair )
{
VRegSetInitialized ( mcBlock , inst , BeMCOperand : : FromVReg ( operand . mVRegPair . mVRegIdx0 ) , addVec , removeVec , deepSet , doSet ) ;
VRegSetInitialized ( mcBlock , inst , BeMCOperand : : FromVReg ( operand . mVRegPair . mVRegIdx1 ) , addVec , removeVec , deepSet , doSet ) ;
}
if ( ! operand . IsVRegAny ( ) )
return ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
if ( vregInfo - > mDefOnFirstUse )
{
int insertIdx = FindSafeInstInsertPos ( * mInsertInstIdxRef ) ;
AllocInst ( BeMCInstKind_Def , operand , insertIdx ) ;
vregInfo - > mDefOnFirstUse = false ;
}
if ( vregInfo - > mRelTo )
VRegSetInitialized ( mcBlock , inst , vregInfo - > mRelTo , addVec , removeVec , deepSet , doSet & & deepSet ) ;
if ( vregInfo - > mRelOffset )
VRegSetInitialized ( mcBlock , inst , vregInfo - > mRelOffset , addVec , removeVec , deepSet , doSet & & deepSet ) ;
if ( doSet )
{
addVec . push_back ( operand . mVRegIdx ) ;
removeVec . push_back ( mVRegInitializedContext . GetIdx ( operand . mVRegIdx , BeTrackKind_Uninitialized ) ) ;
//vregsInitialized = mVRegInitializedContext.SetAndClear(vregsInitialized, operand.mVRegIdx, mVRegInitializedContext.GetIdx(operand.mVRegIdx, BeTrackKind_Uninitialized));
}
}
BeMCInst * BeMCContext : : FindSafePreBranchInst ( BeMCBlock * mcBlock )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
// We set the definition to just right before the first branch
if ( ( inst - > mKind = = BeMCInstKind_Br ) | | ( inst - > mKind = = BeMCInstKind_CondBr ) )
{
// Don't separate out Def/Cmp/CmpToBool/Test calls
while ( instIdx > 0 )
{
auto checkInst = mcBlock - > mInstructions [ instIdx - 1 ] ;
if ( ( checkInst - > mKind ! = BeMCInstKind_Def ) & &
( checkInst - > mKind ! = BeMCInstKind_DefLoad ) & &
( checkInst - > mKind ! = BeMCInstKind_Test ) & &
( checkInst - > mKind ! = BeMCInstKind_Cmp ) & &
( checkInst - > mKind ! = BeMCInstKind_CmpToBool ) )
return inst ;
inst = checkInst ;
instIdx - - ;
}
break ;
}
}
return mcBlock - > mInstructions [ 0 ] ;
}
void BeMCContext : : InitializedPassHelper ( BeMCBlock * mcBlock , BeVTrackingGenContext * genCtx , bool & modifiedBlockBefore , bool & modifiedBlockAfter )
{
modifiedBlockBefore = false ;
modifiedBlockAfter = false ;
genCtx - > mBlocks [ mcBlock - > mBlockIdx ] . mGenerateCount + + ;
genCtx - > mCalls + + ;
genCtx - > mHandledCalls + + ;
BeMDNode * curDbgScope = NULL ;
BeVTrackingList * vregsInitialized = mcBlock - > mPredVRegsInitialized ;
//OutputDebugStrF("InitializedPassHelper %@\n", vregsInitialized.mList);
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
genCtx - > mInstructions + + ;
mInsertInstIdxRef = & instIdx ;
auto inst = mcBlock - > mInstructions [ instIdx ] ;
inst - > mVRegsInitialized = vregsInitialized ;
bool deepSet = false ;
if ( inst - > mKind = = BeMCInstKind_LifetimeStart )
continue ;
SizedArray < int , 16 > removeVec ;
SizedArray < int , 16 > addVec ;
SizedArray < int , 16 > filteredRemoveVec ;
SizedArray < int , 16 > filteredAddVec ;
if ( ( inst - > mKind = = BeMCInstKind_ValueScopeSoftEnd ) | | ( inst - > mKind = = BeMCInstKind_ValueScopeHardEnd ) )
{
bool isSoft = inst - > mKind = = BeMCInstKind_ValueScopeSoftEnd ;
int startVRegIdx = ( int ) inst - > mArg0 . mImmediate ;
int endVRegIdx = ( int ) inst - > mArg1 . mImmediate ;
int listIdx = mVRegInitializedContext . FindIndex ( vregsInitialized , startVRegIdx ) ;
if ( listIdx < 0 )
listIdx = ~ listIdx ;
for ( ; listIdx < vregsInitialized - > mSize ; listIdx + + )
{
int vregIdx = vregsInitialized - > mEntries [ listIdx ] ;
if ( vregIdx > = endVRegIdx )
break ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( isSoft )
{
if ( vregInfo - > mValueScopeRetainedKind > = BeMCValueScopeRetainKind_Soft )
continue ;
}
else
{
if ( vregInfo - > mValueScopeRetainedKind > = BeMCValueScopeRetainKind_Hard )
continue ;
}
filteredRemoveVec . push_back ( vregIdx ) ;
}
}
if ( ( inst - > mResult ) & & ( inst - > mResult . mKind = = BeMCOperandKind_CmpResult ) )
{
auto & cmpResult = mCmpResults [ inst - > mResult . mCmpResultIdx ] ;
if ( cmpResult . mResultVRegIdx ! = - 1 )
{
auto cmpToBoolInst = AllocInst ( BeMCInstKind_CmpToBool , BeMCOperand : : FromCmpKind ( cmpResult . mCmpKind ) , BeMCOperand ( ) , instIdx + 1 ) ;
cmpToBoolInst - > mResult = BeMCOperand : : FromVReg ( cmpResult . mResultVRegIdx ) ;
}
inst - > mResult = BeMCOperand ( ) ;
}
if ( inst - > mKind = = BeMCInstKind_DbgDecl )
{
if ( ! mVRegInitializedContext . IsSet ( vregsInitialized , inst - > mArg0 . mVRegIdx ) )
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
if ( vregInfo - > mIsExpr ) // For shadowed inlined params, set as initialized
addVec . push_back ( inst - > mArg0 . mVRegIdx ) ;
else
{
if ( ( vregInfo - > mDbgVariable ! = NULL ) & & ( vregInfo - > mDbgVariable - > mInitType = = BfIRInitType_NotNeeded_AliveOnDecl ) )
addVec . push_back ( inst - > mArg0 . mVRegIdx ) ;
else
addVec . push_back ( mVRegInitializedContext . GetIdx ( inst - > mArg0 . mVRegIdx , BeTrackKind_Uninitialized ) ) ;
}
}
vregsInitialized = mVRegInitializedContext . Modify ( vregsInitialized , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
continue ;
}
if ( inst - > mKind = = BeMCInstKind_Def )
{
//deepSet = true;
// If we take a pointer to a local variable before initializing it, we need to consider it as being "ambiguously initialized"
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_VRegAddr )
{
auto relVRegInfo = GetVRegInfo ( vregInfo - > mRelTo ) ;
if ( ( relVRegInfo - > mDbgVariable ! = NULL ) & & ( ! mVRegInitializedContext . IsSet ( vregsInitialized , vregInfo - > mRelTo . mVRegIdx ) ) )
relVRegInfo - > mHasAmbiguousInitialization = true ;
}
}
if ( inst - > mKind = = BeMCInstKind_LifetimeEnd )
{
// In some cases we can have a dbg variable that actually points to a global variable (due to macros/inlining/etc), so this check is for that case:
if ( inst - > mArg0 . IsVRegAny ( ) )
{
removeVec . push_back ( inst - > mArg0 . mVRegIdx ) ;
removeVec . push_back ( mVRegInitializedContext . GetIdx ( inst - > mArg0 . mVRegIdx , BeTrackKind_Uninitialized ) ) ;
}
vregsInitialized = mVRegInitializedContext . Modify ( vregsInitialized , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
continue ;
}
if ( inst - > mKind = = BeMCInstKind_DefLoad )
{
// This is also an address-taking operation, OR it's just a usage of an explicitly-undefined value
auto loadRegInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( loadRegInfo - > mRelTo . IsVRegAny ( ) )
{
auto relVRegInfo = GetVRegInfo ( loadRegInfo - > mRelTo ) ;
if ( ( relVRegInfo - > mDbgVariable ! = NULL ) & & ( ! mVRegInitializedContext . IsSet ( vregsInitialized , loadRegInfo - > mRelTo . mVRegIdx ) ) )
relVRegInfo - > mHasAmbiguousInitialization = true ;
}
}
if ( inst - > IsMov ( ) )
{
// This is also an address-taking operation, OR it's just a usage of an explicitly-undefined value
if ( inst - > mArg1 . IsVRegAny ( ) )
{
auto srcRegInfo = GetVRegInfo ( inst - > mArg1 ) ;
if ( ( srcRegInfo - > mDbgVariable ! = NULL ) & & ( ! mVRegInitializedContext . IsSet ( vregsInitialized , inst - > mArg1 . mVRegIdx ) ) )
srcRegInfo - > mHasAmbiguousInitialization = true ;
}
}
int cpyDestVRegIdx = - 1 ;
if ( ( inst - > mKind = = BeMCInstKind_MemCpy ) | | ( inst - > mKind = = BeMCInstKind_MemSet ) )
{
cpyDestVRegIdx = inst - > mArg1 . mVRegPair . mVRegIdx0 ;
}
//deepSet = true;
auto destArg = inst - > GetDest ( ) ;
auto operands = { & inst - > mResult , & inst - > mArg0 , & inst - > mArg1 } ;
for ( auto op : operands )
{
VRegSetInitialized ( mcBlock , inst , * op , addVec , removeVec , deepSet | | ( op = = destArg ) ) ;
}
for ( int removeIdx = 0 ; removeIdx < ( int ) filteredRemoveVec . size ( ) ; removeIdx + + )
{
int vregIdx = filteredRemoveVec [ removeIdx ] ;
if ( vregIdx > = mVRegInitializedContext . mNumItems )
continue ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mChainLifetimeEnd )
{
auto mcCheck = vregInfo - > mRelTo ;
while ( mcCheck . IsVRegAny ( ) )
{
auto checkInfo = mVRegInfo [ mcCheck . mVRegIdx ] ;
removeVec . push_back ( mcCheck . mVRegIdx ) ;
mcCheck = checkInfo - > mRelTo ;
}
}
}
vregsInitialized = mVRegInitializedContext . Modify ( vregsInitialized , addVec , removeVec , filteredAddVec , filteredRemoveVec ) ;
}
mInsertInstIdxRef = NULL ;
mActiveBlock = NULL ;
for ( int succIdx = 0 ; succIdx < ( int ) mcBlock - > mSuccs . size ( ) ; succIdx + + )
{
BeMCBlock * succ = mcBlock - > mSuccs [ succIdx ] ;
auto & entry = genCtx - > mBlocks [ succ - > mBlockIdx ] ;
bool forceRun = ( entry . mGenerateCount = = 0 ) & & ( ! entry . mGenerateQueued ) ;
//if ((!mVRegInitializedContext.HasExtraBitsSet(succ->mPredVRegsInitialized, vregsInitialized)) && (!forceRun))
//continue;
auto newPredVRegsInitialized = mVRegInitializedContext . Merge ( succ - > mPredVRegsInitialized , vregsInitialized ) ;
if ( ( newPredVRegsInitialized = = succ - > mPredVRegsInitialized ) & & ( ! forceRun ) )
continue ;
int vregIdx = - 1 ;
/*SizedArray<int, 16> newNodes;
auto & prevDestEntry = succ - > mPredVRegsInitialized ;
auto & mergeFrom = vregsInitialized . mBits ;
// Take nodes that were exclusive to the new set and add edges to nodes that were exclusive the old set
while ( true )
{
vregIdx = mVRegInitializedContext . GetNextDiffSetIdx ( prevDestEntry . mBits , mergeFrom , vregIdx ) ;
if ( vregIdx = = - 1 )
break ;
newNodes . push_back ( vregIdx ) ;
}
BF_ASSERT ( ( ! newNodes . empty ( ) ) | | ( forceRun ) ) ; */
/*if ((mDebugging) || (mBeFunction->mName == "?DrawEntry@DrawContext@PerfView@BeefPerf@@QEAAXPEAVGraphics@gfx@Beefy@@PEAVTrackNodeEntry@23@MM@Z"))
{
String str ;
str + = StrFormat ( " %s -> %s (Gen=%d) " , ToString ( BeMCOperand : : FromBlock ( mcBlock ) ) . c_str ( ) , ToString ( BeMCOperand : : FromBlock ( succ ) ) . c_str ( ) , genCtx - > mBlocks [ succ - > mBlockIdx ] . mGenerateCount ) ;
for ( auto newNode : newNodes )
str + = StrFormat ( " %d " , newNode ) ;
str + = " \r \n " ;
OutputDebugStringA ( str . c_str ( ) ) ;
} */
succ - > mPredVRegsInitialized = newPredVRegsInitialized ;
//TODO: This was a bad comparison
// if (succ->mBlockIdx > succ->mBlockIdx)
// modifiedBlockAfter = true;
// else
// modifiedBlockBefore = true;
// What does this do?
if ( succ - > mBlockIdx > mcBlock - > mBlockIdx )
modifiedBlockAfter = true ;
else
modifiedBlockBefore = true ;
entry . mGenerateQueued = true ;
}
}
void BeMCContext : : FixVRegInitFlags ( BeMCInst * inst , const BeMCOperand & operand )
{
if ( operand . IsVReg ( ) )
{
SizedArray < int , 1 > addVec = { operand . mVRegIdx } ;
inst - > mVRegsInitialized = mVRegInitializedContext . Add ( inst - > mVRegsInitialized , addVec , true ) ;
}
}
void BeMCContext : : SetVTrackingValue ( BeMCOperand & operand , BeVTrackingValue & vTrackingValue )
{
if ( ! operand . IsVRegAny ( ) )
return ;
vTrackingValue . mEntry - > Set ( operand . mVRegIdx ) ;
auto vregInfo = mVRegInfo [ operand . mVRegIdx ] ;
SetVTrackingValue ( vregInfo - > mRelTo , vTrackingValue ) ;
SetVTrackingValue ( vregInfo - > mRelOffset , vTrackingValue ) ;
}
void BeMCContext : : DoInitInjectionPass ( )
{
BP_ZONE ( " BeMCContext::DoInitInjectionPass " ) ;
for ( auto mcBlock : mBlocks )
{
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
BfIRInitType pendingInitKind = BfIRInitType_NotNeeded ;
if ( inst - > mKind = = BeMCInstKind_DbgDecl )
{
int vregIdx = inst - > mArg0 . mVRegIdx ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
auto dbgVar = vregInfo - > mDbgVariable ;
if ( ! mVRegInitializedContext . IsSet ( inst - > mVRegsInitialized , vregIdx ) )
{
if ( ( dbgVar - > mPendingInitType ! = BfIRInitType_NotNeeded ) & & ( dbgVar - > mPendingInitType ! = BfIRInitType_NotNeeded_AliveOnDecl ) )
{
pendingInitKind = dbgVar - > mPendingInitType ;
// We don't need dynLife anymore since we're explicitly writing this
//vregInfo->mHasDynLife = false;
//AllocInst(BeMCInstKind_Def, inst->mArg0, instIdx);
}
}
}
if ( pendingInitKind ! = BfIRInitType_NotNeeded )
{
int vregIdx = inst - > mArg0 . mVRegIdx ;
auto dbgVar = mVRegInfo [ vregIdx ] - > mDbgVariable ;
auto varType = mVRegInfo [ vregIdx ] - > mType ;
auto initType = dbgVar - > mPendingInitType ;
if ( varType - > IsFloat ( ) )
{
BeMCOperand zeroVal ;
if ( varType - > mTypeCode = = BeTypeCode_Float )
zeroVal . mKind = BeMCOperandKind_Immediate_f32 ;
else
zeroVal . mKind = BeMCOperandKind_Immediate_f64 ;
if ( initType = = BfIRInitType_Uninitialized )
zeroVal . mImmFloat = NAN ;
else
zeroVal . mImmFloat = 0 ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromVReg ( vregIdx ) , zeroVal , instIdx + 1 ) ;
}
else if ( varType - > IsComposite ( ) )
{
SetAndRestoreValue < int * > insertPtr ( mInsertInstIdxRef , & instIdx ) ;
instIdx + + ;
uint8 val = ( initType = = BfIRInitType_Uninitialized ) ? 0xCC : 0 ;
CreateMemSet ( BeMCOperand : : FromVRegAddr ( vregIdx ) , val , varType - > mSize , varType - > mAlign ) ;
instIdx - - ;
}
else
{
int64 val = ( initType = = BfIRInitType_Uninitialized ) ? ( int64 ) 0xCCCCCCCCCCCCCCCCLL : 0 ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromVReg ( vregIdx ) , BeMCOperand : : FromImmediate ( val ) , instIdx + 1 ) ;
}
dbgVar - > mPendingInitType = BfIRInitType_NotNeeded ;
}
}
mActiveBlock = NULL ;
}
SetCurrentInst ( NULL ) ;
}
void BeMCContext : : ReplaceVRegsInit ( BeMCBlock * mcBlock , int startInstIdx , BeVTrackingList * prevList , BeVTrackingList * newList )
{
for ( int instIdx = startInstIdx ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( inst - > mVRegsInitialized ! = prevList )
return ;
inst - > mVRegsInitialized = newList ;
}
}
void BeMCContext : : GenerateVRegInitFlags ( BeVTrackingGenContext & genCtx )
{
BP_ZONE ( " BeMCContext::GenerateVRegInitFlags " ) ;
mVRegInitializedContext . Clear ( ) ;
mVRegInitializedContext . Init ( ( int ) mVRegInfo . size ( ) ) ;
auto emptyList = mVRegInitializedContext . AllocEmptyList ( ) ;
genCtx . mEmptyList = emptyList ;
genCtx . mBlocks . Resize ( mBlocks . size ( ) ) ;
for ( int blockIdx = 0 ; blockIdx < ( int ) mBlocks . size ( ) ; blockIdx + + )
{
auto mcBlock = mBlocks [ blockIdx ] ;
mcBlock - > mPredVRegsInitialized = emptyList ;
}
bool modifiedBlockBefore ;
bool modifiedBlockAfter ;
InitializedPassHelper ( mBlocks [ 0 ] , & genCtx , modifiedBlockBefore , modifiedBlockAfter ) ;
while ( true )
{
bool didWork = false ;
// Handle any outstanding pred entries
for ( int blockIdx = 0 ; blockIdx < ( int ) mBlocks . size ( ) ; blockIdx + + )
{
auto & entry = genCtx . mBlocks [ blockIdx ] ;
if ( entry . mGenerateQueued )
{
entry . mGenerateQueued = false ;
didWork = true ;
auto block = mBlocks [ blockIdx ] ;
bool modifiedBlockBefore ;
bool modifiedBlockAfter ;
InitializedPassHelper ( block , & genCtx , modifiedBlockBefore , modifiedBlockAfter ) ;
if ( modifiedBlockBefore )
break ;
}
}
if ( ! didWork )
break ;
}
// Fix up the vregsInit changes to represent changes from the last intruction in one block to the first instruction on the next blockfs
for ( int blockIdx = 0 ; blockIdx < ( int ) mBlocks . size ( ) - 1 ; blockIdx + + )
{
auto fromBlock = mBlocks [ blockIdx ] ;
auto toBlock = mBlocks [ blockIdx + 1 ] ;
if ( ( fromBlock - > mInstructions . empty ( ) ) | | ( toBlock - > mInstructions . empty ( ) ) )
continue ;
BeMCInst * fromInst = NULL ;
// Last inst may not have vregsInit set
for ( int instIdx = ( int ) fromBlock - > mInstructions . size ( ) - 1 ; instIdx > = 0 ; instIdx - - )
{
fromInst = fromBlock - > mInstructions [ instIdx ] ;
if ( fromInst - > mVRegsInitialized ! = NULL )
break ;
}
auto toInst = toBlock - > mInstructions . front ( ) ;
if ( ( fromInst = = NULL ) | | ( fromInst - > mVRegsInitialized = = NULL ) | |
( toInst = = NULL ) | | ( toInst - > mVRegsInitialized = = NULL ) )
continue ;
auto prevVRegsInit = toInst - > mVRegsInitialized ;
auto newVRegsInit = mVRegInitializedContext . SetChanges ( toInst - > mVRegsInitialized , fromInst - > mVRegsInitialized ) ; ;
for ( int instIdx = 0 ; instIdx < ( int ) toBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = toBlock - > mInstructions [ instIdx ] ;
if ( inst - > mVRegsInitialized ! = prevVRegsInit )
break ;
inst - > mVRegsInitialized = newVRegsInit ;
}
}
int instCount = 0 ;
for ( auto block : mBlocks )
instCount + = ( int ) block - > mInstructions . size ( ) ;
BpEvent ( " GenerateVRegInitFlags Results " ,
StrFormat ( " Blocks: %d \n Instructions: %d \n VRegs: %d \n Calls: %d \n Handled Calls: %d \n Processed Instructions: %d \n VRegInit Bytes: %d \n Temp Bytes: %d " ,
( int ) mBlocks . size ( ) , instCount , ( int ) mVRegInfo . size ( ) , genCtx . mCalls , genCtx . mHandledCalls , genCtx . mInstructions , mVRegInitializedContext . mAlloc . GetAllocSize ( ) , genCtx . mAlloc . GetAllocSize ( ) ) . c_str ( ) ) ;
}
bool BeMCContext : : DoInitializedPass ( )
{
BP_ZONE ( " BeMCContext::DoInitializedPass " ) ;
BeVTrackingGenContext genCtx ;
GenerateVRegInitFlags ( genCtx ) ;
// Search blocks for instances of ambiguous initialization
for ( int blockIdx = 0 ; blockIdx < ( int ) mBlocks . size ( ) ; blockIdx + + )
{
auto mcBlock = mBlocks [ blockIdx ] ;
// If mPredVRegsInitialized is NULL, this blocks is unreachable. It could still be in some other block's preds so we don't
// completely erase it, just empty it
if ( genCtx . mBlocks [ mcBlock - > mBlockIdx ] . mGenerateCount = = 0 )
{
//BF_ASSERT((blockIdx != 0) && (mcBlock->mPreds.size() == 0));
mcBlock - > mInstructions . Clear ( ) ;
continue ;
}
/*int vregIdx = -1;
while ( true )
{
vregIdx = mVRegInitializedContext . GetNextSetIdx ( mcBlock - > mPredVRegsInitialized , vregIdx ) ;
if ( vregIdx = = - 1 )
break ; */
for ( int vregIdx : * mcBlock - > mPredVRegsInitialized )
{
if ( vregIdx > = mVRegInitializedContext . mNumItems )
continue ;
if ( mVRegInitializedContext . IsSet ( mcBlock - > mPredVRegsInitialized , vregIdx , BeTrackKind_Uninitialized ) )
{
// If a register has both the "initialized" and "uninitialized" flags set, that means that there will be debugging range
// where the variable could show an uninitialized value. We zero out the value at the def position to make
// variable viewing less confusing. This state does not affect actual program execution because we've already statically
// checked against usage of unassigned variables -- unless overridden with a "?" uninitialized expression, in which case
// one could argue that 0xCC would be a more useful value, but we go with the argument for the "less confusing" zero in this case.
auto vregInfo = mVRegInfo [ vregIdx ] ;
vregInfo - > mHasAmbiguousInitialization = true ;
}
}
//
}
bool needsReRun = false ;
bool hasInits = false ;
for ( int vregIdx = 0 ; vregIdx < ( int ) mVRegInfo . size ( ) ; vregIdx + + )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mDbgVariable ! = NULL )
{
auto initType = vregInfo - > mDbgVariable - > mInitType ;
if ( initType = = BfIRInitType_NotSet )
{
if ( vregInfo - > mHasAmbiguousInitialization )
initType = BfIRInitType_Zero ;
else
initType = BfIRInitType_NotNeeded ;
}
else if ( ( initType = = BfIRInitType_Uninitialized ) & & ( ! vregInfo - > mHasAmbiguousInitialization ) )
{
// If we have an explicitly-uninitialized variable but it doesn't have ambiguous initialization
// then we don't need to do the 0xCC, we can control visibility in the debugger through scope
initType = BfIRInitType_NotNeeded ;
}
if ( vregInfo - > mDbgVariable - > mPendingInitType = = initType )
continue ;
if ( ( initType ! = BfIRInitType_NotNeeded ) | |
( vregInfo - > mDbgVariable - > mPendingInitType = = BfIRInitType_NotSet ) )
{
vregInfo - > mDbgVariable - > mPendingInitType = initType ;
}
if ( ( initType ! = BfIRInitType_NotNeeded ) & & ( initType ! = BfIRInitType_NotNeeded_AliveOnDecl ) )
{
vregInfo - > mDbgVariable - > mPendingInitDef = true ;
BF_ASSERT ( vregInfo - > mHasDynLife ) ;
vregInfo - > mDoConservativeLife = true ;
hasInits = true ;
}
}
}
if ( hasInits )
{
DoInitInjectionPass ( ) ;
// We need to re-execute the init flag generation
BeVTrackingGenContext genCtx ;
GenerateVRegInitFlags ( genCtx ) ;
}
return ! needsReRun ;
}
void BeMCContext : : DoTLSSetup ( )
{
// Perform at the end of the 'entry' block
if ( mTLSVRegIdx = = - 1 )
return ;
auto mcBlock = mBlocks [ 0 ] ;
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
// Skip past param setup
if ( inst - > mKind = = BeMCInstKind_Def )
continue ;
if ( ( inst - > mKind = = BeMCInstKind_Mov ) & & ( inst - > mArg1 . IsNativeReg ( ) ) )
continue ;
auto int32Type = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int32 ) ;
auto intType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
auto intPtrType = mModule - > mContext - > GetPointerTo ( intType ) ;
auto mcTlsIndex = AllocVirtualReg ( intType ) ;
CreateDefineVReg ( mcTlsIndex , instIdx + + ) ;
auto sym = mCOFFObject - > GetSymbolRef ( " _tls_index " ) ;
if ( sym - > mType = = NULL )
sym - > mType = int32Type ;
AllocInst ( BeMCInstKind_Mov , mcTlsIndex , BeMCOperand : : FromSymbol ( sym - > mIdx ) , instIdx + + ) ;
AllocInst ( BeMCInstKind_Def , BeMCOperand : : FromVReg ( mTLSVRegIdx ) , instIdx + + ) ;
AllocInst ( BeMCInstKind_TLSSetup , BeMCOperand : : FromVReg ( mTLSVRegIdx ) , BeMCOperand : : FromImmediate ( 0 ) , instIdx + + ) ;
auto mcTlsVal = AllocVirtualReg ( intPtrType ) ;
CreateDefineVReg ( mcTlsVal , instIdx + + ) ;
auto tlsValInfo = GetVRegInfo ( mcTlsVal ) ;
tlsValInfo - > mIsExpr = true ;
tlsValInfo - > mRelTo = BeMCOperand : : FromVReg ( mTLSVRegIdx ) ;
tlsValInfo - > mRelOffset = mcTlsIndex ;
tlsValInfo - > mRelOffsetScale = 8 ;
auto mcLoadVal = mcTlsVal ;
mcLoadVal . mKind = BeMCOperandKind_VRegLoad ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromVReg ( mTLSVRegIdx ) , mcLoadVal , instIdx + + ) ;
break ;
}
mActiveBlock = NULL ;
}
void BeMCContext : : DoChainedBlockMerge ( )
{
for ( int blockIdx = 0 ; blockIdx < mBlocks . size ( ) - 1 ; blockIdx + + )
{
auto mcBlock = mBlocks [ blockIdx ] ;
auto nextMcBlock = mBlocks [ blockIdx + 1 ] ;
// We only branch into one block, and the the next block only has current block as a predecessor?
if ( ( mcBlock - > mSuccs . size ( ) = = 1 ) & & ( nextMcBlock - > mPreds . size ( ) = = 1 ) & & ( nextMcBlock - > mPreds [ 0 ] = = mcBlock ) )
{
auto lastInst = mcBlock - > mInstructions . back ( ) ;
// Last instruction of current block is an unconditional branch to the next block
if ( ( lastInst - > mKind = = BeMCInstKind_Br ) & & ( lastInst - > mArg0 . mBlock = = nextMcBlock ) )
{
//auto itr = std::find(mcBlock->mSuccs.begin(), mcBlock->mSuccs.end(), nextMcBlock);
//mcBlock->mSuccs.erase(itr);
mcBlock - > mSuccs . Remove ( nextMcBlock ) ;
for ( auto succ : nextMcBlock - > mSuccs )
{
//auto itr = std::find(mcBlock->mSuccs.begin(), mcBlock->mSuccs.end(), succ);
//if (itr == mcBlock->mSuccs.end())
if ( ! mcBlock - > mSuccs . Contains ( succ ) )
{
mcBlock - > mSuccs . push_back ( succ ) ;
succ - > mPreds . push_back ( mcBlock ) ;
}
/*itr = std::find(succ->mPreds.begin(), succ->mPreds.end(), nextMcBlock);
succ - > mPreds . erase ( itr ) ; */
succ - > mPreds . Remove ( nextMcBlock ) ;
}
mcBlock - > mMaxDeclBlockId = nextMcBlock - > mMaxDeclBlockId ;
mcBlock - > mInstructions . pop_back ( ) ;
for ( auto inst : nextMcBlock - > mInstructions )
mcBlock - > mInstructions . push_back ( inst ) ;
mBlocks . RemoveAt ( blockIdx + 1 ) ;
blockIdx - - ;
}
}
}
for ( int blockIdx = 0 ; blockIdx < mBlocks . size ( ) ; blockIdx + + )
mBlocks [ blockIdx ] - > mBlockIdx = blockIdx ;
}
void BeMCContext : : DetectLoops ( )
{
2019-10-18 09:13:37 -07:00
BP_ZONE ( " BeMCContext::DetectLoops " ) ;
2019-08-23 11:56:54 -07:00
BeMCLoopDetector loopDetector ( this ) ;
loopDetector . DetectLoops ( ) ;
}
void BeMCContext : : RefreshRefCounts ( )
{
for ( auto vregInfo : mVRegInfo )
{
if ( vregInfo - > mMustExist )
vregInfo - > mRefCount = 1 ;
else
vregInfo - > mRefCount = 0 ;
vregInfo - > mAssignCount = 0 ;
}
for ( auto mcBlock : mBlocks )
{
for ( auto & inst : mcBlock - > mInstructions )
{
if ( inst - > IsDef ( ) )
continue ;
auto operands = { inst - > mResult , inst - > mArg0 , inst - > mArg1 } ;
for ( auto op : operands )
{
if ( op . IsVRegAny ( ) )
mVRegInfo [ op . mVRegIdx ] - > mRefCount + + ;
if ( op . mKind = = BeMCOperandKind_VRegPair )
{
auto vreg0 = BeMCOperand : : FromEncoded ( op . mVRegPair . mVRegIdx0 ) ;
if ( vreg0 . IsVRegAny ( ) )
{
mVRegInfo [ vreg0 . mVRegIdx ] - > mRefCount + + ;
mVRegInfo [ vreg0 . mVRegIdx ] - > mAssignCount + + ;
}
auto vreg1 = BeMCOperand : : FromEncoded ( op . mVRegPair . mVRegIdx1 ) ;
if ( vreg1 . IsVRegAny ( ) )
mVRegInfo [ vreg1 . mVRegIdx ] - > mRefCount + + ;
}
}
if ( inst - > IsAssignment ( ) )
{
if ( inst - > mArg0 . IsVRegAny ( ) )
mVRegInfo [ inst - > mArg0 . mVRegIdx ] - > mAssignCount + + ;
}
if ( inst - > mResult )
{
if ( inst - > mResult . IsVRegAny ( ) )
mVRegInfo [ inst - > mResult . mVRegIdx ] - > mAssignCount + + ;
}
}
}
for ( int vregIdx = 0 ; vregIdx < ( int ) mVRegInfo . size ( ) ; vregIdx + + )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mRefCount > 0 )
AddRelRefs ( BeMCOperand : : FromVReg ( vregIdx ) , 0 ) ;
}
}
bool BeMCContext : : CheckVRegEqualityRange ( BeMCBlock * mcBlock , int instIdx , const BeMCOperand & vreg0 , const BeMCOperand & vreg1 , BeMCRemapper & regRemaps , bool onlyCheckFirstLifetime )
{
// The algorithm here is that we check to see if either of these registers are ever written to
// up to the point that one of them dies. If not, we can map them to each other
bool foundSplitPoint = false ;
bool hadPointerDeref = false ;
for ( int checkInstIdx = instIdx + 1 ; checkInstIdx < ( int ) mcBlock - > mInstructions . size ( ) ; checkInstIdx + + )
{
auto checkInst = mcBlock - > mInstructions [ checkInstIdx ] ;
if ( ( ! IsLive ( checkInst - > mLiveness , vreg0 . mVRegIdx , regRemaps ) ) | |
( ( ! onlyCheckFirstLifetime ) & & ( ! IsLive ( checkInst - > mLiveness , vreg1 . mVRegIdx , regRemaps ) ) ) )
{
return true ;
}
if ( ( checkInst - > mKind = = BeMCInstKind_LifetimeEnd ) & &
( ( checkInst - > mArg0 . mVRegIdx = = vreg0 . mVRegIdx ) | | ( checkInst - > mArg0 . mVRegIdx = = vreg1 . mVRegIdx ) ) )
{
if ( checkInstIdx = = ( int ) mcBlock - > mInstructions . size ( ) - 1 )
{
// There are cases where this might be the last instruction in a block, so we won't see the mLiveness change
// so this catches that case
return true ;
}
// We have cases like when have a "defer delete val;", val will get used by the dtor after the LifetimeEnd
return false ;
}
if ( hadPointerDeref )
{
// Pointer deref is okay as long as liveness ends one instruction afterwards
return false ;
}
// Anything can happen during a call...
if ( checkInst - > mKind = = BeMCInstKind_Call )
return false ;
auto mcDest = checkInst - > GetDest ( ) ;
if ( ( mcDest ! = NULL ) & & ( mcDest - > mKind = = BeMCOperandKind_VReg ) )
{
// Any assignment to either of these regs disqualifies them
if ( ( mcDest - > mVRegIdx = = vreg0 . mVRegIdx ) | | ( mcDest - > mVRegIdx = = vreg1 . mVRegIdx ) )
return false ;
// Any writing into memory could be aliased, so we don't replace across that boundary
if ( HasPointerDeref ( * mcDest ) )
{
// Delay the 'return false' until we determine if our liveness ends on the next instruction.
// If it does, then this is okay because it didn't affect the source of any operations
hadPointerDeref = true ;
}
}
}
return false ;
}
void BeMCContext : : DoInstCombinePass ( )
{
BP_ZONE ( " BeMCContext::DoInstCombinePass " ) ;
BeMCRemapper regRemaps ;
regRemaps . Init ( ( int ) mVRegInfo . size ( ) ) ;
BeVTrackingValue vTrackingVal ( ( int ) mVRegInfo . size ( ) ) ;
HashSet < int > wantUnwrapVRegs ;
if ( mDebugging )
{
NOP ;
}
for ( auto mcBlock : mBlocks )
{
BeDbgLoc * curDbgLoc = NULL ;
int safeMergeLocStart = 0 ;
bool lastDefDidMerge = false ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
bool reprocessInst = false ;
auto inst = mcBlock - > mInstructions [ instIdx ] ;
BeMCInst * prevInst = NULL ;
if ( instIdx > 0 )
prevInst = mcBlock - > mInstructions [ instIdx - 1 ] ;
if ( ( inst - > mDbgLoc ! = curDbgLoc ) & & ( inst - > mDbgLoc ! = NULL ) )
{
safeMergeLocStart = instIdx ;
curDbgLoc = inst - > mDbgLoc ;
}
if ( ( inst - > mKind = = BeMCInstKind_Call ) | | ( inst - > mKind = = BeMCInstKind_MemCpy ) | | ( inst - > mKind = = BeMCInstKind_MemSet ) )
{
// Don't merge anything across callsites or memory writes
safeMergeLocStart = instIdx ;
}
auto instDest = inst - > GetDest ( ) ;
if ( ( instDest ! = NULL ) & & ( HasPointerDeref ( * instDest ) ) )
{
// Memory aliasing may invalidate any accesses, don't merge expressions across here
safeMergeLocStart = instIdx + 1 ;
}
SetCurrentInst ( inst ) ;
//TODO: Remove
// if we've remapped other vregs onto this that aren't dead, then we can't end the value lifetime but we
// must end the lifetime of the visible debug variable
/*if (inst->mKind == BeMCInstKind_LifetimeEnd)
{
bool extendValue = false ;
int vregIdx = inst - > mArg0 . mVRegIdx ;
int checkIdx = regRemaps . GetHead ( vregIdx ) ;
while ( checkIdx ! = - 1 )
{
if ( ( checkIdx ! = vregIdx ) & & ( mLivenessContext . IsSet ( inst - > mLiveness , checkIdx ) ) )
extendValue = true ;
checkIdx = regRemaps . GetNext ( checkIdx ) ;
}
if ( extendValue )
{
//Throw error
}
//inst->mKind = BeMCInstKind_LifetimeDbgEnd;
} */
// Handle movs into .addrs
if ( inst - > mKind = = BeMCInstKind_Mov )
{
auto vregInfoDest = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfoDest ! = NULL )
{
auto vregInfoSrc = GetVRegInfo ( inst - > mArg1 ) ;
if ( ( vregInfoSrc ! = NULL ) & & ( vregInfoSrc - > mForceMerge ) )
{
vregInfoSrc - > mForceMerge = false ;
if ( ( vregInfoDest - > mDbgVariable ! = NULL ) & & ( vregInfoSrc - > CanEliminate ( ) ) )
{
BF_ASSERT ( ! mVRegInitializedContext . IsSet ( inst - > mVRegsInitialized , inst - > mArg0 . mVRegIdx ) ) ;
AddRegRemap ( inst - > mArg1 . mVRegIdx , inst - > mArg0 . mVRegIdx , regRemaps ) ;
// Remove any LifetimeStarts since our source may have been alive before us
//if (inst->mArg1.mVRegIdx < inst->mArg0.mVRegIdx)
//removeLifetimeStarts.insert(inst->mArg0.mVRegIdx);
}
}
}
}
// The following code only applies when we aren't on the last instruction
int nextIdx = instIdx + 1 ;
BeMCInst * nextInst = NULL ;
while ( true )
{
if ( nextIdx > = ( int ) mcBlock - > mInstructions . size ( ) )
break ;
nextInst = mcBlock - > mInstructions [ nextIdx ] ;
if ( ! nextInst - > IsDef ( ) )
break ;
nextIdx + + ;
}
if ( nextInst = = NULL )
continue ;
// The following code only applies when we have at least 2 more instructions
// Find a non-Def instruction
int nextNextIdx = nextIdx + 1 ;
BeMCInst * nextNextInst = NULL ;
while ( true )
{
if ( nextNextIdx > = ( int ) mcBlock - > mInstructions . size ( ) )
break ;
nextNextInst = mcBlock - > mInstructions [ nextNextIdx ] ;
if ( ! nextNextInst - > IsDef ( ) )
break ;
nextNextIdx + + ;
}
auto result = RemapOperand ( inst - > mResult , regRemaps ) ;
auto arg0 = RemapOperand ( inst - > mArg0 , regRemaps ) ;
auto arg1 = RemapOperand ( inst - > mArg1 , regRemaps ) ;
if ( inst - > IsDef ( ) )
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
if ( vregInfo - > mIsExpr )
{
for ( int checkInstIdx = safeMergeLocStart ; checkInstIdx < instIdx ; checkInstIdx + + )
{
auto checkInst = mcBlock - > mInstructions [ checkInstIdx ] ;
if ( checkInst - > IsDef ( ) )
{
auto checkArg0 = RemapOperand ( checkInst - > mArg0 , regRemaps ) ;
auto checkVRegInfo = mVRegInfo [ checkArg0 . mVRegIdx ] ;
if ( checkVRegInfo - > mIsExpr )
{
// Remap to use the earlier definition of this expression
if ( AreOperandsEquivalent ( inst - > mArg0 , checkArg0 , regRemaps ) )
{
vTrackingVal . Clear ( ) ;
SetVTrackingValue ( checkArg0 , vTrackingVal ) ;
// Check to make sure we didn't do any writes to any of the values used in the expression. This happens
// during "iVal++", for example.
bool hadBadWrite = false ;
for ( int verifyInstIdx = safeMergeLocStart ; verifyInstIdx < instIdx ; verifyInstIdx + + )
{
auto verifyInst = mcBlock - > mInstructions [ verifyInstIdx ] ;
auto verifyDest = verifyInst - > GetDest ( ) ;
if ( ( verifyDest ! = NULL ) & & ( verifyDest - > IsVRegAny ( ) ) & & ( vTrackingVal . mEntry - > IsSet ( verifyDest - > mVRegIdx ) ) )
{
hadBadWrite = true ;
break ;
}
}
if ( ! hadBadWrite )
{
auto vregInfoFrom = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfoFrom - > mDbgVariable = = NULL )
AddRegRemap ( inst - > mArg0 . mVRegIdx , checkArg0 . mVRegIdx , regRemaps , true ) ;
}
break ;
}
}
}
}
}
}
BeMCOperand origNextDestOperand ;
if ( nextInst - > IsAssignment ( ) )
origNextDestOperand = nextInst - > mArg0 ;
else if ( nextInst - > mResult )
origNextDestOperand = nextInst - > mResult ;
BeMCOperand nextDestOperand = RemapOperand ( origNextDestOperand , regRemaps ) ;
if ( inst - > mKind = = BeMCInstKind_DefLoad )
{
auto vregInfoDest = GetVRegInfo ( inst - > mArg0 ) ;
auto mcLoaded = inst - > mArg0 ;
auto mcAddr = vregInfoDest - > mRelTo ;
// We can only do a regRemap replace if we aren't the product of a pointer dereference
bool mapToDef = false ;
if ( mcAddr . mKind = = BeMCOperandKind_VReg )
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
// We do 'onlyCheckFirstLifetime' because we have cases like when have a "defer delete val;", because val will get used by the dtor after the LiftimeEnd
if ( ( vregInfo - > IsDirectRelTo ( ) ) & & ( vregInfo - > mDbgVariable = = NULL ) & & ( CheckVRegEqualityRange ( mcBlock , instIdx , mcLoaded , mcAddr , regRemaps , true ) ) )
{
// If the source value doesn't change in the lifetime of the loaded value then
// we can just map directly to the source - no copy needed.
inst - > mKind = BeMCInstKind_Def ;
AddRegRemap ( inst - > mArg0 . mVRegIdx , vregInfoDest - > mRelTo . mVRegIdx , regRemaps , true ) ;
continue ;
}
}
else if ( ( mcAddr . mKind = = BeMCOperandKind_Symbol ) | | ( mcAddr . mKind = = BeMCOperandKind_SymbolAddr ) )
{
// No actual load needed - just keep it as a Direct reference
inst - > mKind = BeMCInstKind_Def ;
//wantUnwrapVRegs.Add(inst->mArg0.mVRegIdx);
continue ;
}
bool hadPointerDeref = false ;
for ( int checkInstIdx = instIdx + 1 ; checkInstIdx < ( int ) mcBlock - > mInstructions . size ( ) ; checkInstIdx + + )
{
auto checkInst = mcBlock - > mInstructions [ checkInstIdx ] ;
BF_ASSERT ( mcLoaded . IsVRegAny ( ) ) ;
if ( ! IsLive ( checkInst - > mLiveness , mcLoaded . mVRegIdx , regRemaps ) )
{
break ;
}
if ( ( mcAddr . IsVRegAny ( ) ) & & ( ! IsLive ( checkInst - > mLiveness , mcAddr . mVRegIdx , regRemaps ) ) )
break ;
if ( hadPointerDeref )
{
// Pointer deref is okay as long as liveness ends one instruction afterwards
break ;
}
auto mcDest = checkInst - > GetDest ( ) ;
if ( ( mcDest ! = NULL ) & &
( ( mcDest - > mKind = = BeMCOperandKind_VReg ) | | ( mcDest - > mKind = = mcAddr . mKind ) ) )
{
// Any assignment to either of these regs ends our remapping loop
if ( ( mcDest - > mVRegIdx = = mcLoaded . mVRegIdx ) | | ( mcDest - > mVRegIdx = = mcAddr . mVRegIdx ) )
{
break ;
}
// Any writing into memory could be aliased, so we don't replace across that boundary
if ( HasPointerDeref ( * mcDest ) )
{
// Delay the 'return false' until we determine if our liveness ends on the next instruction.
// If it does, then this is okay because it didn't affect the source of any operations
hadPointerDeref = true ;
}
}
if ( checkInst - > IsInformational ( ) )
continue ;
if ( checkInst - > mKind = = BeMCInstKind_Call ) // Anything can happen during a call
break ;
if ( checkInst - > mDbgLoc ! = inst - > mDbgLoc )
break ;
auto operands = { & checkInst - > mArg0 , & checkInst - > mArg1 } ;
for ( auto & operand : operands )
{
// If we reference the loaded version during the range where the value hasn't changed,
// then just refer back to the original value. This is useful in cases like i++,
// where we want to be able to still do an INC on the source variable rather than
// "loadedI = i; i = loadedI + 1; ... use loadedI ..."
if ( ( operand - > mKind = = BeMCOperandKind_VReg ) & & ( operand - > mVRegIdx = = mcLoaded . mVRegIdx ) )
{
* operand = mcAddr ;
}
if ( ( operand - > mKind = = BeMCOperandKind_VRegLoad ) & & ( operand - > mVRegIdx = = mcLoaded . mVRegIdx ) )
{
if ( mcAddr . mKind = = BeMCOperandKind_VReg )
{
operand - > mVRegIdx = mcAddr . mVRegIdx ;
}
}
}
}
}
// Check for the special case of a single move to a SRET dereference - if so, then make the source composite be the source value
if ( ( inst - > mKind = = BeMCInstKind_Mov ) & & ( inst - > mArg0 . mKind = = BeMCOperandKind_VRegLoad ) & & ( inst - > mArg1 . mKind = = BeMCOperandKind_VReg ) )
{
auto vregInfoDest = GetVRegInfo ( inst - > mArg0 ) ;
auto vregInfoSrc = GetVRegInfo ( inst - > mArg1 ) ;
if ( ( vregInfoDest - > IsDirectRelTo ( ) ) & & ( vregInfoSrc - > IsDirectRelTo ( ) ) )
{
auto destRelTo = GetVRegInfo ( vregInfoDest - > mRelTo ) ;
auto srcRelTo = GetVRegInfo ( vregInfoSrc - > mRelTo ) ;
if ( ( destRelTo - > mIsRetVal ) & & ( vregInfoDest - > mAssignCount = = 1 ) & &
( ! destRelTo - > mIsExpr ) & & ( ! srcRelTo - > mIsExpr ) )
{
mCompositeRetVRegIdx = vregInfoDest - > mRelTo . mVRegIdx ;
RemoveInst ( mcBlock , instIdx ) ;
srcRelTo - > SetRetVal ( ) ;
instIdx - - ;
continue ;
}
}
}
// For the form:
// @Def %vreg0
// [%result] = <op> %vreg0, %vreg1
// Remap %vreg0 to %vreg1 if this is the only assignment, and thus an immutable definition, and vreg1 doesn't change
if ( ( nextDestOperand ) & &
( ( ( inst - > mKind = = BeMCInstKind_Def ) & & ( arg0 = = nextDestOperand ) ) | |
( ( inst - > mKind = = BeMCInstKind_LifetimeStart ) & & ( BeMCOperand : : ToLoad ( arg0 ) = = nextDestOperand ) ) ) )
{
lastDefDidMerge = false ;
auto vregInfoDest = GetVRegInfo ( nextDestOperand ) ;
auto checkOperands = { & nextInst - > mArg0 , & nextInst - > mArg1 } ;
bool checkArg1 = true ;
if ( nextInst - > mResult )
{
if ( ( nextInst - > mKind = = BeMCInstKind_Add ) | | ( nextInst - > mKind = = BeMCInstKind_Mul ) )
{
// These are commutative, so we can remap to either the LHS or RHS
checkArg1 = true ;
}
else
{
// We can only remap to LHS
checkArg1 = false ;
}
}
for ( int argCheckIdx = ( nextInst - > mResult ? 0 : 1 ) ; argCheckIdx < ( checkArg1 ? 2 : 1 ) ; argCheckIdx + + )
{
BeMCOperand origCheckOperand ;
if ( argCheckIdx = = 0 )
origCheckOperand = nextInst - > mArg0 ;
else
origCheckOperand = nextInst - > mArg1 ;
auto checkOperand = RemapOperand ( origCheckOperand , regRemaps ) ;
if ( checkOperand . mKind ! = BeMCOperandKind_VReg ) // Loads/Addrs not applicable
continue ;
auto vregInfoCheck = GetVRegInfo ( checkOperand ) ;
if ( vregInfoCheck = = NULL )
continue ;
if ( ( vregInfoDest - > mIsExpr ) | | ( vregInfoCheck - > mIsExpr ) )
continue ;
if ( ( ! vregInfoDest - > mForceMerge ) & & ( ! vregInfoDest - > mIsRetVal ) )
{
// If we merge to a vreg that has a pointer ref, that could be a deoptimization by not allowing
// either to bind to a register
if ( vregInfoDest - > mForceMem ! = vregInfoCheck - > mForceMem )
{
if ( ! GetType ( checkOperand ) - > IsComposite ( ) )
continue ;
}
}
// We have a special retVal case where we can allow the __returnVal to share an address with the
// actual 'ret' param. This is most useful when returning structs by value, to avoid a copy.
// The only downside is that editing the return value in the debugger will also show the value
// of the ret param changing, which is normally not acceptable but in this case we allow it.
bool allowCoexist = ( ( vregInfoDest - > mIsRetVal ) | |
( vregInfoCheck - > CanEliminate ( ) ) | | ( vregInfoDest - > CanEliminate ( ) ) ) ;
// Only vregs of the same types can be merged
if ( ! AreTypesEquivalent ( vregInfoCheck - > mType , vregInfoDest - > mType ) )
continue ;
bool canMerge = false ;
if ( ( nextInst - > IsMov ( ) ) & & ( allowCoexist ) )
{
if ( ( vregInfoCheck - > mForceMerge ) | | ( vregInfoCheck - > mIsRetVal ) )
canMerge = true ; // This is to allow the incoming vregs to combine with the ".addr" debug variables
else // Always use orig operands for liveness checking
canMerge = CheckVRegEqualityRange ( mcBlock , nextIdx , origCheckOperand , origNextDestOperand , regRemaps ) ;
}
else
{
// If we're dealing with a non-Mov instruction, then the values of both of these vregs will
// be different after the instruction, so we can only map them if the incoming vreg will be dead
// afterwards. If both are dbgVariables then we can't allow them to coeexist because the user
// could edit one of the values in the debugger and we don't want the other one changing.
if ( ( nextNextInst ! = NULL ) & &
( ! IsLive ( nextNextInst - > mLiveness , origCheckOperand . mVRegIdx , regRemaps ) ) & &
( ! IsLive ( nextNextInst - > mLiveness , checkOperand . mVRegIdx , regRemaps ) ) )
canMerge = true ;
}
if ( canMerge )
{
if ( vregInfoCheck - > mForceMerge )
vregInfoCheck - > mForceMerge = false ; // Only allow one merge
lastDefDidMerge = true ;
if ( vregInfoDest - > mIsRetVal )
{
// We don't do a remap for this return value optimization, because we want both vregs
// to still show in the debugger - even though they will point to the same address
// now (in the case of a struct return)
vregInfoCheck - > SetRetVal ( ) ;
//BF_ASSERT(mCompositeRetVRegIdx != -1);
//vregInfoCheck->mRelTo = BeMCOperand::FromVReg(mCompositeRetVRegIdx);
break ;
}
if ( ( vregInfoCheck - > mDbgVariable ! = NULL ) & & ( vregInfoDest - > mDbgVariable ! = NULL ) )
{
// We want separate vreg info so we can track the mDbgVariable separately, but we do want
// the same location
vregInfoDest - > mIsExpr = true ;
vregInfoDest - > mRelTo . mKind = BeMCOperandKind_VReg ;
vregInfoDest - > mRelTo . mVRegIdx = checkOperand . mVRegIdx ;
}
else
{
// Replace %vreg0 with %vreg1?
if ( vregInfoDest - > CanEliminate ( ) )
{
bool canReplace = true ;
if ( canReplace )
{
AddRegRemap ( nextDestOperand . mVRegIdx , checkOperand . mVRegIdx , regRemaps ) ;
break ;
}
}
// Or go the other way, and make %vreg1 use our variable directly since we're just handing off anyway
if ( vregInfoCheck - > CanEliminate ( ) )
{
if ( vregInfoCheck - > mIsRetVal )
{
vregInfoDest - > SetRetVal ( ) ;
//BF_ASSERT(mCompositeRetVRegIdx != -1);
//vregInfoCheck->mRelTo = BeMCOperand::FromVReg(mCompositeRetVRegIdx);
}
AddRegRemap ( checkOperand . mVRegIdx , nextDestOperand . mVRegIdx , regRemaps ) ;
if ( vregInfoDest - > mNaturalReg = = X64Reg_None )
vregInfoDest - > mNaturalReg = vregInfoCheck - > mNaturalReg ;
break ;
}
}
}
}
}
//if (mDebugging)
{
// For the form
// Def %vreg0
// Mov %vreg0, %vreg1
// <Op> <Arg>, %vreg0
// Where vreg0 has no other references, convert to
// <Op> <arg>, &vreg1
if ( ( inst - > mKind = = BeMCInstKind_Def ) & & ( nextInst - > mKind = = BeMCInstKind_Mov ) & & ( nextNextInst ! = NULL ) )
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( ( vregInfo - > mRefCount = = 2 ) & & ( inst - > mArg0 = = nextInst - > mArg0 ) & & ( ! nextInst - > mArg1 . IsNativeReg ( ) ) & &
( nextNextInst - > mArg0 ! = nextInst - > mArg0 ) & & ( nextNextInst - > mArg1 = = nextInst - > mArg0 ) & &
( AreTypesEquivalent ( GetType ( inst - > mArg0 ) , GetType ( nextInst - > mArg1 ) ) ) )
{
nextNextInst - > mArg1 = nextInst - > mArg1 ;
RemoveInst ( mcBlock , instIdx ) ;
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
}
}
// For the form
// <BinOp> %vreg0, %vreg1
// Test %vreg0, 1
// Remove test, because the BinOp will already set the correct flags
if ( ( nextInst - > mKind = = BeMCInstKind_Test ) & & ( nextInst - > mArg1 . IsImmediateInt ( ) ) & & ( nextInst - > mArg1 . mImmediate = = 1 ) & &
( nextInst - > mArg0 = = inst - > mArg0 ) )
{
if ( ( inst - > mKind = = BeMCInstKind_Xor ) | | ( inst - > mKind = = BeMCInstKind_Or ) | | ( inst - > mKind = = BeMCInstKind_And ) )
{
BF_ASSERT ( ! inst - > mResult ) ;
RemoveInst ( mcBlock , instIdx + 1 ) ;
instIdx - - ;
continue ;
}
}
if ( inst - > mResult )
{
auto nextArg0 = RemapOperand ( nextInst - > mArg0 , regRemaps ) ;
auto nextArg1 = RemapOperand ( nextInst - > mArg1 , regRemaps ) ;
// For the form:
// %vreg2 = <op> %vreg0, %vreg1
// Mov %vreg3, %vreg2
// If %vreg2 has no other references, combine into
// %vreg3 = <op> %vreg0, %vreg1
if ( ( nextInst - > IsMov ( ) ) & & ( result = = nextArg1 ) )
{
// We purposely check inst->mResult here rather than 'result', because that ref count is our
// indication of whether the result was actually used again
auto vregInfo = GetVRegInfo ( inst - > mResult ) ;
if ( ( vregInfo ! = NULL ) & & ( vregInfo - > mRefCount = = 2 ) & & ( vregInfo - > CanEliminate ( ) ) )
{
// Make sure the MOV isn't a conversion
auto movArg0Type = GetType ( nextInst - > mArg0 ) ;
auto movArg1Type = GetType ( nextInst - > mArg1 ) ;
if ( AreTypesEquivalent ( movArg0Type , movArg1Type ) )
{
// We actually eliminate the current inst incase there was a Def between the two,
// otherwise the Def would occur AFTER the assignment which would be bad
nextInst - > mResult = nextInst - > mArg0 ;
nextInst - > mKind = inst - > mKind ;
nextInst - > mArg0 = inst - > mArg0 ;
nextInst - > mArg1 = inst - > mArg1 ;
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
}
}
if ( inst - > IsCommutable ( ) )
{
if ( ( nextInst - > IsMov ( ) ) & & ( arg1 = = nextArg0 ) & & ( result = = nextArg1 ) )
{
break ;
}
}
// For the form:
// %vreg2 = <op> %vreg0, %vreg1
// If %vreg0 ends its life on this instruction,
// Replace all instances of %vreg2 with %vreg0
if ( ( inst - > mResult . mKind = = BeMCOperandKind_VReg ) & & ( inst - > mArg0 . mKind = = BeMCOperandKind_VReg ) & &
( inst - > mResult ! = inst - > mArg0 ) )
{
// Check liveness against both the orig arg0 and the remap
//if ((!nextInst->mLiveness->IsSet(inst->mArg0.mVRegIdx)) && (!nextInst->mLiveness->IsSet(arg0.mVRegIdx)))
if ( ! IsLive ( nextInst - > mLiveness , inst - > mArg0 . mVRegIdx , regRemaps ) )
{
// Only do a full regRemap if this is the def initialization
if ( ( prevInst ! = NULL ) & & ( prevInst - > mKind = = BeMCInstKind_Def ) & & ( prevInst - > mArg0 . mVRegIdx = = inst - > mResult . mVRegIdx ) )
{
auto vregInfoResult = GetVRegInfo ( result ) ;
auto vregInfo0 = GetVRegInfo ( arg0 ) ;
if ( ( vregInfoResult - > CanEliminate ( ) ) & & ( ! vregInfo0 - > mIsExpr ) )
{
AddRegRemap ( inst - > mResult . mVRegIdx , arg0 . mVRegIdx , regRemaps ) ;
}
}
}
}
}
if ( inst - > mKind = = BeMCInstKind_RestoreVolatiles )
{
if ( mcBlock = = mBlocks . back ( ) )
{
bool hadInvalidInst = false ;
for ( int checkInstIdx = instIdx + 1 ; checkInstIdx < ( int ) mcBlock - > mInstructions . size ( ) ; checkInstIdx + + )
{
auto checkInst = mcBlock - > mInstructions [ checkInstIdx ] ;
if ( ( checkInst - > mKind ! = BeMCInstKind_DbgDecl ) & &
( checkInst - > mKind ! = BeMCInstKind_EnsureInstructionAt ) & &
( checkInst - > mKind ! = BeMCInstKind_Ret ) )
{
hadInvalidInst = true ;
break ;
}
}
if ( ! hadInvalidInst )
{
// We return after this, we don't need to restore volatiles
inst - > mArg1 = BeMCOperand : : FromPreserveFlag ( BeMCPreserveFlag_NoRestore ) ;
continue ;
}
}
}
if ( nextNextInst = = NULL )
{
if ( reprocessInst )
instIdx - - ;
continue ;
}
// For the form
// Xor &vreg0, 1
// CondBr <label>, <op>
// If vreg0 dies at the end, convert to to:
// Test &veg0, 1
// CondBr <label>, ~<op>
// This is advantageous because Test is a non-modifying instruction so we can hopefully elide a copy
if ( ( inst - > mKind = = BeMCInstKind_Xor ) & &
( nextInst - > mKind = = BeMCInstKind_CondBr ) )
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( ( vregInfo ! = NULL ) & & ( ! IsLive ( nextNextInst - > mLiveness , inst - > mArg0 . mVRegIdx , regRemaps ) ) )
{
inst - > mKind = BeMCInstKind_Test ;
BF_ASSERT ( nextInst - > mArg1 . mKind = = BeMCOperandKind_CmpKind ) ;
nextInst - > mArg1 . mCmpKind = BeModule : : InvertCmp ( nextInst - > mArg1 . mCmpKind ) ;
if ( ( instIdx > = 2 ) & & ( ! lastDefDidMerge ) )
{
auto defInst = mcBlock - > mInstructions [ instIdx - 2 ] ;
if ( ( defInst - > mKind = = BeMCInstKind_Def ) & & ( defInst - > mArg0 = = inst - > mArg0 ) )
{
// Go back and try to eliminate this vreg
instIdx - = 3 ;
continue ;
}
}
}
}
// For the form:
// %vreg0 = CmpToBool <cmpType>
// Test %vreg0, %vreg0
// CondBr %label, eq
// If %vreg0 has no other references, convert to:
// CondBr %label, <cmpType>
if ( ( inst - > mKind = = BeMCInstKind_CmpToBool ) & &
( nextInst - > mKind = = BeMCInstKind_Test ) & & ( nextInst - > mArg0 = = nextInst - > mArg1 ) & &
( nextNextInst - > mKind = = BeMCInstKind_CondBr ) & &
( nextInst - > mArg0 = = inst - > mResult ) )
{
auto vregInfo = GetVRegInfo ( inst - > mResult ) ;
if ( ( vregInfo ! = NULL ) & & ( vregInfo - > mRefCount = = 3 ) )
{
BF_ASSERT ( nextNextInst - > mArg1 . mKind = = BeMCOperandKind_CmpKind ) ;
nextNextInst - > mArg1 . mCmpKind = inst - > mArg0 . mCmpKind ;
RemoveInst ( mcBlock , nextIdx ) ;
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
}
}
if ( reprocessInst )
instIdx - - ;
}
}
SetCurrentInst ( NULL ) ;
Dictionary < int , int > regRemapMap ;
for ( int vregIdx = 0 ; vregIdx < ( int ) mVRegInfo . size ( ) ; vregIdx + + )
{
int remapIdx = regRemaps . GetHead ( vregIdx ) ;
if ( remapIdx ! = vregIdx )
regRemapMap [ vregIdx ] = remapIdx ;
}
// We want to keep the earliest @Def and remap (in necessary) the later one
// For remaps, the liveVRregIdx will always indicate which @Def occurs earlier,
// unless there's a case I haven't considered
Dictionary < int , int > defMap ;
for ( auto & remapPair : regRemapMap )
{
int remapFrom = remapPair . mKey ;
int remapTo = remapPair . mValue ;
if ( remapTo < 0 )
continue ;
auto vregInfoFrom = mVRegInfo [ remapFrom ] ;
auto vregInfoTo = mVRegInfo [ remapTo ] ;
int bestVRegIdx = ( vregInfoFrom - > mDefOrderIdx < vregInfoTo - > mDefOrderIdx ) ? remapFrom : remapTo ;
//auto itr = defMap.find(remapTo);
//if (itr != defMap.end())
int * prevBestVRegIdxPtr = NULL ;
if ( defMap . TryGetValue ( remapTo , & prevBestVRegIdxPtr ) )
{
auto prevBestVRegIdx = * prevBestVRegIdxPtr ;
if ( mVRegInfo [ bestVRegIdx ] - > mDefOrderIdx < mVRegInfo [ prevBestVRegIdx ] - > mDefOrderIdx )
defMap [ remapTo ] = bestVRegIdx ;
}
else
defMap [ remapTo ] = bestVRegIdx ;
}
// We have a many-to-one relation so we have to use both a
HashSet < int > keepDefs ;
for ( auto & defPair : defMap )
keepDefs . Add ( defPair . mValue ) ;
HashSet < int > removeDefs ;
for ( auto & remapPair : regRemapMap )
{
/*if (keepDefs.find(remapPair.first) == keepDefs.end())
removeDefs . insert ( remapPair . first ) ; */
removeDefs . Add ( remapPair . mKey ) ;
/*if (keepDefs.find(remapPair.second) == keepDefs.end())
removeDefs . insert ( remapPair . second ) ; */
keepDefs . Add ( remapPair . mValue ) ;
auto vregInfoFrom = mVRegInfo [ remapPair . mKey ] ;
auto vregInfoTo = mVRegInfo [ remapPair . mValue ] ;
if ( vregInfoFrom - > mHasDynLife )
vregInfoTo - > mHasDynLife = true ;
}
auto _RemapOperand = [ & ] ( BeMCOperand * operand )
{
if ( operand - > IsVRegAny ( ) )
{
int * regIdxPtr = NULL ;
if ( regRemapMap . TryGetValue ( operand - > mVRegIdx , & regIdxPtr ) )
{
int regIdx = * regIdxPtr ;
if ( regIdx < 0 )
{
if ( operand - > mKind = = BeMCOperandKind_VRegLoad )
operand - > mKind = BeMCOperandKind_VReg ;
else
NotImpl ( ) ;
operand - > mVRegIdx = - regIdx ;
}
else
{
operand - > mVRegIdx = regIdx ;
}
}
}
if ( operand - > IsVRegAny ( ) )
{
if ( wantUnwrapVRegs . Contains ( operand - > mVRegIdx ) )
{
// Replace any rel-to symbol info with direct references to those symbols
auto vregInfo = mVRegInfo [ operand - > mVRegIdx ] ;
BF_ASSERT ( vregInfo - > IsDirectRelToAny ( ) ) ;
if ( operand - > mKind = = BeMCOperandKind_VReg )
* operand = vregInfo - > mRelTo ;
else if ( operand - > mKind = = BeMCOperandKind_VRegAddr )
* operand = BeMCOperand : : ToAddr ( vregInfo - > mRelTo ) ;
}
}
} ;
if ( ( ! regRemapMap . IsEmpty ( ) ) | | ( ! wantUnwrapVRegs . IsEmpty ( ) ) )
{
for ( auto mcBlock : mBlocks )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( inst - > IsDef ( ) )
{
//auto itr = removeDefs.find(inst->mArg0.mVRegIdx);
//if (itr != removeDefs.end())
if ( ( removeDefs . Contains ( inst - > mArg0 . mVRegIdx ) ) | | ( wantUnwrapVRegs . Contains ( inst - > mArg0 . mVRegIdx ) ) )
{
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
}
/*if (inst->mKind == BeMCInstKind_LifetimeStart)
{
if ( removeLifetimeStarts . find ( inst - > mArg0 . mVRegIdx ) ! = removeLifetimeStarts . end ( ) )
{
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
} */
auto operands = { & inst - > mResult , & inst - > mArg0 , & inst - > mArg1 } ;
for ( auto & operand : operands )
{
_RemapOperand ( operand ) ;
}
}
}
for ( auto vregInfo : mVRegInfo )
{
auto operands = { & vregInfo - > mRelTo , & vregInfo - > mRelOffset } ;
for ( auto & operand : operands )
{
if ( operand - > IsVRegAny ( ) )
{
RemapOperand ( * operand , regRemaps ) ;
_RemapOperand ( operand ) ;
}
}
}
}
}
void BeMCContext : : DoRegAssignPass ( )
{
BP_ZONE ( " BeMCContext::DoRegAssignPass " ) ;
bool generateLiveness = true ;
//
if ( generateLiveness )
{
for ( auto & node : mColorizer . mNodes )
{
node . mEdges . Clear ( ) ;
}
mColorizer . Prepare ( ) ;
GenerateLiveness ( ) ;
}
else
{
mColorizer . Prepare ( ) ;
}
mColorizer . GenerateRegCosts ( ) ;
//
{
BP_ZONE ( " BeMCContext::DoRegAssignPass:ints " ) ;
mColorizer . AssignRegs ( BeMCColorizer : : RegKind_Ints ) ;
}
//
{
BP_ZONE ( " BeMCContext::DoRegAssignPass:float " ) ;
mColorizer . AssignRegs ( BeMCColorizer : : RegKind_Floats ) ;
}
# ifdef _DEBUG
BF_ASSERT ( mColorizer . Validate ( ) ) ;
for ( int vregIdx = 0 ; vregIdx < ( int ) mVRegInfo . size ( ) ; vregIdx + + )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mRefCount = = 0 )
continue ;
if ( ( vregInfo - > mIsRetVal ) & & ( mCompositeRetVRegIdx ! = - 1 ) & & ( vregIdx ! = mCompositeRetVRegIdx ) )
continue ;
if ( ( vregInfo - > mForceReg ) & & ( ! vregInfo - > mRelTo ) & & ( vregInfo - > mReg = = X64Reg_None ) )
{
Fail ( " Failed to assign register to ForceReg vreg " ) ;
}
if ( vregInfo - > mDisableRAX )
{
BF_ASSERT ( ResizeRegister ( vregInfo - > mReg , 8 ) ! = X64Reg_RAX ) ;
}
if ( vregInfo - > mDisableRDX )
{
BF_ASSERT ( ResizeRegister ( vregInfo - > mReg , 8 ) ! = X64Reg_RDX ) ;
}
if ( vregInfo - > mDisableR11 )
{
BF_ASSERT ( ResizeRegister ( vregInfo - > mReg , 8 ) ! = X64Reg_R11 ) ;
}
if ( vregInfo - > mDisableR12 )
{
BF_ASSERT ( ResizeRegister ( vregInfo - > mReg , 8 ) ! = X64Reg_R12 ) ;
}
if ( vregInfo - > mDisableR13 )
{
BF_ASSERT ( ResizeRegister ( vregInfo - > mReg , 8 ) ! = X64Reg_R13 ) ;
}
}
# endif
}
void BeMCContext : : DoFrameObjPass ( )
{
BF_ASSERT ( mBlocks . size ( ) = = 1 ) ;
SetCurrentInst ( NULL ) ;
// MS x64 ABI requires a "home address" of 4 intptrs when we call a function, plus whatever
// we need for calls with more than 4 params.
// If we're doing UseBP, we have to allocate these at call time
int homeSize = BF_ALIGN ( BF_MAX ( mMaxCallParamCount , 4 ) * 8 , 16 ) ;
2019-11-26 13:11:17 -08:00
2019-08-23 11:56:54 -07:00
mStackSize = 0 ;
if ( mUseBP )
mUsedRegs . Add ( X64Reg_RBP ) ;
int regStackOffset = 0 ;
int xmmRegStackSize = 0 ;
for ( auto usedReg : mUsedRegs )
{
if ( ! IsVolatileReg ( usedReg ) )
{
BF_ASSERT ( usedReg ! = X64Reg_RSP ) ;
if ( IsXMMReg ( usedReg ) )
xmmRegStackSize + = 16 ;
else
regStackOffset + = 8 ;
}
}
int xmmStackOffset = - 1 ;
if ( xmmRegStackSize > 0 )
{
int align = 16 ;
int alignOffset = regStackOffset + 8 ;
int alignedPosition = ( mStackSize + alignOffset + ( align - 1 ) ) & ~ ( align - 1 ) ;
mStackSize = alignedPosition - alignOffset ;
mStackSize + = xmmRegStackSize ;
xmmStackOffset = - mStackSize - regStackOffset ;
}
for ( int vregIdx = 0 ; vregIdx < ( int ) mVRegInfo . size ( ) ; vregIdx + + )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( ( vregInfo - > mIsRetVal ) & & ( vregIdx ! = mCompositeRetVRegIdx ) )
continue ;
if ( ( vregInfo - > mRefCount > 0 ) & & ( ! vregInfo - > mIsExpr ) & & ( vregInfo - > mReg = = X64Reg_None ) & & ( vregInfo - > mFrameOffset = = INT_MIN ) )
{
2019-11-26 13:11:17 -08:00
BF_ASSERT ( vregInfo - > mAlign ! = - 1 ) ;
int align = BF_MAX ( vregInfo - > mAlign , 1 ) ;
2019-08-23 11:56:54 -07:00
int alignOffset = regStackOffset + 8 ;
int alignedPosition = ( mStackSize + alignOffset + ( align - 1 ) ) & ~ ( align - 1 ) ;
mStackSize = alignedPosition - alignOffset ;
//vregInfo->mFrameOffset = -mStackSize - regStackOffset - 8;
2019-11-26 13:11:17 -08:00
mStackSize + = BF_ALIGN ( vregInfo - > mType - > mSize , vregInfo - > mAlign ) ;
2019-08-23 11:56:54 -07:00
vregInfo - > mFrameOffset = - mStackSize - regStackOffset ;
}
}
// If we have dynamic stack resizing then we have a stack frame and must be 16-byte aligned
// even if we're a leaf function
bool mHasFramePointer = false ;
// Potentially pull off retaddr bytes if alignment allows --
// Stack must be aligned to 16 bytes and retaddr offsets us by 8,
// so we must always offset by 0x?8
int align = 8 ;
mStackSize = ( mStackSize + ( align - 1 ) ) & ~ ( align - 1 ) ;
//if (!mUseBP)
{
// MS x64 ABI requires a "home address" of 4 intptrs when we call a function, plus whatever
// we need for calls with more than 4 params.
// If we're doing UseBP, we have to allocate these at call time
if ( mMaxCallParamCount ! = - 1 )
{
mStackSize + = homeSize ;
// Even param counts, to align to 16 bytes.
// Minimum is space for 4 params
//int paramSpace = (std::max(mMaxCallParamCount, 4) + 1) & ~1;
//mStackSize += paramSpace * 8;
}
}
int stackAdjust = mStackSize ;
mStackSize + = regStackOffset ;
if ( ( mStackSize ! = 0 ) & & ( mStackSize % 16 = = 0 ) )
{
mStackSize + = 8 ;
stackAdjust + = 8 ;
}
mActiveBlock = mBlocks [ 0 ] ;
if ( mUseBP )
{
AllocInst ( BeMCInstKind_Unwind_SetBP , 0 ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_RBP ) , BeMCOperand : : FromReg ( X64Reg_RSP ) , 0 ) ;
}
int xmmRegIdx = 0 ;
for ( auto usedReg : mUsedRegs )
{
if ( ( ! IsVolatileReg ( usedReg ) ) & & ( IsXMMReg ( usedReg ) ) )
{
AllocInst ( BeMCInstKind_Unwind_SaveXMM , BeMCOperand : : FromReg ( usedReg ) , BeMCOperand : : FromImmediate ( mStackSize + xmmStackOffset + xmmRegIdx * 16 ) , 0 ) ;
AllocInst ( BeMCInstKind_Push , BeMCOperand : : FromReg ( usedReg ) , BeMCOperand : : FromImmediate ( mStackSize + xmmStackOffset + xmmRegIdx * 16 ) , 0 ) ;
xmmRegIdx + + ;
}
}
if ( stackAdjust > 0 )
{
AllocInst ( BeMCInstKind_Unwind_Alloc , BeMCOperand : : FromImmediate ( stackAdjust ) , 0 ) ;
AllocInst ( BeMCInstKind_Sub , BeMCOperand : : FromReg ( X64Reg_RSP ) , BeMCOperand : : FromImmediate ( stackAdjust ) , 0 ) ;
if ( stackAdjust > = 4096 )
{
BeMCOperand mcFunc ;
mcFunc . mKind = BeMCOperandKind_SymbolAddr ;
mcFunc . mSymbolIdx = mCOFFObject - > GetSymbolRef ( " __chkstk " ) - > mIdx ;
AllocInst ( BeMCInstKind_Call , mcFunc , 0 ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand : : FromImmediate ( stackAdjust ) , 0 ) ;
}
}
for ( auto usedReg : mUsedRegs )
{
if ( ( ! IsVolatileReg ( usedReg ) ) & & ( ! IsXMMReg ( usedReg ) ) )
{
AllocInst ( BeMCInstKind_Unwind_PushReg , BeMCOperand : : FromReg ( usedReg ) , 0 ) ;
AllocInst ( BeMCInstKind_Push , BeMCOperand : : FromReg ( usedReg ) , 0 ) ;
}
}
BeMCOperand restoreBPVal ;
if ( mUseBP )
{
auto nativeType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
restoreBPVal = AllocVirtualReg ( nativeType ) ;
auto restoreBPVReg = mVRegInfo [ restoreBPVal . mVRegIdx ] ;
restoreBPVReg - > mIsExpr = true ;
restoreBPVReg - > mRelTo = BeMCOperand : : FromReg ( X64Reg_RBP ) ;
restoreBPVReg - > mRelOffset = BeMCOperand : : FromImmediate ( stackAdjust ) ;
restoreBPVal . mKind = BeMCOperandKind_VReg ;
}
for ( int instIdx = 0 ; instIdx < ( int ) mActiveBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto checkInst = mActiveBlock - > mInstructions [ instIdx ] ;
if ( checkInst - > mKind = = BeMCInstKind_Ret )
{
mCurDbgLoc = checkInst - > mDbgLoc ;
int insertIdx = instIdx ;
int xmmRegIdx = 0 ;
for ( auto usedReg : mUsedRegs )
{
if ( ( ! IsVolatileReg ( usedReg ) ) & & ( IsXMMReg ( usedReg ) ) )
{
AllocInst ( BeMCInstKind_Pop , BeMCOperand : : FromReg ( usedReg ) , BeMCOperand : : FromImmediate ( mStackSize + xmmStackOffset + xmmRegIdx * 16 ) , insertIdx + + ) ;
xmmRegIdx + + ;
}
}
if ( mUseBP )
{
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_RSP ) , restoreBPVal , insertIdx + + ) ;
}
else if ( stackAdjust > 0 )
{
AllocInst ( BeMCInstKind_Add , BeMCOperand : : FromReg ( X64Reg_RSP ) , BeMCOperand : : FromImmediate ( stackAdjust ) , insertIdx + + ) ;
instIdx + + ;
}
for ( auto usedReg : mUsedRegs )
{
if ( ( ! IsVolatileReg ( usedReg ) ) & & ( ! IsXMMReg ( usedReg ) ) )
AllocInst ( BeMCInstKind_Pop , BeMCOperand : : FromReg ( usedReg ) , insertIdx + + ) ;
}
instIdx = insertIdx ;
}
}
}
static int gLegalizeIdx = 0 ;
void BeMCContext : : DoActualization ( )
{
while ( true )
{
bool hasMoreWork = false ;
for ( auto mcBlock : mBlocks )
{
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
SetCurrentInst ( inst ) ;
if ( inst - > mKind = = BeMCInstKind_DbgDecl )
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo - > mWantsExprActualize )
{
if ( inst - > mArg0 . mKind = = BeMCOperandKind_VReg )
{
if ( vregInfo - > mDbgVariable - > mIsValue )
{
vregInfo - > mDbgVariable - > mIsValue = false ;
inst - > mArg0 . mKind = BeMCOperandKind_VRegAddr ;
}
else
{
inst - > mArg0 . mKind = BeMCOperandKind_VRegAddr ;
vregInfo - > mDbgVariable - > mType = mModule - > mDbgModule - > CreateReferenceType ( vregInfo - > mDbgVariable - > mType ) ;
}
// if (auto ptrType = BeValueDynCast<BeDbgPointerType>(vregInfo->mDbgVariable->mType))
// vregInfo->mDbgVariable->mType = ptrType->mElement;
// if (vregInfo->mType->IsPointer())
// vregInfo->mType = ((BePointerType*)vregInfo->mType)->mElementType;
}
vregInfo - > mWantsExprActualize = false ;
}
}
if ( inst - > IsDef ( ) )
{
int vregIdx = inst - > mArg0 . mVRegIdx ;
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo - > mWantsExprActualize )
{
if ( vregInfo - > mDbgVariable ! = NULL )
{
// Wait until dbgDecl so we can potentially set to VRegAddr
}
else
{
vregInfo - > mWantsExprActualize = false ;
}
BF_ASSERT ( ( ! vregInfo - > IsDirectRelTo ( ) ) | | ( vregInfo - > mDbgVariable ! = NULL ) ) ;
if ( vregInfo - > mIsExpr )
{
// Create a new reg with the expression, and then load the value into the old non-expressionized reg
// This has the advantage of fixing any other references to this expr, too
auto scratchReg = AllocVirtualReg ( vregInfo - > mType , 2 , false ) ;
auto scratchVRegInfo = mVRegInfo [ scratchReg . mVRegIdx ] ;
scratchVRegInfo - > mIsExpr = true ;
scratchVRegInfo - > mRelTo = vregInfo - > mRelTo ;
scratchVRegInfo - > mRelOffset = vregInfo - > mRelOffset ;
scratchVRegInfo - > mRelOffsetScale = vregInfo - > mRelOffsetScale ;
vregInfo - > mIsExpr = false ;
vregInfo - > mRelTo = BeMCOperand ( ) ;
vregInfo - > mRelOffset = BeMCOperand ( ) ;
vregInfo - > mRelOffsetScale = 1 ;
CreateDefineVReg ( scratchReg , instIdx + + + 1 ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromVReg ( vregIdx ) , scratchReg , instIdx + + + 1 ) ;
}
}
}
}
}
if ( ! hasMoreWork )
break ;
}
}
void BeMCContext : : DoLoads ( )
{
for ( auto mcBlock : mBlocks )
{
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
SetCurrentInst ( inst ) ;
if ( inst - > mKind = = BeMCInstKind_DefLoad )
{
// Almost all DefLoads should be eliminated during InstCombine, but for 'persistent loads', we need
// to actualize them now
inst - > mKind = BeMCInstKind_Def ;
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
if ( vregInfo - > mRefCount = = 0 )
continue ;
auto relLoaded = vregInfo - > mRelTo ;
auto loadTo = inst - > mArg0 ;
BF_ASSERT ( loadTo . mKind = = BeMCOperandKind_VReg ) ;
AllocInst ( BeMCInstKind_Mov , loadTo , relLoaded , instIdx + + + 1 ) ;
vregInfo - > mIsExpr = false ;
vregInfo - > mRelTo = BeMCOperand ( ) ;
}
if ( inst - > mKind = = BeMCInstKind_Def )
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
if ( vregInfo - > mRefCount = = 0 )
continue ;
if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_VRegAddr )
{
auto vregRelInfo = mVRegInfo [ vregInfo - > mRelTo . mVRegIdx ] ;
vregRelInfo - > mForceMem = true ;
CheckForce ( vregInfo ) ;
}
}
}
}
}
static int sLegalizationIdx = 0 ;
// Returning false means we generated some new vregs that need to be assigned to registers
bool BeMCContext : : DoLegalization ( )
{
BP_ZONE ( " BeMCContext::DoLegalization " ) ;
bool debugging = false ;
//if (mBeFunction->mName == "?DrawEntry@DrawContext@PerfView@BeefPerf@@QEAAXPEAVGraphics@gfx@Beefy@@PEAVTrackNodeEntry@23@MM@Z")
//debugging = true;
if ( debugging )
OutputDebugStrF ( " DoLegalization \n " ) ;
bool isFinalRun = true ;
int startingVRegCount = ( int ) mVRegInfo . size ( ) ;
HashSet < int > vregExprChangeSet ;
bool hasPendingActualizations = false ;
SetCurrentInst ( NULL ) ;
int regPreserveDepth = 0 ;
for ( auto mcBlock : mBlocks )
{
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
BfIRInitType pendingInitKind = BfIRInitType_NotNeeded ;
gLegalizeIdx + + ;
auto inst = mcBlock - > mInstructions [ instIdx ] ;
SetCurrentInst ( inst ) ;
if ( ( mDebugging ) & & ( inst - > mKind = = BeMCInstKind_IMul ) )
{
NOP ;
}
if ( inst - > mKind = = BeMCInstKind_Mov )
{
// Useless mov, remove it
if ( OperandsEqual ( inst - > mArg0 , inst - > mArg1 , true ) )
{
// We leave the instructions alone here and remove them in DoRegFinalization.
// There are some cases with DirectRelTo vregs where we need to preserve liveness that a mov
// generates. We just want to keep from inserting any additional legalization instructions here (ie: movs to temps)
continue ;
}
}
BeMCInst * nextInst = NULL ;
if ( instIdx < mcBlock - > mInstructions . size ( ) - 1 )
{
// It's important that we do this at the top before any instructions might get split, so liveness of the next instruction can be analyzed
nextInst = mcBlock - > mInstructions [ instIdx + 1 ] ;
}
auto arg0 = GetFixedOperand ( inst - > mArg0 ) ;
auto arg1 = GetFixedOperand ( inst - > mArg1 ) ;
auto arg0Type = GetType ( arg0 ) ;
auto arg1Type = GetType ( arg1 ) ;
if ( ( arg0Type ! = NULL ) & & ( arg0Type - > mSize = = 0 ) & & ( ! inst - > IsPsuedo ( ) ) )
{
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
// Check operands
if ( ( ! inst - > IsPsuedo ( ) ) & & ( arg0Type ! = NULL ) & & ( ! arg0Type - > IsComposite ( ) ) )
{
bool replacedOpr = false ;
for ( int oprIdx = 0 ; oprIdx < 2 ; oprIdx + + )
{
BeMCOperand & origOperand = ( oprIdx = = 0 ) ? inst - > mArg0 : inst - > mArg1 ;
BeMCOperand & remappedOperand = ( oprIdx = = 0 ) ? arg0 : arg1 ;
if ( remappedOperand . IsVRegAny ( ) )
{
bool isMulti = false ;
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
int errorVReg = - 1 ;
auto rmForm = GetRMParams ( remappedOperand , regA , regB , bScale , disp , & errorVReg ) ;
bool badOperand = false ;
bool scratchForceReg = false ;
if ( rmForm = = BeMCRMMode_Invalid )
{
badOperand = true ;
scratchForceReg = true ;
}
// We can only allow a "complex direct" expression such as (RAX+RBX) on the SRC side of
// a mov, where it can be emitted as a LEA. All other uses are invalid.
bool allowComplexDirect = ( inst - > mKind = = BeMCInstKind_Mov ) & & ( oprIdx = = 1 ) ;
if ( allowComplexDirect )
{
auto vregInfo = GetVRegInfo ( remappedOperand ) ;
if ( ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_SymbolAddr ) & &
( vregInfo - > mRelOffset . IsImmediateInt ( ) ) & &
( vregInfo - > mRelOffsetScale = = 1 ) )
{
badOperand = false ;
}
}
else
{
if ( ( rmForm = = BeMCRMMode_Direct ) & &
( ( regB ! = X64Reg_None ) | | ( bScale ! = 1 ) | | ( disp ! = 0 ) ) )
{
badOperand = true ;
}
// bool isSimple = (rmForm == BeMCRMMode_Direct) && (regB == X64Reg_None) && (bScale == 1) && (disp == 0);
// if ((!isSimple) && (oprIdx == 1) && (!arg0isSimple))
// {
// NOP;
// }
// if (oprIdx == 0)
// arg0isSimple = isSimple;
}
if ( badOperand )
{
BeMCOperand checkOperand = remappedOperand ;
while ( checkOperand . mKind = = BeMCOperandKind_VReg )
{
auto vregInfo = GetVRegInfo ( checkOperand ) ;
if ( ! vregInfo - > IsDirectRelToAny ( ) )
break ;
checkOperand = vregInfo - > mRelTo ;
}
if ( inst - > mKind = = BeMCInstKind_Mov )
{
if ( ( checkOperand . mKind = = BeMCOperandKind_Symbol ) | | ( checkOperand . mKind = = BeMCOperandKind_SymbolAddr ) )
{
// SymbolAddr can be emitted as an LEA
badOperand = false ;
}
if ( checkOperand . mKind = = BeMCOperandKind_VRegAddr )
{
auto vregInfo = GetVRegInfo ( checkOperand ) ;
if ( ( vregInfo - > IsDirectRelToAny ( ) ) & & ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_Symbol ) )
{
badOperand = false ;
}
}
}
}
if ( badOperand )
{
//if (vregExprChangeSet.find(errorVReg) == vregExprChangeSet.end())
if ( ! vregExprChangeSet . Contains ( errorVReg ) )
{
auto remappedVRegInfo = mVRegInfo [ remappedOperand . mVRegIdx ] ;
BeMCOperand savedOperand = BeMCOperand : : FromVReg ( remappedOperand . mVRegIdx ) ;
BeMCOperand newOperand ;
if ( origOperand . mKind = = BeMCOperandKind_VRegLoad )
{
// Loads keep their load-ness
savedOperand = BeMCOperand : : FromVReg ( remappedOperand . mVRegIdx ) ;
newOperand = origOperand ;
}
else
{
savedOperand = origOperand ;
newOperand . mKind = BeMCOperandKind_VReg ;
}
auto type = GetType ( savedOperand ) ;
//TODO: Are there circumstances when this needs to be forceReg?
BeMCOperand scratchReg = AllocVirtualReg ( type , 2 , scratchForceReg ) ;
int insertPos = FindSafeInstInsertPos ( instIdx ) ;
CreateDefineVReg ( scratchReg , insertPos ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , savedOperand , insertPos + 1 ) ;
newOperand . mVRegIdx = scratchReg . mVRegIdx ;
IntroduceVRegs ( scratchReg , mcBlock , insertPos , insertPos + 1 ) ;
if ( ( insertPos = = instIdx ) | | ( ! scratchForceReg ) )
{
// Only allow short-lived forceRegs
instIdx + = 2 ;
//origOperand.mVRegIdx = scratchReg.mVRegIdx;
origOperand = newOperand ;
}
else
{
// .. otherwise we need yet another layer of indirection for the load
auto loadedType = GetType ( inst - > mArg0 ) ;
BeMCOperand loadedReg = AllocVirtualReg ( loadedType , 2 , false ) ;
CreateDefineVReg ( loadedReg , insertPos + 2 ) ;
AllocInst ( BeMCInstKind_Mov , loadedReg , origOperand , insertPos + 3 ) ;
IntroduceVRegs ( scratchReg , mcBlock , insertPos + 2 , insertPos + 3 ) ;
instIdx + = 4 ;
origOperand = loadedReg ;
}
replacedOpr = true ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " BadOperand %d \n " , errorVReg ) ;
}
}
}
}
if ( replacedOpr )
{
//instIdx--;
continue ;
}
}
if ( ! inst - > IsPsuedo ( ) )
{
if ( ( HasSymbolAddr ( inst - > mArg0 ) ) & & ( inst - > mKind ! = BeMCInstKind_Call ) )
{
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , true , false ) ;
isFinalRun = false ;
// if (debugging)
// OutputDebugStrF(" SymbolAddr0\n");
}
// We can only allow symbol addresses on a MOV where they can be made into a LEA
if ( ( HasSymbolAddr ( inst - > mArg1 ) ) & & ( inst - > mKind ! = BeMCInstKind_Mov ) )
{
ReplaceWithNewVReg ( inst - > mArg1 , instIdx , true , false ) ;
isFinalRun = false ;
// if (debugging)
// OutputDebugStrF(" SymbolAddr1\n");
}
}
if ( ! inst - > IsPsuedo ( ) )
{
if ( ( arg0 . IsImmediate ( ) ) & & ( arg1 ) & & ( inst - > IsCommutable ( ) ) )
{
// Always make the immediate be the second arg
BF_SWAP ( inst - > mArg0 , inst - > mArg1 ) ;
BF_SWAP ( arg0 , arg1 ) ;
}
bool isIntMul = ( inst - > IsMul ( ) ) & & ( arg0Type - > IsInt ( ) ) ;
bool isIntDiv = ( ( ( inst - > mKind = = BeMCInstKind_IDiv ) | | ( inst - > mKind = = BeMCInstKind_Div ) ) ) & & ( arg0Type - > IsInt ( ) ) ;
bool isMov_R64_IMM64 = ( ( inst - > mKind = = BeMCInstKind_Mov ) & & ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsImmediateInt ( ) ) ) ;
bool doStdCheck = true ;
if ( isMov_R64_IMM64 )
doStdCheck = false ;
bool is3FormMul = false ;
if ( ( isIntMul ) & & ( inst - > mResult ) )
{
is3FormMul = true ;
if ( inst - > mArg1 . IsImmediateInt ( ) )
{
bool isLegal = true ;
int64 immediateInt = inst - > mArg1 . GetImmediateInt ( ) ;
if ( inst - > mKind = = BeMCInstKind_Mul )
{
if ( immediateInt > 0xFFFFFFFFLL )
isLegal = false ;
}
else
{
if ( ( immediateInt < - 0x80000000LL ) | | ( immediateInt > 0x7FFFFFFF ) )
isLegal = false ;
}
if ( isLegal )
doStdCheck = false ;
}
}
// Int 3-form mul does not follow these rules
if ( doStdCheck )
{
bool isIncOrDec = false ;
isIncOrDec = ( ( ( inst - > mKind = = BeMCInstKind_Add ) | | ( inst - > mKind = = BeMCInstKind_Sub ) ) & &
( arg1 . IsImmediateInt ( ) ) & & ( arg1 . mImmediate = = 1 ) ) ;
bool badOps = false ;
if ( ( ! isIncOrDec ) & & ( ! isIntMul ) & & ( ! isIntDiv ) )
{
if ( ( arg0 . MayBeMemory ( ) ) & & ( arg1 . MayBeMemory ( ) ) )
{
if ( arg0 = = arg1 )
{
if ( inst - > mKind ! = BeMCInstKind_Mov )
{
badOps = true ;
}
}
else
badOps = true ;
}
//TODO: For what instructions was this true? CMP, MOV, ADD, etc... seems to have it
if ( ( arg1Type ! = NULL ) & & ( arg1Type - > IsFloat ( ) ) )
{
// MOV is allowed on '<r/m>, <imm>'
if ( ( inst - > mKind ! = BeMCInstKind_Mov ) & & ( arg0 . MayBeMemory ( ) ) & & ( arg1 . IsImmediate ( ) ) )
badOps = true ;
}
}
// "MOV <r>, <imm64>" is the only instruction that allows an IMM64 immediate
if ( ( ( inst - > mKind ! = BeMCInstKind_Mov ) | | ( ! arg0 . IsNativeReg ( ) ) ) & &
( arg1 . mKind = = BeMCOperandKind_Immediate_i64 ) )
{
if ( ( arg1 . mImmediate < - 0x80000000LL ) | | ( arg1 . mImmediate > 0x7FFFFFFF ) )
badOps = true ;
}
if ( badOps )
{
// On X64 we can never have an instruction where both args are memory so we create a short-lived scratch vreg
// and run another reg pass to generate register access
// From: <op> a, b
// To: @Def scratch
// mov scratch, b
// <op> a, scratch
auto targetType = GetType ( inst - > mArg0 ) ;
if ( ! targetType - > IsComposite ( ) )
{
auto scratchType = GetType ( inst - > mArg1 ) ;
BeMCOperand scratchReg = AllocVirtualReg ( scratchType , 2 , true ) ;
CreateDefineVReg ( scratchReg , instIdx ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , inst - > mArg1 , instIdx + 1 ) ;
inst - > mArg1 = scratchReg ;
IntroduceVRegs ( scratchReg , mcBlock , instIdx , instIdx + 2 ) ;
// Don't process the changed instructions until after another reg pass
instIdx + = 2 ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " BadOps \n " ) ;
continue ;
}
}
if ( inst - > mResult )
{
if ( inst - > mResult = = inst - > mArg0 )
{
inst - > mResult = BeMCOperand ( ) ;
}
else if ( ( arg0Type - > IsFloat ( ) ) & & ( ! CouldBeReg ( inst - > mResult ) ) )
{
// We need a REG on the dest for sure, so just create a scratch here, otherwise we end up
// requiring additional scratch vregs later
// FROM:
// %vreg0 = op %vreg1, %vreg2
// To:
// Mov %scratch<reg>, %vreg1
// Op %scratch<reg>, %vreg2
// Mov %result, %scatch<reg>
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , true ) ;
AllocInst ( BeMCInstKind_Mov , inst - > mResult , inst - > mArg0 , instIdx + 1 ) ;
IntroduceVRegs ( inst - > mArg0 , mcBlock , instIdx , instIdx + 2 ) ;
inst - > mResult = BeMCOperand ( ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Float RegOnDest \n " ) ;
instIdx + + ;
continue ;
}
else if ( ( inst - > mResult = = inst - > mArg1 ) & &
( ( inst - > mKind = = BeMCInstKind_Add ) | | ( inst - > mKind = = BeMCInstKind_Mul ) | | ( inst - > mKind = = BeMCInstKind_IMul ) ) )
{
BF_SWAP ( inst - > mArg0 , inst - > mArg1 ) ;
BF_SWAP ( arg0 , arg1 ) ;
inst - > mResult = BeMCOperand ( ) ;
}
else
{
bool addCanBeLEA =
( ( inst - > mKind = = BeMCInstKind_Sub ) | | ( inst - > mKind = = BeMCInstKind_Add ) ) & &
( arg0Type - > mSize > = 2 ) & & // Int8s don't have an LEA
( GetFixedOperand ( inst - > mResult ) . IsNativeReg ( ) ) & & ( arg0 . IsNativeReg ( ) ) & & ( arg1 . IsImmediateInt ( ) ) ;
if ( addCanBeLEA )
{
// LEA is add-only
if ( inst - > mKind = = BeMCInstKind_Sub )
{
BF_ASSERT ( inst - > mArg1 . IsImmediate ( ) ) ;
inst - > mKind = BeMCInstKind_Add ;
inst - > mArg1 . mImmediate = - inst - > mArg1 . mImmediate ;
}
}
else
{
bool handled = false ;
if ( OperandsEqual ( inst - > mResult , inst - > mArg1 ) )
{
if ( inst - > mKind = = BeMCInstKind_Sub )
{
// From: b = Sub a, b
// To: Neg b
// Add b, a
AllocInst ( BeMCInstKind_Add , inst - > mResult , inst - > mArg0 , instIdx + 1 ) ;
inst - > mKind = BeMCInstKind_Neg ;
inst - > mArg0 = inst - > mResult ;
inst - > mArg1 = BeMCOperand ( ) ;
inst - > mResult = BeMCOperand ( ) ;
handled = true ;
instIdx - - ; // Rerun on the Neg
}
else
{
// We need a scratch reg for this
ReplaceWithNewVReg ( inst - > mArg1 , instIdx , true ) ;
IntroduceVRegs ( inst - > mArg1 , mcBlock , instIdx , instIdx + 2 ) ;
}
}
if ( ! handled )
{
// From: result = <op> a, b
// To: mov result, a
// <op> result, b
AllocInst ( BeMCInstKind_Mov , inst - > mResult , inst - > mArg0 , instIdx + + ) ;
inst - > mArg0 = inst - > mResult ;
inst - > mResult = BeMCOperand ( ) ;
FixVRegInitFlags ( inst , inst - > mArg0 ) ;
if ( debugging )
OutputDebugStrF ( " Result \n " ) ;
isFinalRun = false ;
}
}
continue ;
}
}
}
if ( ( inst - > mKind ! = BeMCInstKind_Cmp ) & &
( inst - > mKind ! = BeMCInstKind_CmpToBool ) & &
( inst - > mKind ! = BeMCInstKind_Mov ) )
{
if ( ( arg0Type ! = NULL ) & & ( arg0Type - > IsFloat ( ) ) )
{
// <r/m>, <xmm> is not valid, <xmm>, <r/m>
if ( ! arg0 . IsNativeReg ( ) )
{
if ( ( arg1 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsVReg ( ) ) & & ( nextInst ! = NULL ) & & ( nextInst - > mLiveness ! = NULL ) & & ( inst - > IsCommutable ( ) ) )
{
int underlyingVRegIdx = GetUnderlyingVReg ( inst - > mArg1 . mVRegIdx ) ;
if ( ! mLivenessContext . IsSet ( nextInst - > mLiveness , underlyingVRegIdx ) )
{
// If Arg1 is a temporary vreg, then we can use that as our destination so we don't need
// to create another temporary vreg
BF_SWAP ( inst - > mArg0 , inst - > mArg1 ) ;
AllocInst ( BeMCInstKind_Mov , inst - > mArg1 , inst - > mArg0 , instIdx + 1 ) ;
continue ;
}
}
BeMCOperand prevDest ;
auto prevDestPtr = inst - > GetDest ( ) ;
if ( prevDestPtr ! = NULL )
prevDest = * prevDestPtr ;
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , true ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Float Arg0 \n " ) ;
if ( prevDest )
{
// This is a modifying instruction so we need to store the result afterward
AllocInst ( BeMCInstKind_Mov , prevDest , inst - > mArg0 , instIdx + 1 ) ;
IntroduceVRegs ( inst - > mArg0 , mcBlock , instIdx , instIdx + 2 ) ;
instIdx + + ; // Skip this instruction until next pass
}
else
{
IntroduceVRegs ( inst - > mArg0 , mcBlock , instIdx , instIdx + 1 ) ;
}
continue ;
}
}
}
if ( ( ! inst - > IsMov ( ) ) & & ( arg0 . IsVReg ( ) ) & & ( arg0Type - > IsFloat ( ) ) )
{
BF_ASSERT ( ! inst - > mResult ) ;
// XMM instructions (besides MOVs) require native register destinations
if ( ( inst - > IsCommutable ( ) ) & & ( arg1 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsVReg ( ) ) )
{
auto nextInst = mcBlock - > mInstructions [ instIdx + 1 ] ;
int underlyingVRegIdx = GetUnderlyingVReg ( inst - > mArg1 . mVRegIdx ) ;
if ( ! mLivenessContext . IsSet ( nextInst - > mLiveness , underlyingVRegIdx ) )
{
// Convert a "Mul %vreg0, %vreg1<reg>"
// To
// Mul %reg1<reg>, %vreg0
// Mov %vreg0, %reg1<reg>
// This only works if %reg1 dies after this instruction and this is
// cheaper than the more general case below
AllocInst ( BeMCInstKind_Mov , inst - > mArg0 , inst - > mArg1 , instIdx + + + 1 ) ;
BF_SWAP ( inst - > mArg0 , inst - > mArg1 ) ;
BF_SWAP ( arg0 , arg1 ) ;
continue ;
}
}
// Convert from "<op> %reg0, %reg1"
// To
// Mov %vreg2<reg>, %vreg0
// <op> %vreg2<reg>, %vreg1
// Mov %vreg0, %vreg2<reg>
auto prevDest = inst - > mArg0 ;
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , true ) ;
AllocInst ( BeMCInstKind_Mov , prevDest , inst - > mArg0 , instIdx + 1 ) ;
IntroduceVRegs ( inst - > mArg0 , mcBlock , instIdx , instIdx + 2 ) ;
instIdx + + ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Float reg dest \n " ) ;
continue ;
}
}
if ( inst - > mKind = = BeMCInstKind_Call )
{
// Convert from
// Mov %reg0, <x>
// ..
// Call %reg0
// To "Call <x>"
// This is a common case for virtual dispatch where complex address expressions get actualized
// but then we end up with an 'extra' vreg
if ( inst - > mArg0 . IsVReg ( ) )
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo - > mRefCount = = 1 )
{
for ( int checkInstIdx = instIdx - 1 ; checkInstIdx > = 0 ; checkInstIdx - - )
{
auto checkInst = mcBlock - > mInstructions [ checkInstIdx ] ;
if ( ( checkInst - > mKind = = BeMCInstKind_Mov ) & & ( checkInst - > mArg0 = = inst - > mArg0 ) )
{
// We can't extend down any ForceRegs, those must be confined to very short intervals
if ( ! HasForceRegs ( checkInst - > mArg1 ) )
{
inst - > mArg0 = checkInst - > mArg1 ;
RemoveInst ( mcBlock , checkInstIdx ) ;
instIdx - - ;
continue ;
}
}
}
}
}
}
switch ( inst - > mKind )
{
case BeMCInstKind_PreserveVolatiles :
regPreserveDepth + + ;
break ;
case BeMCInstKind_RestoreVolatiles :
regPreserveDepth - - ;
break ;
case BeMCInstKind_DefLoad :
{
Fail ( " DefLoad- DoLoads should have removed these " ) ;
}
break ;
case BeMCInstKind_Def :
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
// Legalize vreg exprs
if ( ( vregInfo - > mRefCount > 0 ) & & ( vregInfo - > mRelTo ) & & ( ! vregInfo - > IsDirectRelToAny ( ) ) )
{
if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_SymbolAddr )
continue ; // Just leave it
if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_Symbol )
{
continue ;
}
if ( vregInfo - > IsDirectRelTo ( ) )
{
if ( vregInfo - > mRelTo . IsVRegAny ( ) )
{
// Propagate change to the directRel
//auto itr = vregExprChangeSet.find(vregInfo->mRelTo.mVRegIdx);
//if (itr != vregExprChangeSet.end())
if ( vregExprChangeSet . Contains ( vregInfo - > mRelTo . mVRegIdx ) )
{
BF_ASSERT ( ! isFinalRun ) ;
vregExprChangeSet . Add ( inst - > mArg0 . mVRegIdx ) ;
isFinalRun = false ;
}
}
}
else
{
if ( vregInfo - > mRelOffset )
{
auto relOfsType = GetType ( vregInfo - > mRelOffset ) ;
// We can only do offsets by int64s
if ( relOfsType - > mTypeCode ! = BeTypeCode_Int64 )
{
bool didExtend = false ;
if ( vregInfo - > mRelOffset . IsVReg ( ) )
{
// First we try to resize the original vreg, which we are allowed to do
// if there's only one mov and the vreg dies by the end of the block
int relVRegIdx = vregInfo - > mRelOffset . mVRegIdx ;
bool foundDef = false ;
BeMCInst * setInst = NULL ;
for ( int checkIdx = 0 ; checkIdx < ( int ) mcBlock - > mInstructions . size ( ) ; checkIdx + + )
{
auto checkInst = mcBlock - > mInstructions [ checkIdx ] ;
if ( ( checkInst - > mKind = = BeMCInstKind_Def ) & & ( checkInst - > mArg0 . mVRegIdx = = relVRegIdx ) )
foundDef = true ;
if ( foundDef )
{
if ( ( checkInst - > mKind = = BeMCInstKind_Mov ) & & ( checkInst - > mArg0 = = vregInfo - > mRelOffset ) )
{
if ( setInst ! = NULL )
break ; // Only one set is allowed
setInst = checkInst ;
}
else
{
// If we also use this reg for a non-offset reason then we can't just extend the size
bool hasNonOffsetRef = false ;
auto operands = { & checkInst - > mResult , & checkInst - > mArg0 , & checkInst - > mArg1 } ;
for ( auto operand : operands )
if ( ContainsNonOffsetRef ( * operand , vregInfo - > mRelOffset ) )
hasNonOffsetRef = true ;
if ( hasNonOffsetRef )
{
break ;
}
}
if ( ! mLivenessContext . IsSet ( checkInst - > mLiveness , relVRegIdx ) )
{
if ( setInst ! = NULL )
{
didExtend = true ;
auto relVRegInfo = mVRegInfo [ relVRegIdx ] ;
setInst - > mKind = BeMCInstKind_MovSX ;
relVRegInfo - > mType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
2019-11-26 13:11:17 -08:00
relVRegInfo - > mAlign = relVRegInfo - > mType - > mAlign ;
2019-08-23 11:56:54 -07:00
if ( debugging )
OutputDebugStrF ( " Def MovSX \n " ) ;
isFinalRun = false ;
}
break ;
}
}
}
}
if ( ! didExtend )
{
auto relOfs64 = AllocVirtualReg ( mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) , 2 ) ;
CreateDefineVReg ( relOfs64 , instIdx ) ;
AllocInst ( BeMCInstKind_MovSX , relOfs64 , vregInfo - > mRelOffset , instIdx + 1 ) ;
IntroduceVRegs ( inst - > mArg0 , mcBlock , instIdx , instIdx + 2 ) ;
instIdx + = 2 ;
vregInfo - > mRelOffset = relOfs64 ;
vregExprChangeSet . Add ( relOfs64 . mVRegIdx ) ;
if ( debugging )
OutputDebugStrF ( " Def MovSX 2 \n " ) ;
isFinalRun = false ;
}
}
}
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
int errorVRegIdx = - 1 ;
bool isValid = GetRMParams ( inst - > mArg0 , regA , regB , bScale , disp , & errorVRegIdx ) ! = BeMCRMMode_Invalid ;
if ( ! isValid )
{
if ( errorVRegIdx = = - 1 )
{
BF_ASSERT ( ! vregInfo - > mRelOffset ) ;
if ( vregInfo - > mType - > IsPointer ( ) )
{
// This must be a cast like "(void*)gGlobalVar". We need to actualize the symbol address
// in a native register now.
vregExprChangeSet . Add ( inst - > mArg0 . mVRegIdx ) ;
AllocInst ( BeMCInstKind_Mov , inst - > mArg0 , vregInfo - > mRelTo , instIdx + + + 1 ) ;
vregInfo - > mIsExpr = false ;
vregInfo - > mRelTo = BeMCOperand ( ) ;
vregInfo - > mForceReg = true ;
CheckForce ( vregInfo ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Symbol Addr \n " ) ;
break ;
}
else
{
// This could be a special case like
// MOV (int64)floatVal, reg64
// For two-step loading of immediates into memory without using .rdata
continue ;
}
}
else if ( errorVRegIdx = = - 2 )
{
vregExprChangeSet . Add ( inst - > mArg0 . mVRegIdx ) ;
MarkInvalidRMRegs ( inst - > mArg0 ) ;
if ( debugging )
OutputDebugStrF ( " GetRMParams invalid indices \n " ) ;
isFinalRun = false ;
break ;
}
//if (vregExprChangeSet.find(errorVRegIdx) != vregExprChangeSet.end())
if ( vregExprChangeSet . Contains ( errorVRegIdx ) )
{
// This means we have already modified some dependent vregs, so we may be legalized already.
// Wait till next iteration to determine that.
BF_ASSERT ( ! isFinalRun ) ;
isValid = true ; //
}
}
// The only valid form is [<reg>*<1/2/4/8>+<imm>]
// If we violate that then we have to break it up
//if ((vregInfo->mRelOffsetScale != 1) && (vregInfo->mRelOffsetScale != 2) && (vregInfo->mRelOffsetScale != 4) && (vregInfo->mRelOffsetScale != 8))
if ( ( ! isValid ) & & ( errorVRegIdx = = inst - > mArg0 . mVRegIdx ) )
{
vregExprChangeSet . Add ( errorVRegIdx ) ;
if ( ( vregInfo - > mRelOffsetScale ! = 1 ) & & ( vregInfo - > mRelOffsetScale ! = 2 ) & & ( vregInfo - > mRelOffsetScale ! = 4 ) & & ( vregInfo - > mRelOffsetScale ! = 8 ) )
{
auto relOffsetType = GetType ( vregInfo - > mRelOffset ) ;
BeMCOperand scratchReg = AllocVirtualReg ( relOffsetType , 2 , true ) ;
CreateDefineVReg ( scratchReg , instIdx + + ) ;
auto newInst = AllocInst ( BeMCInstKind_IMul , vregInfo - > mRelOffset , BeMCOperand : : FromImmediate ( vregInfo - > mRelOffsetScale ) , instIdx + + ) ;
newInst - > mResult = scratchReg ;
vregInfo - > mRelOffset = scratchReg ;
vregInfo - > mRelOffsetScale = 1 ;
if ( debugging )
OutputDebugStrF ( " Invalid RM combo \n " ) ;
vregExprChangeSet . Add ( scratchReg . mVRegIdx ) ;
}
else if ( ! vregInfo - > mRelTo . IsVRegAny ( ) )
{
vregInfo - > mWantsExprActualize = true ;
hasPendingActualizations = true ;
}
isFinalRun = false ;
}
else if ( ! isValid )
{
auto errorVRegInfo = mVRegInfo [ errorVRegIdx ] ;
if ( ( errorVRegInfo - > mIsExpr ) & & ( ! errorVRegInfo - > IsDirectRelTo ( ) ) )
{
errorVRegInfo - > mWantsExprActualize = true ;
hasPendingActualizations = true ;
vregExprChangeSet . Add ( errorVRegIdx ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " RM not valid, actualize \n " ) ;
}
else
{
// We don't want to have too many concurrent ForceReg vregs at once, since that causes too much register pressure and
// can cause register allocation to fail at the extreme end. The scratchReg adds another ForceReg for the lifetime
// of the def vreg, so if the def vreg doesn't immediately die and there are already too many ForceRegs active then
// we need to actualize ourselves
bool actualizeSelf = false ;
if ( instIdx < mcBlock - > mInstructions . size ( ) - 2 )
{
auto checkInst = mcBlock - > mInstructions [ instIdx + 2 ] ;
if ( mLivenessContext . IsSet ( checkInst - > mLiveness , inst - > mArg0 . mVRegIdx ) )
{
actualizeSelf = true ;
}
}
else
{
actualizeSelf = true ;
}
if ( actualizeSelf )
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
// DirectRel cannot actualize
if ( vregInfo - > IsDirectRelTo ( ) )
actualizeSelf = false ;
}
if ( actualizeSelf )
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
vregInfo - > mWantsExprActualize = true ;
hasPendingActualizations = true ;
}
else
{
// This may be a local variable that failed to be assigned to a reg, create a scratch local with a forced reg
auto scratchReg = AllocVirtualReg ( errorVRegInfo - > mType , 2 , false ) ;
auto scratchVRegInfo = mVRegInfo [ scratchReg . mVRegIdx ] ;
auto errorVReg = BeMCOperand : : FromVReg ( errorVRegIdx ) ;
if ( ( vregInfo - > mRelTo = = errorVReg ) | | ( vregInfo - > mRelOffset = = errorVReg ) )
{
CreateDefineVReg ( scratchReg , instIdx + + ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , errorVReg , instIdx + + ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " RM failed, scratch vreg \n " ) ;
vregExprChangeSet . Add ( scratchReg . mVRegIdx ) ;
if ( vregInfo - > mRelTo = = errorVReg )
{
scratchVRegInfo - > mForceReg = true ;
CheckForce ( scratchVRegInfo ) ;
vregInfo - > mRelTo . mVRegIdx = scratchReg . mVRegIdx ;
}
else if ( vregInfo - > mRelOffset = = errorVReg )
{
scratchVRegInfo - > mForceReg = true ;
CheckForce ( scratchVRegInfo ) ;
vregInfo - > mRelOffset . mVRegIdx = scratchReg . mVRegIdx ;
}
}
else
{
// This should be impossible - a previous def for an inner expr should have caught this case
if ( vregExprChangeSet . IsEmpty ( ) )
{
Fail ( " Error " ) ;
}
}
}
}
}
}
}
if ( ( vregInfo - > mDbgVariable ! = NULL ) & & ( vregInfo - > mDbgVariable - > mPendingInitType ! = BfIRInitType_NotNeeded ) )
pendingInitKind = vregInfo - > mDbgVariable - > mPendingInitType ;
}
break ;
case BeMCInstKind_DbgDecl :
{
bool isInvalid = false ;
int vregIdx = inst - > mArg0 . mVRegIdx ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
auto dbgVar = vregInfo - > mDbgVariable ;
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int scale = 1 ;
int disp = 0 ;
BeMCRMMode mode ;
if ( dbgVar - > mIsValue )
mode = GetRMParams ( inst - > mArg0 , regA , regB , scale , disp ) ;
else
{
if ( inst - > mArg0 . mKind = = BeMCOperandKind_VRegAddr )
mode = GetRMParams ( BeMCOperand : : ToLoad ( inst - > mArg0 ) , regA , regB , scale , disp ) ;
else
{
mode = GetRMParams ( inst - > mArg0 , regA , regB , scale , disp ) ;
if ( ( mode ! = BeMCRMMode_Direct ) & & ( scale ! = 1 ) & & ( disp ! = 0 ) )
isInvalid = true ;
}
}
if ( mode = = BeMCRMMode_Invalid )
{
if ( vregInfo - > mType - > mSize ! = 0 )
{
isInvalid = true ;
}
}
else if ( mode = = BeMCRMMode_Direct )
{
if ( ( regB ! = X64Reg_None ) | | ( disp ! = 0 ) | | ( scale ! = 1 ) )
{
isInvalid = true ;
}
}
else if ( mode = = BeMCRMMode_Deref )
{
if ( ( regB ! = X64Reg_None ) | | ( scale ! = 1 ) )
{
isInvalid = true ;
}
}
if ( isInvalid )
{
isFinalRun = false ;
/*ReplaceWithNewVReg(inst->mArg0, instIdx, true, false);
// The debug value is not representable, so copy it. This can only occur within mixins where we are pointing
// to composed values
BF_ASSERT ( inst - > mArg0 . mKind = = BeMCOperandKind_VReg ) ;
inst - > mArg0 . mKind = BeMCOperandKind_VRegAddr ;
dbgVar - > mIsValue = false ;
auto newVRegInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
vregInfo - > mDbgVariable = NULL ;
newVRegInfo - > mDbgVariable = dbgVar ; */
// The debug value is not representable
inst - > mArg0 . mKind = BeMCOperandKind_VReg ;
vregInfo - > mWantsExprActualize = true ;
hasPendingActualizations = true ;
vregExprChangeSet . Add ( inst - > mArg0 . mVRegIdx ) ;
isFinalRun = false ;
break ;
}
if ( ! mVRegInitializedContext . IsSet ( inst - > mVRegsInitialized , vregIdx ) )
{
if ( ( dbgVar - > mPendingInitType ! = BfIRInitType_NotNeeded ) & & ( dbgVar - > mPendingInitType ! = BfIRInitType_NotNeeded_AliveOnDecl ) )
{
Fail ( " Shouldn't happen here anymore " ) ;
/*pendingInitKind = dbgVar->mPendingInitType;
// We don't need dynLife anymore since we're explicitly writing this
vregInfo - > mHasDynLife = false ;
AllocInst ( BeMCInstKind_Def , inst - > mArg0 , instIdx ) ;
// We don't definitively need this, it's just to make sure this doesn't cause a liveness error
isFinalRun = false ; */
}
}
}
break ;
case BeMCInstKind_MemCpy :
{
if ( inst - > mArg1 )
{
BF_ASSERT ( inst - > mArg1 . mKind = = BeMCOperandKind_VRegPair ) ;
int * vregIndices [ ] = { & inst - > mArg1 . mVRegPair . mVRegIdx0 , & inst - > mArg1 . mVRegPair . mVRegIdx1 } ;
for ( int * argIdxPtr : vregIndices )
{
auto mcArg = BeMCOperand : : FromEncoded ( * argIdxPtr ) ;
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
auto form = GetRMParams ( mcArg , regA , regB , bScale , disp ) ;
if ( ( form ! = BeMCRMMode_Direct ) | | ( regB ! = X64Reg_None ) | | ( regA = = X64Reg_R11 ) )
{
BeMCOperand scratchReg = AllocVirtualReg ( GetType ( mcArg ) , 2 , true ) ;
auto vregInfo = GetVRegInfo ( scratchReg ) ;
vregInfo - > mDisableR11 = true ;
int insertPos = FindSafeInstInsertPos ( instIdx ) ;
CreateDefineVReg ( scratchReg , insertPos ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , mcArg , insertPos + 1 ) ;
* argIdxPtr = scratchReg . mVRegIdx ;
instIdx + = 2 ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " MemCpy \n " ) ;
}
}
}
}
break ;
case BeMCInstKind_MemSet :
{
if ( inst - > mArg1 )
{
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
auto form = GetRMParams ( inst - > mArg1 , regA , regB , bScale , disp ) ;
if ( ( form ! = BeMCRMMode_Direct ) | | ( regB ! = X64Reg_None ) | | ( regA = = X64Reg_R11 ) | | ( disp ! = 0 ) )
{
BeMCOperand scratchReg = AllocVirtualReg ( GetType ( inst - > mArg1 ) , 2 , true ) ;
auto vregInfo = GetVRegInfo ( scratchReg ) ;
vregInfo - > mDisableR11 = true ;
int insertPos = FindSafeInstInsertPos ( instIdx ) ;
CreateDefineVReg ( scratchReg , insertPos ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , inst - > mArg1 , insertPos + 1 ) ;
inst - > mArg1 = scratchReg ;
instIdx + = 2 ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " MemSet \n " ) ;
}
}
}
break ;
case BeMCInstKind_Neg :
{
if ( arg0Type - > IsFloat ( ) )
{
inst - > mKind = BeMCInstKind_Xor ;
if ( arg0Type - > mTypeCode = = BeTypeCode_Float )
inst - > mArg1 . mKind = BeMCOperandKind_Immediate_f32_Packed128 ;
else
inst - > mArg1 . mKind = BeMCOperandKind_Immediate_f64_Packed128 ;
inst - > mArg1 . mImmFloat = - 0.0 ;
}
}
break ;
case BeMCInstKind_Div :
case BeMCInstKind_IDiv :
case BeMCInstKind_Rem :
case BeMCInstKind_IRem :
{
// Unsigned div and rem can be implemented with bitwise operations
// Negative values cannot (at least following C standards, because of rounding issues)
if ( ( inst - > mKind = = BeMCInstKind_Div ) | | ( inst - > mKind = = BeMCInstKind_Rem ) )
{
if ( ( ! inst - > mResult ) & & ( inst - > mArg1 . IsImmediateInt ( ) ) & & ( IsPowerOfTwo ( inst - > mArg1 . mImmediate ) ) )
{
if ( inst - > mKind = = BeMCInstKind_Div )
{
int64 divVal = inst - > mArg1 . mImmediate ;
int shiftCount = 0 ;
while ( divVal > 1 )
{
shiftCount + + ;
divVal > > = 1 ;
}
inst - > mKind = BeMCInstKind_Shr ;
inst - > mArg1 = BeMCOperand : : FromImmediate ( shiftCount ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Div SHR \n " ) ;
break ;
}
else if ( inst - > mKind = = BeMCInstKind_Rem )
{
inst - > mKind = BeMCInstKind_And ;
inst - > mArg1 = BeMCOperand : : FromImmediate ( inst - > mArg1 . mImmediate - 1 ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Div REM \n " ) ;
break ;
}
}
}
if ( arg1 . IsImmediate ( ) )
{
// Oops, must be 'rm'
ReplaceWithNewVReg ( inst - > mArg1 , instIdx , true , false ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Div/Rem not RM \n " ) ;
}
auto arg0Type = GetType ( arg0 ) ;
if ( arg0Type - > IsInt ( ) )
{
// DIV/IDIV can only operate on the RDX:RAX pair, except for i8 divide which just uses AX
bool isRegADividend = ( arg0 . mReg = = X64Reg_RAX ) | | ( arg0 . mReg = = X64Reg_EAX ) | | ( arg0 . mReg = = X64Reg_AX ) | | ( arg0 . mReg = = X64Reg_AL ) ;
if ( ( ! arg0 . IsNativeReg ( ) ) | | ( ! isRegADividend ) | |
( inst - > mKind = = BeMCInstKind_Rem ) | | ( inst - > mKind = = BeMCInstKind_IRem ) )
{
bool preserveRDX = ( arg0Type - > mSize ! = 1 ) & & ( regPreserveDepth = = 0 ) ;
auto mcScratch = BeMCOperand : : FromReg ( ResizeRegister ( X64Reg_RAX , arg0Type - > mSize ) ) ;
BF_ASSERT ( ! inst - > mResult ) ;
BeMCInst * preserveRAXInst = AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , instIdx + + ) ;
BeMCInst * preserveRDXInst = NULL ;
if ( preserveRDX )
preserveRDXInst = AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RDX ) , instIdx + + ) ;
AllocInst ( BeMCInstKind_Mov , mcScratch , inst - > mArg0 , instIdx + + ) ;
if ( ( inst - > mKind = = BeMCInstKind_Rem ) | | ( inst - > mKind = = BeMCInstKind_IRem ) )
{
if ( inst - > mKind = = BeMCInstKind_Rem )
inst - > mKind = BeMCInstKind_Div ;
else
inst - > mKind = BeMCInstKind_IDiv ;
BeMCOperand mcRemaindier ;
if ( arg0Type - > mSize = = 1 )
{
mcRemaindier = BeMCOperand : : FromReg ( ResizeRegister ( X64Reg_AH , arg0Type - > mSize ) ) ;
preserveRAXInst - > mArg1 = inst - > mArg0 ; // RAX preserve elision exception
DisableRegister ( inst - > mArg0 , X64Reg_SIL ) ; // Disable Hi8
}
else
{
mcRemaindier = BeMCOperand : : FromReg ( ResizeRegister ( X64Reg_RDX , arg0Type - > mSize ) ) ;
preserveRDXInst - > mArg1 = inst - > mArg0 ; // RDX preserve elision exception
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo ! = NULL )
{
// This is to avoid overlap with PreserveRAX
DisableRegister ( inst - > mArg0 , X64Reg_RAX ) ;
}
}
AllocInst ( BeMCInstKind_Mov , inst - > mArg0 , mcRemaindier , instIdx + + + 1 ) ;
}
else
{
preserveRAXInst - > mArg1 = inst - > mArg0 ; // RAX preserve elision exception
AllocInst ( BeMCInstKind_Mov , inst - > mArg0 , mcScratch , instIdx + + + 1 ) ;
}
inst - > mArg0 = mcScratch ;
if ( preserveRDX )
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RDX ) , instIdx + + + 1 ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , instIdx + + + 1 ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Div/Rem Setup \n " ) ;
}
else
{
if ( regPreserveDepth = = 0 )
{
if ( arg0Type - > mSize ! = 1 )
{
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RDX ) , instIdx + + ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RDX ) , instIdx + + + 1 ) ;
isFinalRun = false ; // Reassign regs
if ( debugging )
OutputDebugStrF ( " Div/Rem Preserve \n " ) ;
}
}
}
// We can't allow division by RDX because we need RAX:RDX for the dividend
auto divisorReg = ResizeRegister ( arg1 . mReg , 8 ) ;
if ( ( arg1 . IsNativeReg ( ) ) & &
( ( divisorReg = = X64Reg_RDX ) | | ( divisorReg = = X64Reg_RAX ) ) )
{
BF_ASSERT ( inst - > mArg1 . IsVRegAny ( ) ) ;
int vregIdx = GetUnderlyingVReg ( inst - > mArg1 . mVRegIdx ) ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo ! = NULL )
{
vregInfo - > mDisableRAX = true ;
vregInfo - > mDisableRDX = true ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Div/Rem invalid reg \n " ) ;
}
}
}
else if ( inst - > mKind = = BeMCInstKind_IRem )
{
SetAndRestoreValue < int * > insertPtr ( mInsertInstIdxRef , & instIdx ) ;
SizedArray < BeMCOperand , 3 > args = { inst - > mArg0 , inst - > mArg1 } ;
auto mcFunc = BeMCOperand : : FromSymbolAddr ( mCOFFObject - > GetSymbolRef ( ( arg0Type - > mTypeCode = = BeTypeCode_Double ) ? " fmod " : " fmodf " ) - > mIdx ) ;
auto fmodVal = CreateCall ( mcFunc , args , arg0Type ) ;
inst - > mKind = BeMCInstKind_Mov ;
inst - > mResult = BeMCOperand ( ) ;
inst - > mArg1 = fmodVal ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " FMod \n " ) ;
}
}
break ;
case BeMCInstKind_Mul :
case BeMCInstKind_IMul :
{
if ( arg0Type - > mSize = = 1 )
{
if ( ( ! arg0 . IsNativeReg ( ) ) | | ( arg0 . mReg ! = X64Reg_AL ) | | ( inst - > mResult ) )
{
BF_ASSERT ( inst - > mResult ) ;
// Int8 multiplies can only be done on AL
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , instIdx + + ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_AL ) , inst - > mArg0 , instIdx + + ) ;
AllocInst ( BeMCInstKind_Mov , inst - > mResult , BeMCOperand : : FromReg ( X64Reg_AL ) , instIdx + + + 1 ) ;
inst - > mArg0 = BeMCOperand : : FromReg ( X64Reg_AL ) ;
inst - > mResult = BeMCOperand ( ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , instIdx + + + 1 ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Mul Size 1 \n " ) ;
break ;
}
if ( inst - > mArg1 . IsImmediateInt ( ) )
{
ReplaceWithNewVReg ( inst - > mArg1 , instIdx , true , false ) ;
}
BF_ASSERT ( ! inst - > mResult ) ;
}
else
{
if ( inst - > mResult )
{
// The 3-op form of MUL must be in "reg, r/m64, imm" form
if ( ! inst - > mArg1 . IsImmediateInt ( ) )
{
Fail ( " Not supported " ) ;
//isFinalRun = false;
break ;
}
}
// Convert from %vreg0 = mul %vreg1, %vreg2
// To: %scratch = mul %vreg1, %vreg2
// mov %reg0, %scratch
if ( inst - > mResult )
{
bool isLegal = true ;
int64 immediateInt = inst - > mArg1 . GetImmediateInt ( ) ;
// if (inst->mKind == BeMCInstKind_Mul)
// {
// if (immediateInt > 0xFFFFFFFFLL)
// isLegal = false;
// }
// else
// {
// if ((immediateInt < -0x80000000LL) || (immediateInt > 0x7FFFFFFF))
// isLegal = false;
// }
if ( ! GetFixedOperand ( inst - > mResult ) . IsNativeReg ( ) )
{
BeMCOperand scratchReg = AllocVirtualReg ( GetType ( inst - > mResult ) , 2 , true ) ;
CreateDefineVReg ( scratchReg , instIdx ) ;
AllocInst ( BeMCInstKind_Mov , inst - > mResult , scratchReg , instIdx + 2 ) ;
inst - > mResult = scratchReg ;
if ( debugging )
OutputDebugStrF ( " Mul 3-form not reg \n " ) ;
isFinalRun = false ;
instIdx + = 2 ;
break ;
}
}
else
{
if ( ! GetFixedOperand ( inst - > mArg0 ) . IsNativeReg ( ) )
{
auto prevArg0 = inst - > mArg0 ;
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , true ) ;
AllocInst ( BeMCInstKind_Mov , prevArg0 , inst - > mArg0 , instIdx + 1 ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Mul not reg \n " ) ;
instIdx + + ;
break ;
}
}
}
}
// Fallthrough
case BeMCInstKind_Add :
case BeMCInstKind_Sub :
{
}
break ;
case BeMCInstKind_Shl :
case BeMCInstKind_Shr :
case BeMCInstKind_Sar :
{
if ( ( ! inst - > mArg1 . IsNativeReg ( ) ) & & ( ! inst - > mArg1 . IsImmediateInt ( ) ) )
{
auto mcShift = inst - > mArg1 ;
auto shiftType = GetType ( mcShift ) ;
BF_ASSERT ( shiftType - > IsInt ( ) ) ;
if ( shiftType - > mSize ! = 1 )
{
auto mcShift8 = AllocVirtualReg ( mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int8 ) , 2 ) ;
CreateDefineVReg ( mcShift8 , instIdx + + ) ;
auto vregInfo = GetVRegInfo ( mcShift8 ) ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = mcShift ;
mcShift = mcShift8 ;
}
// The only non-constant shift is by 'CL'
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RCX ) , instIdx + + ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_CL ) , mcShift , instIdx + + ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RCX ) , instIdx + + + 1 ) ;
inst - > mArg1 = BeMCOperand : : FromReg ( X64Reg_RCX ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Shift by CL \n " ) ;
continue ;
}
if ( inst - > mArg1 . IsNativeReg ( ) )
{
BF_ASSERT ( inst - > mArg1 . mReg = = X64Reg_RCX ) ;
X64CPURegister regs [ 2 ] = { X64Reg_None , X64Reg_None } ;
GetUsedRegs ( inst - > mArg0 , regs [ 0 ] , regs [ 1 ] ) ;
if ( ( regs [ 0 ] = = X64Reg_RCX ) | | ( regs [ 1 ] = = X64Reg_RCX ) )
{
// Bad reg!
auto mcTarget = inst - > mArg0 ;
int insertIdx = instIdx - 1 ;
ReplaceWithNewVReg ( inst - > mArg0 , insertIdx , true ) ;
BF_ASSERT ( mcBlock - > mInstructions [ instIdx + 3 ] - > mKind = = BeMCInstKind_RestoreVolatiles ) ;
AllocInst ( BeMCInstKind_Mov , mcTarget , inst - > mArg0 , instIdx + 4 ) ;
}
}
}
break ;
case BeMCInstKind_Cmp :
{
bool needSwap = false ;
// Cmp <imm>, <r/m> is not legal, so we need to swap LHS/RHS, which means also modifying the instruction that uses the result of the cmp
if ( inst - > mArg0 . IsImmediate ( ) )
needSwap = true ;
if ( arg0Type - > IsFloat ( ) )
{
// Cmp <r/m>, <xmm> is not valid, only Cmp <xmm>, <r/m>
if ( ( ! arg0 . IsNativeReg ( ) ) & & ( arg1 . IsNativeReg ( ) ) )
{
needSwap = true ;
}
else
{
if ( ! arg0 . IsNativeReg ( ) )
{
Fail ( " xmm reg required " ) ;
}
}
}
if ( needSwap )
{
bool hasConstResult = false ;
int constResult = 0 ;
if ( inst - > mArg1 . IsImmediate ( ) )
{
//constResult = inst->mArg0.mImmediate - inst->mArg1.mImmediate;
// Should have been handled in the frontend
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , true ) ;
break ;
}
int checkInstIdx = instIdx + 1 ;
auto checkBlock = mcBlock ;
for ( ; checkInstIdx < ( int ) checkBlock - > mInstructions . size ( ) ; checkInstIdx + + )
{
auto checkInst = checkBlock - > mInstructions [ checkInstIdx ] ;
if ( ( checkInst - > mKind = = BeMCInstKind_CondBr ) | | ( checkInst - > mKind = = BeMCInstKind_CmpToBool ) )
{
if ( constResult )
{
NotImpl ( ) ;
//mcBlock->RemoveInst(instIdx);
//instIdx--;
}
else
{
BeMCOperand & cmpArg = ( checkInst - > mKind = = BeMCInstKind_CondBr ) ? checkInst - > mArg1 : checkInst - > mArg0 ;
BF_SWAP ( inst - > mArg0 , inst - > mArg1 ) ;
BF_ASSERT ( cmpArg . mKind = = BeMCOperandKind_CmpKind ) ;
cmpArg . mCmpKind = BeModule : : SwapCmpSides ( cmpArg . mCmpKind ) ;
}
break ;
}
else if ( checkInst - > mKind = = BeMCInstKind_Br )
{
// Sometimes the matching use is in another block
FindTarget ( checkInst - > mArg0 , checkBlock , checkInstIdx ) ;
}
else if ( ( checkInst - > mKind ! = BeMCInstKind_Def ) & & ( checkInst - > mKind ! = BeMCInstKind_DbgDecl ) & &
( checkInst - > mKind ! = BeMCInstKind_ValueScopeSoftEnd ) & & ( checkInst - > mKind ! = BeMCInstKind_ValueScopeHardEnd ) )
{
Fail ( " Malformed " ) ;
}
}
break ;
}
}
break ;
case BeMCInstKind_CmpToBool :
{
if ( inst - > mResult . IsVRegAny ( ) )
{
if ( ! mVRegInitializedContext . IsSet ( mCurVRegsInit , inst - > mResult . mVRegIdx ) )
{
SizedArray < int , 1 > addVec = { inst - > mResult . mVRegIdx } ;
mCurVRegsInit = mVRegInitializedContext . Add ( mCurVRegsInit , addVec , true ) ;
BF_ASSERT ( instIdx < ( int ) mcBlock - > mInstructions . size ( ) - 1 ) ;
if ( instIdx < ( int ) mcBlock - > mInstructions . size ( ) - 1 )
{
auto nextInst = mcBlock - > mInstructions [ instIdx + 1 ] ;
if ( nextInst - > mVRegsInitialized ! = NULL )
{
auto fixedVRegsInit = mVRegInitializedContext . RemoveChange ( nextInst - > mVRegsInitialized , inst - > mResult . mVRegIdx ) ;
ReplaceVRegsInit ( mcBlock , instIdx + 1 , nextInst - > mVRegsInitialized , fixedVRegsInit ) ;
}
}
}
}
// Do this with a CMOV instead?
auto result = inst - > mResult ;
inst - > mResult . mKind = BeMCOperandKind_None ;
inst - > mKind = BeMCInstKind_CondBr ;
inst - > mArg1 = inst - > mArg0 ; // Move cmpType
inst - > mArg0 = BeMCOperand : : FromLabel ( mCurLabelIdx ) ;
int insertInstIdx = instIdx + 1 ;
//auto mcInst = AllocInst(BeMCInstKind_CondBr, BeMCOperand::FromLabel(mCurLabelIdx), BeMCOperand::FromCmpKind(BeCmpKind_EQ), insertInstIdx++);
auto mcInst = AllocInst ( BeMCInstKind_Mov , result , BeMCOperand : : FromImmediate ( 0 ) , insertInstIdx + + ) ;
mcInst = AllocInst ( BeMCInstKind_Br , BeMCOperand : : FromLabel ( mCurLabelIdx + 1 ) , insertInstIdx + + ) ;
CreateLabel ( insertInstIdx + + ) ;
mcInst = AllocInst ( BeMCInstKind_Mov , result , BeMCOperand : : FromImmediate ( 1 ) , insertInstIdx + + ) ;
CreateLabel ( insertInstIdx + + ) ;
}
break ;
case BeMCInstKind_MovSX :
case BeMCInstKind_Mov :
{
bool isSignedExt = inst - > mKind = = BeMCInstKind_MovSX ;
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
if ( inst - > mArg1 . mKind = = BeMCOperandKind_VRegAddr )
{
auto vregInfo = mVRegInfo [ inst - > mArg1 . mVRegIdx ] ;
if ( ( ! vregInfo - > mIsExpr ) & & ( ! vregInfo - > mForceMem ) )
{
Fail ( " VRegAddr used without ForceMem " ) ;
}
}
if ( arg0Type - > mSize = = 0 )
{
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
if ( arg0Type - > IsComposite ( ) )
{
if ( arg1 . mKind = = BeMCOperandKind_Immediate_i64 )
{
// This is just a "zero initializer" marker
BF_ASSERT ( arg1 . mImmediate = = 0 ) ;
SetAndRestoreValue < int * > insertPtr ( mInsertInstIdxRef , & instIdx ) ;
RemoveInst ( mcBlock , instIdx ) ;
CreateMemSet ( BeMCOperand : : ToAddr ( arg0 ) , 0 , arg0Type - > mSize , arg0Type - > mAlign ) ;
instIdx - - ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Zero init \n " ) ;
break ;
}
else if ( arg1 . mKind = = BeMCOperandKind_ConstAgg )
{
bool needsSaveRegs = true ;
if ( instIdx > 0 )
{
auto prevInst = mcBlock - > mInstructions [ instIdx - 1 ] ;
if ( ( prevInst - > mKind = = BeMCInstKind_PreserveVolatiles ) /*&& (prevInst->mArg0.mReg == X64Reg_R11)*/ )
needsSaveRegs = false ;
}
auto argType = GetType ( arg1 ) ;
bool useRep = argType - > mSize > = BF_REP_MOV_LIMIT ;
int movInstIdx = instIdx ;
if ( needsSaveRegs )
{
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand ( ) , instIdx ) ;
instIdx + = 1 ;
if ( useRep )
{
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand ( ) , instIdx ) ;
instIdx + = 1 ;
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RDI ) , BeMCOperand ( ) , instIdx ) ;
instIdx + = 1 ;
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RCX ) , BeMCOperand ( ) , instIdx ) ;
instIdx + = 1 ;
}
movInstIdx = instIdx ;
if ( useRep )
{
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RCX ) , BeMCOperand ( ) , instIdx + 1 ) ;
instIdx + = 1 ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RDI ) , BeMCOperand ( ) , instIdx + 1 ) ;
instIdx + = 1 ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand ( ) , instIdx + 1 ) ;
instIdx + = 1 ;
}
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand ( ) , instIdx + 1 ) ;
instIdx + = 1 ;
isFinalRun = false ;
}
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
auto form = GetRMParams ( inst - > mArg0 , regA , regB , bScale , disp ) ;
if ( form = = BeMCRMMode_Invalid )
{
if ( inst - > mArg0 . IsSymbol ( ) )
{
// Just make it an addr so we can replace it with a temporary
inst - > mArg0 . mKind = BeMCOperandKind_SymbolAddr ;
}
ReplaceWithNewVReg ( inst - > mArg0 , movInstIdx , true , false ) ;
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
vregInfo - > mDisableR11 = true ;
instIdx + = 2 ;
isFinalRun = false ;
}
continue ;
}
// Struct = Struct
else //if (arg1.mKind == BeMCOperandKind_VReg)
{
auto arg0Addr = BeMCOperand : : ToAddr ( arg0 ) ;
auto arg1Addr = BeMCOperand : : ToAddr ( arg1 ) ;
SetAndRestoreValue < int * > insertPtr ( mInsertInstIdxRef , & instIdx ) ;
RemoveInst ( mcBlock , instIdx ) ;
CreateMemCpy ( arg0Addr , arg1Addr , BF_MIN ( arg0Type - > mSize , arg1Type - > mSize ) , BF_MIN ( arg0Type - > mAlign , arg1Type - > mAlign ) ) ;
instIdx - - ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Mov MemCpy \n " ) ;
break ;
}
}
if ( ( arg0Type - > IsFloat ( ) ) & & ( arg1Type - > IsInt ( ) ) )
{
if ( ( arg1Type - > mTypeCode = = BeTypeCode_Int64 ) & & ( ! isSignedExt ) )
{
//uint64->float
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand ( ) , instIdx ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_RAX ) , inst - > mArg1 , instIdx + 1 ) ;
inst - > mArg1 = BeMCOperand : : FromReg ( X64Reg_RAX ) ;
inst - > mKind = BeMCInstKind_MovSX ;
int labelIdx = CreateLabel ( instIdx + 3 ) ;
AllocInst ( BeMCInstKind_Test , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand : : FromReg ( X64Reg_RAX ) , instIdx + 3 ) ;
AllocInst ( BeMCInstKind_CondBr , BeMCOperand : : FromLabel ( labelIdx ) , BeMCOperand : : FromCmpKind ( BeCmpKind_SGE ) , instIdx + 4 ) ;
BeMCOperand mcOffset ;
if ( arg0Type - > mSize = = 8 )
mcOffset . mKind = BeMCOperandKind_Immediate_f64 ;
else
mcOffset . mKind = BeMCOperandKind_Immediate_f32 ;
mcOffset . mImmFloat = 1.8446744073709552e+19 ;
AllocInst ( BeMCInstKind_Add , inst - > mArg0 , mcOffset , instIdx + 5 ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand ( ) , instIdx + 7 ) ;
if ( debugging )
OutputDebugStrF ( " uint64->float \n " ) ;
isFinalRun = false ;
instIdx - - ;
continue ; // Rerun from PreserveVolatiles
}
else if ( ( ! isSignedExt ) | | ( arg1Type - > mSize < 4 ) )
{
// Int->Float conversions only work on 32 and 64-bit signed values, so we
// zero-extend into a larger scratch vreg and then we do the signed conversion on that.
// Convert from
// mov float, uint<bits>
// to
// mov scratch int<bits*2>, uint<bits>
// mov float, scratch int<bits*2>
if ( ( arg1 . IsNativeReg ( ) ) & & ( arg1Type - > mSize = = 4 ) )
{
// If we already have a 32-bit register, then we know that the upper 32-bits are zeroed
// so during emission we just use the 64-bit version of that register
}
else
{
auto mcScratch = AllocVirtualReg ( mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) , 2 , true ) ;
CreateDefineVReg ( mcScratch , instIdx + + ) ;
if ( ( isSignedExt ) & & ( arg1Type - > mSize < 8 ) )
AllocInst ( BeMCInstKind_MovSX , mcScratch , inst - > mArg1 , instIdx + + ) ;
else
AllocInst ( BeMCInstKind_Mov , mcScratch , inst - > mArg1 , instIdx + + ) ;
inst - > mKind = BeMCInstKind_MovSX ;
inst - > mArg1 = mcScratch ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " MovSX \n " ) ;
}
}
}
if ( ! arg0 . IsNativeReg ( ) )
{
// With f32s we can do a imm32 mov into memory, but there's no imm64 version of that
if ( ( arg0Type - > mTypeCode = = BeTypeCode_Double ) & & ( arg1 . IsImmediate ( ) ) )
{
// One option would be to do a "movsd xmm, .rdata" to load up the immediate...
// and that would leave arg0 with the possibility of binding to a register
// in a subsequent reg pass, but we don't do that.
//ReplaceWithNewVReg(inst->mArg1, instIdx, true);
// We do an int64 load/store, so arg0 must never be allowed to be a register
if ( arg0 . IsVReg ( ) )
{
auto vregInfo = mVRegInfo [ arg0 . mVRegIdx ] ;
vregInfo - > mForceMem = true ;
CheckForce ( vregInfo ) ;
}
BeMCOperand castedVReg = AllocVirtualReg ( mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) , 2 , false ) ;
CreateDefineVReg ( castedVReg , instIdx + + ) ;
auto castedVRegInfo = GetVRegInfo ( castedVReg ) ;
castedVRegInfo - > mIsExpr = true ;
castedVRegInfo - > mRelTo = inst - > mArg0 ;
BeMCOperand scratchReg = AllocVirtualReg ( mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) , 2 , true ) ;
CreateDefineVReg ( scratchReg , instIdx + + ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , BeMCOperand : : FromImmediate ( arg1 . mImmediate ) , instIdx + + ) ;
inst - > mArg0 = castedVReg ;
inst - > mArg1 = scratchReg ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Movsd \n " ) ;
}
// Conversion types only work in "r, rm" format
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
bool isCompat = arg0Type - > mSize = = arg1Type - > mSize ;
if ( ( arg0Type - > IsFloat ( ) ) ! = arg1Type - > IsFloat ( ) )
isCompat = false ;
if ( ! isCompat )
{
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , false ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Mov float rm \n " ) ;
}
}
}
break ;
case BeMCInstKind_XAdd :
{
for ( int oprIdx = 0 ; oprIdx < 2 ; oprIdx + + )
{
BeMCOperand & origOperand = ( oprIdx = = 0 ) ? inst - > mArg0 : inst - > mArg1 ;
if ( origOperand . IsVRegAny ( ) )
{
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
int errorVReg = - 1 ;
auto rmForm = GetRMParams ( origOperand , regA , regB , bScale , disp , & errorVReg ) ;
auto vregInfo = GetVRegInfo ( origOperand ) ;
bool isValid = true ;
if ( oprIdx = = 1 )
{
if ( rmForm ! = BeMCRMMode_Direct )
{
auto newVReg = ReplaceWithNewVReg ( origOperand , instIdx , true , true ) ;
auto newVRegInfo = GetVRegInfo ( newVReg ) ;
newVRegInfo - > mDisableRAX = true ;
isFinalRun = false ;
}
}
}
}
}
break ;
case BeMCInstKind_CmpXChg :
{
for ( int oprIdx = 0 ; oprIdx < 2 ; oprIdx + + )
{
BeMCOperand & origOperand = ( oprIdx = = 0 ) ? inst - > mArg0 : inst - > mArg1 ;
if ( origOperand . IsVRegAny ( ) )
{
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
int errorVReg = - 1 ;
auto rmForm = GetRMParams ( origOperand , regA , regB , bScale , disp , & errorVReg ) ;
auto vregInfo = GetVRegInfo ( origOperand ) ;
if ( ( ResizeRegister ( regA , 8 ) = = X64Reg_RAX ) | | ( ResizeRegister ( regB , 8 ) = = X64Reg_RAX ) )
{
if ( ! vregInfo - > mIsExpr )
{
isFinalRun = false ;
vregInfo - > mDisableRAX = true ;
continue ;
}
else
{
int safeIdx = FindSafeInstInsertPos ( instIdx , true ) ;
isFinalRun = false ;
auto origType = GetType ( origOperand ) ;
BeMCOperand scratchReg = AllocVirtualReg ( mModule - > mContext - > GetPointerTo ( origType ) , 2 , false ) ;
CreateDefineVReg ( scratchReg , safeIdx + + ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , BeMCOperand : : ToAddr ( origOperand ) , safeIdx + + ) ;
auto newVRegInfo = GetVRegInfo ( scratchReg ) ;
newVRegInfo - > mDisableRAX = true ;
origOperand = scratchReg ;
origOperand . mKind = BeMCOperandKind_VRegLoad ;
continue ;
}
}
bool isValid = true ;
if ( oprIdx = = 1 )
{
if ( rmForm ! = BeMCRMMode_Direct )
{
int safeIdx = FindSafeInstInsertPos ( instIdx , true ) ;
auto newVReg = ReplaceWithNewVReg ( origOperand , safeIdx , true , true ) ;
auto newVRegInfo = GetVRegInfo ( newVReg ) ;
newVRegInfo - > mDisableRAX = true ;
isFinalRun = false ;
}
}
}
}
}
break ;
case BeMCInstKind_Load :
{
// And Load gets converted to a "Load %reg0, [%reg1]"
// So both mArg0 and mArg1 must be a register
if ( ! IsAddressable ( arg1 ) )
{
// Convert to
// Mov %scratch, %vreg1
// Load %vreg0, [%scratch]
ReplaceWithNewVReg ( inst - > mArg1 , instIdx , true ) ;
arg1 = GetFixedOperand ( inst - > mArg1 ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Load \n " ) ;
}
// Do this one second since the 'insert after' makes instIdx no longer point to the Load inst
if ( ! arg0 . IsNativeReg ( ) )
{
// Convert to
// Load %scratch, [%vreg1]
// Mov %vreg0, %scratch
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , false ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Load 2 \n " ) ;
}
}
break ;
case BeMCInstKind_Store :
{
// Store gets converted to a "Store [reg], reg"
if ( ! IsAddressable ( arg0 ) )
{
// Convert to
// Mov %scratch, %vreg0
// Store %scratch, [%vreg1]
ReplaceWithNewVReg ( inst - > mArg0 , instIdx , true ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Store \n " ) ;
}
if ( ! arg1 . IsNativeReg ( ) )
{
// Convert to
// Mov %scratch, %vreg1
// Store %vreg0, [%scratch]
ReplaceWithNewVReg ( inst - > mArg1 , instIdx , true ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " Store2 \n " ) ;
}
}
break ;
}
if ( ( pendingInitKind ! = BfIRInitType_NotNeeded ) & & ( pendingInitKind ! = BfIRInitType_NotNeeded_AliveOnDecl ) )
{
Fail ( " Shouldn't happen here anymore " ) ;
/*int vregIdx = inst->mArg0.mVRegIdx;
auto dbgVar = mVRegInfo [ vregIdx ] - > mDbgVariable ;
auto varType = mVRegInfo [ vregIdx ] - > mType ;
auto initType = dbgVar - > mPendingInitType ;
if ( varType - > IsFloat ( ) )
{
BeMCOperand zeroVal ;
if ( varType - > mTypeCode = = BeTypeCode_Float )
zeroVal . mKind = BeMCOperandKind_Immediate_f32 ;
else
zeroVal . mKind = BeMCOperandKind_Immediate_f64 ;
if ( initType = = BfIRInitType_Uninitialized )
zeroVal . mImmFloat = NAN ;
else
zeroVal . mImmFloat = 0 ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromVReg ( vregIdx ) , zeroVal , instIdx + 1 ) ;
}
else if ( varType - > IsComposite ( ) )
{
SetAndRestoreValue < int * > insertPtr ( mInsertInstIdxRef , & instIdx ) ;
instIdx + + ;
uint8 val = ( initType = = BfIRInitType_Uninitialized ) ? 0xCC : 0 ;
CreateMemSet ( BeMCOperand : : FromVRegAddr ( vregIdx ) , val , varType - > mSize , varType - > mAlign ) ;
instIdx - - ;
isFinalRun = false ;
}
else
{
int64 val = ( initType = = BfIRInitType_Uninitialized ) ? ( int64 ) 0xCCCCCCCCCCCCCCCCLL : 0 ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromVReg ( vregIdx ) , BeMCOperand : : FromImmediate ( val ) , instIdx + 1 ) ;
}
dbgVar - > mPendingInitType = BfIRInitType_NotNeeded ; */
}
}
}
BF_ASSERT ( regPreserveDepth = = 0 ) ;
SetCurrentInst ( NULL ) ;
if ( hasPendingActualizations )
{
DoActualization ( ) ;
isFinalRun = false ;
if ( debugging )
OutputDebugStrF ( " DoActualizations \n " ) ;
}
return isFinalRun ;
}
void BeMCContext : : DoSanityChecking ( )
{
for ( auto mcBlock : mBlocks )
{
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) - 1 ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
auto nextInst = mcBlock - > mInstructions [ instIdx + 1 ] ;
auto dest = inst - > GetDest ( ) ;
if ( dest = = NULL )
continue ;
auto vregInfo = GetVRegInfo ( * dest ) ;
if ( ( vregInfo ! = NULL ) & & ( vregInfo - > mHasDynLife ) )
{
if ( ( nextInst - > mVRegsInitialized ! = NULL ) & & ( ! mVRegInitializedContext . IsSet ( nextInst - > mVRegsInitialized , dest - > mVRegIdx ) ) )
{
if ( nextInst - > mKind ! = BeMCInstKind_RestoreVolatiles )
{
// It's possible during legalization to extend the usage of a dynLife value 'upward', and we need
// to ensure we also extend the init flags up as well
Fail ( " Invalid liveness - from init flag not being set properly? " ) ;
}
}
}
}
}
}
void BeMCContext : : DoBlockCombine ( )
{
auto masterBlock = mMCBlockAlloc . Alloc ( ) ;
mActiveBlock = masterBlock ;
for ( auto mcBlock : mBlocks )
{
mcBlock - > mLabelIdx = mCurLabelIdx + + ;
}
for ( auto mcBlock : mBlocks )
{
auto inst = AllocInst ( ) ;
inst - > mKind = BeMCInstKind_Label ;
inst - > mArg0 . mKind = BeMCOperandKind_Label ;
inst - > mArg0 . mLabelIdx = mcBlock - > mLabelIdx ;
for ( auto blockInst : mcBlock - > mInstructions )
{
if ( inst - > mDbgLoc = = NULL )
inst - > mDbgLoc = blockInst - > mDbgLoc ;
auto operands = { & blockInst - > mArg0 , & blockInst - > mArg1 } ;
for ( auto op : operands )
{
if ( op - > mKind = = BeMCOperandKind_Block )
{
op - > mKind = BeMCOperandKind_Label ;
op - > mLabelIdx = op - > mBlock - > mLabelIdx ;
}
}
masterBlock - > mInstructions . push_back ( blockInst ) ;
}
}
mBlocks . Clear ( ) ;
mBlocks . push_back ( masterBlock ) ;
}
bool BeMCContext : : DoJumpRemovePass ( )
{
struct LabelStats
{
public :
bool mHasRefs ;
bool mForceKeepLabel ;
bool mFromMultipleDbgLocs ;
// If all branches share the dbgLoc with the first inst then we can potentially do a remap
BeDbgLoc * mRefDbgLoc ;
public :
LabelStats ( )
{
mHasRefs = false ;
mForceKeepLabel = false ;
mFromMultipleDbgLocs = false ;
mRefDbgLoc = NULL ;
}
} ;
bool didWork = false ;
SizedArray < LabelStats , 32 > labelStats ;
labelStats . resize ( mCurLabelIdx ) ;
Dictionary < int , int > labelRemaps ;
for ( auto & switchEntry : mSwitchEntries )
{
labelStats [ switchEntry . mBlock - > mLabelIdx ] . mForceKeepLabel = true ;
labelStats [ switchEntry . mBlock - > mLabelIdx ] . mHasRefs = true ;
}
for ( auto mcBlock : mBlocks )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( inst - > mKind = = BeMCInstKind_Label )
{
if ( instIdx < ( int ) mcBlock - > mInstructions . size ( ) - 2 )
{
auto nextInst = mcBlock - > mInstructions [ instIdx + 1 ] ;
if ( nextInst - > mDbgLoc ! = NULL )
{
auto & stats = labelStats [ inst - > mArg0 . mLabelIdx ] ;
if ( ( stats . mRefDbgLoc ! = NULL ) & & ( stats . mRefDbgLoc ! = nextInst - > mDbgLoc ) )
stats . mFromMultipleDbgLocs = true ;
else
stats . mRefDbgLoc = nextInst - > mDbgLoc ;
}
}
}
else
{
auto operands = { & inst - > mArg0 , & inst - > mArg1 } ;
for ( auto operand : operands )
{
if ( operand - > mKind = = BeMCOperandKind_Label )
{
labelStats [ operand - > mLabelIdx ] . mHasRefs = true ;
if ( inst - > mDbgLoc ! = NULL )
{
auto & stats = labelStats [ operand - > mLabelIdx ] ;
if ( ( stats . mRefDbgLoc ! = NULL ) & & ( stats . mRefDbgLoc ! = inst - > mDbgLoc ) )
stats . mFromMultipleDbgLocs = true ;
else
stats . mRefDbgLoc = inst - > mDbgLoc ;
}
}
}
}
}
}
for ( auto mcBlock : mBlocks )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
bool goBackToLastBranch = false ;
bool removeInst = false ;
// If we're just jumping to the next label, remove jump
// It's possible there are several labels in a row here, so check against all of them
if ( inst - > mKind = = BeMCInstKind_Br )
{
if ( inst - > mArg0 . mKind = = BeMCOperandKind_Label )
{
for ( int labelInstIdx = instIdx + 1 ; labelInstIdx < ( int ) mcBlock - > mInstructions . size ( ) ; labelInstIdx + + )
{
auto labelInst = mcBlock - > mInstructions [ labelInstIdx ] ;
if ( labelInst - > mKind ! = BeMCInstKind_Label )
{
if ( labelInst - > IsPsuedo ( ) )
continue ;
break ;
}
if ( inst - > mArg0 = = labelInst - > mArg0 )
{
didWork = true ;
inst - > mKind = inst - > mArg1 ? BeMCInstKind_Nop : BeMCInstKind_EnsureInstructionAt ;
inst - > mArg0 = BeMCOperand ( ) ;
inst - > mArg1 = BeMCOperand ( ) ;
goBackToLastBranch = true ;
break ;
}
}
}
}
if ( removeInst )
{
mcBlock - > mInstructions . RemoveAt ( instIdx ) ;
instIdx - - ;
continue ;
}
// Do we have a label that immediately branches?
if ( inst - > mKind = = BeMCInstKind_Label )
{
// We can only remap this if the contained instructions and all references share the same dbgLoc
if ( ( instIdx < ( int ) mcBlock - > mInstructions . size ( ) - 2 ) & & ( ! labelStats [ inst - > mArg0 . mLabelIdx ] . mFromMultipleDbgLocs ) )
{
bool allowRemove = true ;
auto nextInst = mcBlock - > mInstructions [ instIdx + 1 ] ;
auto nextNextInst = mcBlock - > mInstructions [ instIdx + 2 ] ;
if ( ( nextInst - > mKind = = BeMCInstKind_Br ) & &
( ( inst - > mDbgLoc = = NULL ) | | ( inst - > mDbgLoc = = nextNextInst - > mDbgLoc ) ) & &
( ! labelStats [ inst - > mArg0 . mLabelIdx ] . mForceKeepLabel ) )
{
int checkIdx = instIdx - 1 ;
while ( checkIdx > = 0 )
{
auto prevInst = mcBlock - > mInstructions [ checkIdx ] ;
if ( prevInst - > mKind = = BeMCInstKind_Label )
{
// Keep looking
}
else if ( prevInst - > mKind = = BeMCInstKind_Br )
{
break ;
}
else
{
// We flowed into here, so we can't remove this branch
allowRemove = false ;
break ;
}
checkIdx - - ;
}
/*if (labelStats[inst->mArg0.mLabelIdx].mFromMultipleDbgLocs)
allowRemove = false ; */
didWork = true ;
RemoveInst ( mcBlock , instIdx ) ; // Remove label
labelRemaps . TryAdd ( inst - > mArg0 . mLabelIdx , nextInst - > mArg0 . mLabelIdx ) ;
if ( allowRemove )
{
RemoveInst ( mcBlock , instIdx ) ; // Remove branch
goBackToLastBranch = true ;
}
}
}
}
if ( goBackToLastBranch )
{
// We may be able to remove the previous branch now
int checkIdx = instIdx - 1 ;
while ( checkIdx > = 0 )
{
auto checkInst = mcBlock - > mInstructions [ checkIdx ] ;
if ( checkInst - > mKind = = BeMCInstKind_Br )
{
instIdx = checkIdx - 1 ;
break ;
}
else if ( checkInst - > mKind ! = BeMCInstKind_Label )
break ;
checkIdx - - ;
}
continue ;
}
}
}
if ( ! labelRemaps . IsEmpty ( ) )
{
while ( true )
{
bool didRemapMerge = false ;
for ( auto & remapPair : labelRemaps )
{
//auto itr = labelRemaps.find(remapPair.second);
//if (itr != labelRemaps.end())
int * valuePtr = NULL ;
if ( labelRemaps . TryGetValue ( remapPair . mValue , & valuePtr ) )
{
//remapPair.second = itr->second;
remapPair . mValue = * valuePtr ;
didRemapMerge = true ;
}
}
if ( ! didRemapMerge )
break ;
}
for ( auto mcBlock : mBlocks )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
auto operands = { & inst - > mArg0 , & inst - > mArg1 } ;
for ( auto operand : operands )
{
if ( operand - > mKind = = BeMCOperandKind_Label )
{
//auto itr = labelRemaps.find(operand->mLabelIdx);
//if (itr != labelRemaps.end())
int * valuePtr = NULL ;
if ( labelRemaps . TryGetValue ( operand - > mLabelIdx , & valuePtr ) )
{
labelStats [ operand - > mLabelIdx ] . mHasRefs = false ;
//operand->mLabelIdx = itr->second;
operand - > mLabelIdx = * valuePtr ;
labelStats [ operand - > mLabelIdx ] . mHasRefs = true ;
}
}
}
}
}
}
for ( auto mcBlock : mBlocks )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
bool goBackToLastBranch = false ;
}
}
// Remove unreferenced labels
for ( auto mcBlock : mBlocks )
{
bool doMorePasses = false ;
bool isUnreachable = false ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( inst - > mKind = = BeMCInstKind_Label )
{
isUnreachable = false ;
if ( ! labelStats [ inst - > mArg0 . mLabelIdx ] . mHasRefs )
{
didWork = true ;
mcBlock - > mInstructions . RemoveAt ( instIdx ) ;
instIdx - - ;
}
}
if ( inst - > mKind = = BeMCInstKind_CondBr )
doMorePasses = true ;
if ( inst - > mKind = = BeMCInstKind_Br )
{
if ( isUnreachable )
{
// We can't possible reach this branch. This is an artifact of other br removals
didWork = true ;
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
isUnreachable = true ;
}
}
if ( doMorePasses )
{
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
// For the form:
// CondBr %label0, eq
// Br %label1
// %label0:
// Invert condition and convert that to
// CondBr %label1, neq
// %label0:
// We perform this after the jump-removal process to ensure we compare against the correct
// final label positions, otherwise there are cases where we would incorrectly apply this
// transformation early and end up with worse results than just not doing it
if ( inst - > mKind = = BeMCInstKind_CondBr )
{
if ( instIdx < ( int ) mcBlock - > mInstructions . size ( ) - 2 )
{
int nextIdx = instIdx + 1 ;
int nextNextIdx = instIdx + 2 ;
auto nextInst = mcBlock - > mInstructions [ nextIdx ] ;
if ( nextInst - > mKind = = BeMCInstKind_EnsureInstructionAt )
{
if ( nextInst - > mDbgLoc ! = inst - > mDbgLoc )
continue ;
nextIdx + + ;
nextNextIdx + + ;
nextInst = mcBlock - > mInstructions [ nextIdx ] ;
}
auto nextNextInst = mcBlock - > mInstructions [ nextNextIdx ] ;
if ( ( nextInst - > mKind = = BeMCInstKind_Br ) & &
( nextNextInst - > mKind = = BeMCInstKind_Label ) & & ( inst - > mArg0 = = nextNextInst - > mArg0 ) )
{
didWork = true ;
inst - > mArg0 = nextInst - > mArg0 ;
BF_ASSERT ( inst - > mArg1 . mKind = = BeMCOperandKind_CmpKind ) ;
inst - > mArg1 . mCmpKind = BeModule : : InvertCmp ( inst - > mArg1 . mCmpKind ) ;
mcBlock - > mInstructions . RemoveAt ( nextIdx ) ;
}
}
}
}
}
}
return didWork ;
}
void BeMCContext : : DoRegFinalization ( )
{
SizedArray < int , 32 > savedVolatileVRegs ;
mUsedRegs . Clear ( ) ;
SetCurrentInst ( NULL ) ;
// Remove the Def instructions, replace vreg
for ( auto mcBlock : mBlocks )
{
mActiveBlock = mcBlock ;
for ( int instIdx = 0 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
SetCurrentInst ( inst ) ;
if ( inst - > IsDef ( ) )
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo - > mDbgVariable ! = NULL )
vregInfo - > mAwaitingDbgStart = true ;
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
continue ;
}
// This is similar to an optimization we have in DoLegalization, but it allows for directRelTo's to match
// We can't match those in DoLegalization because it would alter the liveness
if ( inst - > mKind = = BeMCInstKind_Mov )
{
// Useless mov, remove it
if ( OperandsEqual ( inst - > mArg0 , inst - > mArg1 , false ) )
{
if ( inst - > mDbgLoc ! = NULL )
{
inst - > mKind = BeMCInstKind_EnsureInstructionAt ;
inst - > mArg0 = BeMCOperand ( ) ;
inst - > mArg1 = BeMCOperand ( ) ;
}
else
{
RemoveInst ( mcBlock , instIdx ) ;
instIdx - - ;
}
continue ;
}
}
if ( ( inst - > mKind > = BeMCInstKind_Def ) & & ( inst - > mKind < = BeMCInstKind_LifetimeEnd ) )
continue ;
if ( inst - > mResult = = inst - > mArg0 )
{
// Destination is implicitly arg0
inst - > mResult . mKind = BeMCOperandKind_None ;
}
// Unfortunate rule:
// if we end up with:
// PreserveVolatiles RAX
// Shl vreg0<RCX>, CL
// RestoreVolatiles RAXS
// Then convert that to
// PreserveVolatiles RAX
// Shl <SavedLoc>, RAX
// RestoreVolatiles RAX
/*if ((inst->mKind == BeMCInstKind_Shl) ||
( inst - > mKind = = BeMCInstKind_Shr ) | |
( inst - > mKind = = BeMCInstKind_Sar ) )
{
if ( inst - > mArg1 . IsNativeReg ( ) )
{
}
} */
auto operands = { & inst - > mResult , & inst - > mArg0 , & inst - > mArg1 } ;
for ( auto operand : operands )
{
BeMCOperand checkOperand = * operand ;
while ( true )
{
auto vregInfo = GetVRegInfo ( checkOperand ) ;
if ( vregInfo = = NULL )
break ;
// We may think that range starts should always occur on a "mov <vreg>, <value>", but
// we have some cases like un-splatting into a composite, or using an uninitialized local
// variable where we want to start the range on ANY use, not just a direct assignment
if ( vregInfo - > mAwaitingDbgStart )
{
//AllocInst(BeMCInstKind_DbgRangeStart, checkOperand, instIdx + 1);
//vregInfo->mAwaitingDbgStart = false;
}
if ( ! vregInfo - > mRelTo )
break ;
checkOperand = vregInfo - > mRelTo ;
}
FixOperand ( * operand ) ;
if ( operand - > mKind = = BeMCOperandKind_NativeReg )
{
auto nativeReg = ResizeRegister ( operand - > mReg , 8 ) ;
BF_ASSERT ( nativeReg ! = X64Reg_None ) ;
if ( nativeReg ! = X64Reg_RSP ) // This can happen from allocas
mUsedRegs . Add ( nativeReg ) ;
}
}
if ( inst - > IsMov ( ) )
{
bool removeInst = false ;
if ( inst - > mArg0 = = inst - > mArg1 )
{
removeInst = true ;
}
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsNativeReg ( ) ) )
{
// Removes size-reducing moves such as "mov eax, rax"
if ( ResizeRegister ( inst - > mArg0 . mReg , 8 ) = = inst - > mArg1 . mReg )
{
removeInst = true ;
}
}
if ( removeInst )
{
inst - > mKind = BeMCInstKind_EnsureInstructionAt ;
inst - > mResult = BeMCOperand ( ) ;
inst - > mArg0 = BeMCOperand ( ) ;
inst - > mArg1 = BeMCOperand ( ) ;
}
}
switch ( inst - > mKind )
{
case BeMCInstKind_PreserveVolatiles :
{
if ( mDebugging )
{
NOP ;
}
int preserveIdx ;
BeMCInst * preserveInst ;
BeMCInst * restoreInst ;
if ( inst - > mKind = = BeMCInstKind_PreserveVolatiles )
{
preserveIdx = instIdx ;
preserveInst = inst ;
restoreInst = mcBlock - > mInstructions [ FindRestoreVolatiles ( mcBlock , instIdx ) ] ;
}
else
{
preserveIdx = FindPreserveVolatiles ( mcBlock , instIdx ) ;
preserveInst = mcBlock - > mInstructions [ preserveIdx ] ;
restoreInst = inst ;
}
int insertIdx = instIdx ;
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsNativeReg ( ) ) )
{
// If our exception (IE: div target) is set to the desired preserve reg then
if ( inst - > mArg0 . mReg = = ResizeRegister ( inst - > mArg1 . mReg , 8 ) )
break ;
}
// Save volatile registers
for ( int liveVRegIdx : * inst - > mLiveness )
{
if ( liveVRegIdx > = mLivenessContext . mNumItems )
continue ;
auto checkVRegIdx = GetUnderlyingVReg ( liveVRegIdx ) ;
// Already handled the underlying vreg?
if ( ( checkVRegIdx ! = liveVRegIdx ) & & ( mLivenessContext . IsSet ( preserveInst - > mLiveness , checkVRegIdx ) ) )
continue ;
auto vregInfo = mVRegInfo [ checkVRegIdx ] ;
if ( vregInfo - > mReg ! = X64Reg_None )
{
// Do we specify a particular reg, or just all volatiles?
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg ! = ResizeRegister ( vregInfo - > mReg , 8 ) ) )
continue ;
if ( ! mLivenessContext . IsSet ( restoreInst - > mLiveness , liveVRegIdx ) )
{
// This vreg doesn't survive until the PreserveRegs -- it's probably used for params or the call addr
continue ;
}
if ( IsVolatileReg ( vregInfo - > mReg ) )
{
if ( vregInfo - > mVolatileVRegSave = = - 1 )
{
BF_ASSERT ( inst - > mKind ! = BeMCInstKind_RestoreVolatiles ) ; // Should have already been allocated
auto savedVReg = AllocVirtualReg ( vregInfo - > mType ) ;
auto savedVRegInfo = mVRegInfo [ savedVReg . mVRegIdx ] ;
savedVRegInfo - > mForceMem = true ;
vregInfo - > mVolatileVRegSave = savedVReg . mVRegIdx ;
}
savedVolatileVRegs . push_back ( checkVRegIdx ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromVReg ( vregInfo - > mVolatileVRegSave ) , GetVReg ( checkVRegIdx ) , insertIdx + + ) ;
instIdx + + ;
}
}
}
bool savingSpecificReg = false ;
if ( inst - > mArg0 )
{
if ( inst - > mArg0 . IsNativeReg ( ) )
savingSpecificReg = true ;
else
BF_ASSERT ( inst - > mArg0 . mKind = = BeMCOperandKind_PreserveFlag ) ;
}
// Full PreserveRegs case:
// Check register crossover errors, ie:
// Mov RCX, reg0<RDX>
// Mov RDX, reg1<RCX>
if ( ! savingSpecificReg )
{
int preserveIdx = instIdx ;
bool regsFailed = false ;
for ( int pass = 0 ; true ; pass + + )
{
bool regStomped [ X64Reg_COUNT ] = { false } ;
bool regFailed [ X64Reg_COUNT ] = { false } ;
regsFailed = false ;
bool isFinalPass = pass = = 4 ;
int instEndIdx = - 1 ;
for ( int instIdx = preserveIdx + 1 ; instIdx < ( int ) mcBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( ( inst - > mKind = = BeMCInstKind_RestoreVolatiles ) | |
( inst - > mKind = = BeMCInstKind_Call ) )
{
instEndIdx = instIdx ;
if ( inst - > mKind = = BeMCInstKind_Call )
{
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
GetRMParams ( inst - > mArg0 , regA , regB , bScale , disp ) ;
if ( ( ( regA ! = X64Reg_None ) & & ( regA ! = X64Reg_RAX ) & & ( regStomped [ regA ] ) ) | |
( ( regB ! = X64Reg_None ) & & ( regStomped [ regB ] ) ) )
{
BF_ASSERT ( pass = = 0 ) ;
// Target is stomped! Mov to RAX and then handle it along with the generalized reg setup
auto callTarget = BeMCOperand : : FromReg ( X64Reg_RAX ) ;
AllocInst ( BeMCInstKind_Mov , callTarget , inst - > mArg0 , preserveIdx + 1 ) ;
inst - > mArg0 = callTarget ;
regsFailed = true ; // So we'll rerun taking into account the new RAX reg
instEndIdx + + ;
}
}
break ;
}
if ( inst - > mKind = = BeMCInstKind_Def )
{
// If we have a Def here, it's because of a legalization that introduced a variable in a non-safe position
Fail ( " Illegal def " ) ;
}
if ( inst - > mKind = = BeMCInstKind_Mov )
{
X64CPURegister regs [ 2 ] = { X64Reg_None , X64Reg_None } ;
GetUsedRegs ( inst - > mArg1 , regs [ 0 ] , regs [ 1 ] ) ;
bool isStomped = false ;
for ( auto reg : regs )
{
if ( reg ! = X64Reg_None )
{
if ( regStomped [ reg ] )
{
regsFailed = true ;
isStomped = true ;
regFailed [ reg ] = true ;
}
}
}
// Don't swap on the final pass, we need regsFailed to be accurate so this is just a
// verification pass
if ( ( isStomped ) & & ( ! isFinalPass ) )
{
// Bubble up, maybe a reordering will fix our issue
BF_SWAP ( mcBlock - > mInstructions [ instIdx - 1 ] , mcBlock - > mInstructions [ instIdx ] ) ;
}
if ( inst - > mArg0 . IsNativeReg ( ) )
{
auto reg = ResizeRegister ( inst - > mArg0 . mReg , 8 ) ;
regStomped [ ( int ) reg ] = true ;
}
}
}
if ( ! regsFailed )
break ;
if ( isFinalPass )
{
// We've failed to reorder
int deferredIdx = 0 ;
for ( int instIdx = preserveIdx + 1 ; instIdx < instEndIdx ; instIdx + + )
{
auto inst = mcBlock - > mInstructions [ instIdx ] ;
if ( ( inst - > mKind = = BeMCInstKind_RestoreVolatiles ) | |
( inst - > mKind = = BeMCInstKind_Call ) )
break ;
if ( inst - > mKind = = BeMCInstKind_Mov )
{
if ( inst - > mArg0 . IsNativeReg ( ) )
{
auto reg = ResizeRegister ( inst - > mArg0 . mReg , 8 ) ;
if ( regFailed [ ( int ) reg ] )
{
// Convert
// Mov <reg>, vreg0
// To
// Push vreg0
// ...
// Pop <reg>
auto mcPopReg = inst - > mArg0 ;
auto popType = GetType ( mcPopReg ) ;
if ( ! popType - > IsFloat ( ) )
mcPopReg . mReg = ResizeRegister ( mcPopReg . mReg , 8 ) ;
auto pushType = GetType ( inst - > mArg1 ) ;
auto useTypeCode = pushType - > IsFloat ( ) ? pushType - > mTypeCode : BeTypeCode_Int64 ;
BF_ASSERT ( deferredIdx < 4 ) ;
auto mcDeferred = GetCallArgVReg ( deferredIdx + + , useTypeCode ) ;
CreateDefineVReg ( BeMCOperand : : FromVReg ( mcDeferred . mVRegIdx ) , instIdx + + ) ;
instEndIdx + + ;
auto popInst = AllocInst ( BeMCInstKind_Mov , mcPopReg , mcDeferred , instEndIdx ) ;
//auto popInst = AllocInst(BeMCInstKind_Pop, mcPopReg, instEndIdx);
auto arg1 = inst - > mArg1 ;
FixOperand ( arg1 ) ;
if ( arg1 . IsNativeReg ( ) )
{
inst - > mKind = BeMCInstKind_Mov ;
inst - > mArg0 = mcDeferred ;
if ( ! popType - > IsFloat ( ) )
inst - > mArg1 = BeMCOperand : : FromReg ( ResizeRegister ( arg1 . mReg , 8 ) ) ;
else
inst - > mArg1 = BeMCOperand : : FromReg ( arg1 . mReg ) ;
}
else
{
// Use R11 or XMM5 as our temporary - they are the least likely volatiles to be
// allocated, so we may not need to restore them after using them
X64CPURegister scratchReg ;
if ( pushType - > mTypeCode = = BeTypeCode_Float )
scratchReg = X64Reg_XMM5_f32 ;
else if ( pushType - > mTypeCode = = BeTypeCode_Double )
scratchReg = X64Reg_XMM5_f64 ;
else
scratchReg = X64Reg_R11 ;
int volatileVRegSave = - 1 ;
for ( auto vregIdx : savedVolatileVRegs )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( ResizeRegister ( vregInfo - > mReg , 8 ) = = scratchReg )
{
volatileVRegSave = vregInfo - > mVolatileVRegSave ;
}
}
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( scratchReg ) , inst - > mArg1 , instIdx + + ) ;
instEndIdx + + ;
inst - > mKind = BeMCInstKind_Mov ;
inst - > mArg0 = mcDeferred ;
inst - > mArg1 = BeMCOperand : : FromReg ( scratchReg ) ;
if ( volatileVRegSave ! = - 1 )
{
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( scratchReg ) , BeMCOperand : : FromVReg ( volatileVRegSave ) , + + instIdx ) ;
instEndIdx + + ;
}
}
}
}
}
}
break ;
}
}
}
}
break ;
case BeMCInstKind_RestoreVolatiles :
{
bool doRestore = true ;
if ( ( inst - > mArg0 . mKind = = BeMCOperandKind_PreserveFlag ) & & ( ( inst - > mArg0 . mPreserveFlag & BeMCPreserveFlag_NoRestore ) ! = 0 ) )
{
// Don't bother restore registers for NoReturns
doRestore = false ;
}
if ( doRestore )
{
for ( auto vregIdx : savedVolatileVRegs )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
AllocInst ( BeMCInstKind_Mov , GetVReg ( vregIdx ) , BeMCOperand : : FromVReg ( vregInfo - > mVolatileVRegSave ) , instIdx + + ) ;
}
}
savedVolatileVRegs . clear ( ) ;
}
break ;
}
}
}
SetCurrentInst ( NULL ) ;
// Do this at the end so we can properly handle RangeStarts
for ( int vregIdx = 0 ; vregIdx < ( int ) mVRegInfo . size ( ) ; vregIdx + + )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mRelTo )
{
FixOperand ( vregInfo - > mRelTo ) ;
FixOperand ( vregInfo - > mRelOffset ) ;
}
}
}
BeMCInstForm BeMCContext : : GetInstForm ( BeMCInst * inst )
{
if ( ! inst - > mArg0 )
return BeMCInstForm_Unknown ;
if ( ! inst - > mArg1 )
{
if ( inst - > mArg0 . mKind = = BeMCOperandKind_Symbol )
return BeMCInstForm_Symbol ;
if ( inst - > mArg0 . mKind = = BeMCOperandKind_SymbolAddr )
return BeMCInstForm_SymbolAddr ;
auto arg0Type = GetType ( inst - > mArg0 ) ;
if ( arg0Type = = NULL )
return BeMCInstForm_Unknown ;
if ( inst - > mArg0 . IsImmediate ( ) )
{
if ( ( inst - > mArg0 . mImmediate > = - 0x80000000LL ) & & ( inst - > mArg0 . mImmediate < = 0x7FFFFFFF ) )
return BeMCInstForm_IMM32 ;
}
if ( inst - > mArg0 . mKind = = BeMCOperandKind_NativeReg )
{
if ( arg0Type - > mSize = = 4 )
return BeMCInstForm_R32 ;
if ( arg0Type - > mSize = = 2 )
return BeMCInstForm_R16 ;
if ( arg0Type - > mSize = = 1 )
return BeMCInstForm_R8 ;
return BeMCInstForm_R64 ;
}
if ( arg0Type - > mSize = = 4 )
return BeMCInstForm_RM32 ;
if ( arg0Type - > mSize = = 2 )
return BeMCInstForm_RM16 ;
if ( arg0Type - > mSize = = 1 )
return BeMCInstForm_RM8 ;
return BeMCInstForm_RM64 ;
}
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
if ( ( arg0Type ! = NULL ) & & ( arg1Type ! = NULL ) & &
( ( arg0Type - > IsFloat ( ) ) | | ( arg1Type - > IsFloat ( ) ) ) )
{
if ( inst - > mArg0 . IsNativeReg ( ) )
{
if ( arg0Type - > mTypeCode = = BeTypeCode_Double )
{
/*if (inst->mArg1.IsNativeReg())
{
if ( arg1Type - > mTypeCode = = BeTypeCode_Double )
return BeMCInstForm_XMM64_XMM64 ;
else if ( arg1Type - > mTypeCode = = BeTypeCode_Float )
return BeMCInstForm_XMM64_XMM32 ;
} */
if ( inst - > mArg1 . IsImmediate ( ) )
return BeMCInstForm_XMM64_IMM ;
/*if (inst->mArg1.IsSymbol())
return BeMCInstForm_XMM64_FRM64 ; */
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Float : return BeMCInstForm_XMM64_FRM32 ;
case BeTypeCode_Double : return BeMCInstForm_XMM64_FRM64 ;
case BeTypeCode_Int32 : return BeMCInstForm_XMM64_RM32 ;
case BeTypeCode_Int64 : return BeMCInstForm_XMM64_RM64 ;
default : NotImpl ( ) ;
}
}
else if ( arg0Type - > mTypeCode = = BeTypeCode_Float )
{
/*if (inst->mArg1.IsNativeReg())
{
if ( arg1Type - > mTypeCode = = BeTypeCode_Double )
return BeMCInstForm_XMM32_XMM64 ;
else if ( arg1Type - > mTypeCode = = BeTypeCode_Float )
return BeMCInstForm_XMM32_XMM32 ;
} */
if ( inst - > mArg1 . IsImmediate ( ) )
return BeMCInstForm_XMM32_IMM ;
/*if (inst->mArg1.IsSymbol())
return BeMCInstForm_XMM32_FRM32 ; */
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Float : return BeMCInstForm_XMM32_FRM32 ;
case BeTypeCode_Double : return BeMCInstForm_XMM32_FRM64 ;
case BeTypeCode_Int32 : return BeMCInstForm_XMM32_RM32 ;
case BeTypeCode_Int64 : return BeMCInstForm_XMM32_RM64 ;
default : NotImpl ( ) ;
}
}
else if ( arg1Type - > mTypeCode = = BeTypeCode_Double )
{
if ( arg0Type - > mSize = = 4 )
return BeMCInstForm_R32_F64 ;
else
return BeMCInstForm_R64_F64 ;
}
else if ( arg1Type - > mTypeCode = = BeTypeCode_Float )
{
if ( arg0Type - > mSize = = 4 )
return BeMCInstForm_R32_F32 ;
else
return BeMCInstForm_R64_F32 ;
}
}
else
{
if ( inst - > mArg1 . IsImmediate ( ) )
return BeMCInstForm_Unknown ;
if ( arg0Type - > mTypeCode = = BeTypeCode_Double )
{
//TODO: This used to INCORRECTLY say 'switch (arg0Type->mTypeCode)'
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Float : return BeMCInstForm_FRM32_XMM64 ;
case BeTypeCode_Double : return BeMCInstForm_FRM64_XMM64 ;
default : NotImpl ( ) ;
}
}
else if ( arg0Type - > mTypeCode = = BeTypeCode_Float )
{
//TODO: This used to INCORRECTLY say 'switch (arg0Type->mTypeCode)'
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Float : return BeMCInstForm_FRM32_XMM32 ;
case BeTypeCode_Double : return BeMCInstForm_FRM64_XMM32 ;
default : NotImpl ( ) ;
}
}
else
NotImpl ( ) ;
}
}
if ( ( inst - > mArg1 . IsImmediate ( ) ) & & ( arg0Type ! = NULL ) ) // MOV r/m64, imm32
{
int64 maxSize = 8 ;
if ( ( arg0Type - > IsInt ( ) ) & & ( inst - > mArg1 . IsImmediateInt ( ) ) )
maxSize = arg0Type - > mSize ;
if ( ( inst - > mArg1 . mKind = = BeMCOperandKind_Immediate_Null ) | | ( maxSize < = 1 ) | |
( ( inst - > mArg1 . mImmediate > = - 0x80 ) & & ( inst - > mArg1 . mImmediate < = 0x7F ) ) )
{
switch ( arg0Type - > mSize )
{
case 8 : return BeMCInstForm_RM64_IMM8 ;
case 4 : return BeMCInstForm_RM32_IMM8 ;
case 2 : return BeMCInstForm_RM16_IMM8 ;
case 1 : return BeMCInstForm_RM8_IMM8 ;
}
}
else if ( ( maxSize < = 2 ) | |
( ( inst - > mArg1 . mImmediate > = - 0x8000 ) & & ( inst - > mArg1 . mImmediate < = 0x7FFF ) ) )
{
switch ( arg0Type - > mSize )
{
case 8 : return BeMCInstForm_RM64_IMM16 ;
case 4 : return BeMCInstForm_RM32_IMM16 ;
case 2 : return BeMCInstForm_RM16_IMM16 ;
case 1 : return BeMCInstForm_RM8_IMM8 ;
}
}
else if ( ( maxSize < = 4 ) | |
( ( inst - > mArg1 . mImmediate > = - 0x80000000LL ) & & ( inst - > mArg1 . mImmediate < = 0x7FFFFFFF ) ) )
{
switch ( arg0Type - > mSize )
{
case 8 : return BeMCInstForm_RM64_IMM32 ;
case 4 : return BeMCInstForm_RM32_IMM32 ;
case 2 : return BeMCInstForm_RM16_IMM16 ;
case 1 : return BeMCInstForm_RM8_IMM8 ;
}
}
else
return BeMCInstForm_RM64_IMM64 ;
}
if ( ( arg0Type = = NULL ) | | ( arg1Type = = NULL ) | | ( arg0Type - > mSize ! = arg1Type - > mSize ) )
return BeMCInstForm_Unknown ;
if ( inst - > mArg0 . mKind = = BeMCOperandKind_NativeReg )
{
switch ( GetType ( inst - > mArg0 ) - > mSize )
{
case 8 : return BeMCInstForm_R64_RM64 ;
case 4 : return BeMCInstForm_R32_RM32 ;
case 2 : return BeMCInstForm_R16_RM16 ;
case 1 : return BeMCInstForm_R8_RM8 ;
}
}
if ( inst - > mArg1 . mKind = = BeMCOperandKind_NativeReg )
{
switch ( GetType ( inst - > mArg1 ) - > mSize )
{
case 8 : return BeMCInstForm_RM64_R64 ;
case 4 : return BeMCInstForm_RM32_R32 ;
case 2 : return BeMCInstForm_RM16_R16 ;
case 1 : return BeMCInstForm_RM8_R8 ;
}
}
return BeMCInstForm_Unknown ;
}
BeMCInstForm BeMCContext : : ToIMM16 ( BeMCInstForm instForm )
{
switch ( instForm )
{
case BeMCInstForm_RM16_IMM8 : return BeMCInstForm_RM32_IMM16 ;
case BeMCInstForm_RM32_IMM8 : return BeMCInstForm_RM32_IMM16 ;
case BeMCInstForm_RM64_IMM8 : return BeMCInstForm_RM64_IMM16 ;
case BeMCInstForm_RM16_IMM16 : return BeMCInstForm_RM32_IMM16 ;
case BeMCInstForm_RM32_IMM16 : return BeMCInstForm_RM32_IMM16 ;
case BeMCInstForm_RM64_IMM16 : return BeMCInstForm_RM64_IMM16 ;
}
NotImpl ( ) ;
return instForm ;
}
BeMCInstForm BeMCContext : : ToIMM32OrSmaller ( BeMCInstForm instForm )
{
switch ( instForm )
{
case BeMCInstForm_RM8_IMM8 : return BeMCInstForm_RM8_IMM8 ;
case BeMCInstForm_RM16_IMM8 : return BeMCInstForm_RM16_IMM16 ;
case BeMCInstForm_RM32_IMM8 : return BeMCInstForm_RM32_IMM32 ;
case BeMCInstForm_RM64_IMM8 : return BeMCInstForm_RM64_IMM32 ;
case BeMCInstForm_RM16_IMM16 : return BeMCInstForm_RM16_IMM16 ;
case BeMCInstForm_RM32_IMM16 : return BeMCInstForm_RM32_IMM32 ;
case BeMCInstForm_RM64_IMM16 : return BeMCInstForm_RM64_IMM32 ;
case BeMCInstForm_RM32_IMM32 : return BeMCInstForm_RM32_IMM32 ;
case BeMCInstForm_RM64_IMM32 : return BeMCInstForm_RM64_IMM32 ;
}
NotImpl ( ) ;
return instForm ;
}
void BeMCContext : : SetCurrentInst ( BeMCInst * inst )
{
if ( inst = = NULL )
{
mCurDbgLoc = NULL ;
mCurVRegsInit = NULL ;
mCurVRegsLive = NULL ;
}
else
{
mCurDbgLoc = inst - > mDbgLoc ;
mCurVRegsInit = inst - > mVRegsInitialized ;
mCurVRegsLive = inst - > mLiveness ;
}
}
int BeMCContext : : FindPreserveVolatiles ( BeMCBlock * mcBlock , int instIdx )
{
for ( int checkIdx = instIdx - 1 ; checkIdx > = 0 ; checkIdx - - )
{
BeMCInst * checkInst = mcBlock - > mInstructions [ checkIdx ] ;
if ( checkInst - > mKind = = BeMCInstKind_PreserveVolatiles )
return checkIdx ;
}
return - 1 ;
}
int BeMCContext : : FindRestoreVolatiles ( BeMCBlock * mcBlock , int instIdx )
{
for ( int checkIdx = instIdx + 1 ; checkIdx < ( int ) mcBlock - > mInstructions . size ( ) ; checkIdx + + )
{
BeMCInst * checkInst = mcBlock - > mInstructions [ checkIdx ] ;
if ( checkInst - > mKind = = BeMCInstKind_RestoreVolatiles )
return checkIdx ;
}
return mcBlock - > mInstructions . size ( ) - 1 ; // Ret
}
void BeMCContext : : EmitInstPrefix ( BeMCInstForm instForm , BeMCInst * inst )
{
switch ( instForm )
{
case BeMCInstForm_R16_RM16 :
case BeMCInstForm_R16_RM64_ADDR :
case BeMCInstForm_RM16_R16 :
case BeMCInstForm_RM16_IMM8 :
case BeMCInstForm_RM16_IMM16 :
case BeMCInstForm_RM64_R16_ADDR :
case BeMCInstForm_RM16 :
case BeMCInstForm_R16 :
Emit ( 0x66 ) ;
}
switch ( instForm )
{
case BeMCInstForm_R64_RM64 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
break ;
case BeMCInstForm_R8_RM8 :
case BeMCInstForm_R16_RM16 :
case BeMCInstForm_R32_RM32 :
case BeMCInstForm_R8_RM64_ADDR :
case BeMCInstForm_R16_RM64_ADDR :
case BeMCInstForm_R32_RM64_ADDR :
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
break ;
case BeMCInstForm_R64_RM64_ADDR :
EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
break ;
case BeMCInstForm_RM8_R8 :
case BeMCInstForm_RM16_R16 :
case BeMCInstForm_RM32_R32 :
case BeMCInstForm_RM64_R8_ADDR :
case BeMCInstForm_RM64_R16_ADDR :
case BeMCInstForm_RM64_R32_ADDR :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
break ;
case BeMCInstForm_RM64_R64 :
case BeMCInstForm_RM64_R64_ADDR :
EmitREX ( inst - > mArg1 , inst - > mArg0 , true ) ;
break ;
case BeMCInstForm_RM8_IMM8 :
case BeMCInstForm_RM16_IMM8 :
case BeMCInstForm_RM16_IMM16 :
case BeMCInstForm_RM32_IMM8 :
case BeMCInstForm_RM32_IMM16 :
case BeMCInstForm_RM32_IMM32 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
break ;
case BeMCInstForm_RM64_IMM8 :
case BeMCInstForm_RM64_IMM16 :
case BeMCInstForm_RM64_IMM32 :
case BeMCInstForm_RM64_IMM64 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , true ) ;
break ;
}
}
void BeMCContext : : EmitInst ( BeMCInstForm instForm , uint16 codeBytes , BeMCInst * inst )
{
EmitInstPrefix ( instForm , inst ) ;
if ( codeBytes < = 0xFF )
mOut . Write ( ( uint8 ) codeBytes ) ;
else
mOut . Write ( ( int16 ) codeBytes ) ;
switch ( instForm )
{
case BeMCInstForm_R8_RM8 :
case BeMCInstForm_R16_RM16 :
case BeMCInstForm_R32_RM32 :
case BeMCInstForm_R64_RM64 :
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeMCInstForm_R8_RM64_ADDR :
case BeMCInstForm_R16_RM64_ADDR :
case BeMCInstForm_R32_RM64_ADDR :
case BeMCInstForm_R64_RM64_ADDR :
EmitModRM_Addr ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeMCInstForm_RM8_R8 :
case BeMCInstForm_RM16_R16 :
case BeMCInstForm_RM32_R32 :
case BeMCInstForm_RM64_R64 :
EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM64_R8_ADDR :
case BeMCInstForm_RM64_R16_ADDR :
case BeMCInstForm_RM64_R32_ADDR :
case BeMCInstForm_RM64_R64_ADDR :
EmitModRM_Addr ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM64_IMM32 :
break ;
default :
Fail ( " Notimpl EmitInst " ) ;
}
}
void BeMCContext : : EmitInst ( BeMCInstForm instForm , uint16 codeBytes , uint8 rx , BeMCInst * inst )
{
EmitInstPrefix ( instForm , inst ) ;
BF_ASSERT ( codeBytes ! = 0 ) ;
if ( codeBytes < = 0xFF )
mOut . Write ( ( uint8 ) codeBytes ) ;
else
mOut . Write ( ( int16 ) codeBytes ) ;
switch ( instForm )
{
case BeMCInstForm_RM8_IMM8 :
case BeMCInstForm_RM16_IMM8 :
case BeMCInstForm_RM32_IMM8 :
case BeMCInstForm_RM64_IMM8 :
EmitModRM ( rx , inst - > mArg0 , - 1 ) ;
mOut . Write ( ( int8 ) inst - > mArg1 . GetImmediateInt ( ) ) ;
break ;
case BeMCInstForm_RM16_IMM16 :
case BeMCInstForm_RM32_IMM16 :
case BeMCInstForm_RM64_IMM16 :
EmitModRM ( rx , inst - > mArg0 , - 2 ) ;
mOut . Write ( ( int16 ) inst - > mArg1 . GetImmediateInt ( ) ) ;
break ;
case BeMCInstForm_RM32_IMM32 :
case BeMCInstForm_RM64_IMM32 :
EmitModRM ( rx , inst - > mArg0 , - 4 ) ;
mOut . Write ( ( int32 ) inst - > mArg1 . GetImmediateInt ( ) ) ;
break ;
case BeMCInstForm_RM64_IMM64 :
EmitModRM ( rx , inst - > mArg0 , - 8 ) ;
mOut . Write ( ( int64 ) inst - > mArg1 . GetImmediateInt ( ) ) ;
break ;
default :
Fail ( " Notimpl EmitInst " ) ;
}
}
void BeMCContext : : EmitStdInst ( BeMCInstForm instForm , BeMCInst * inst , uint8 opcode_rm_r , uint8 opcode_r_rm , uint8 opcode_rm_imm , uint8 opcode_rm_imm_rx )
{
switch ( instForm )
{
case BeMCInstForm_R8_RM8 : EmitInst ( instForm , opcode_r_rm - 1 , inst ) ; break ;
case BeMCInstForm_R16_RM16 :
case BeMCInstForm_R32_RM32 :
case BeMCInstForm_R64_RM64 : EmitInst ( instForm , opcode_r_rm , inst ) ; break ;
case BeMCInstForm_RM8_R8 : EmitInst ( instForm , opcode_rm_r - 1 , inst ) ; break ;
case BeMCInstForm_RM16_R16 :
case BeMCInstForm_RM32_R32 :
case BeMCInstForm_RM64_R64 : EmitInst ( instForm , opcode_rm_r , inst ) ; break ;
// These immediate forms assume an expansion to imm32, OR the register size for 8 and 16 bit registers
case BeMCInstForm_RM8_IMM8 : EmitInst ( BeMCInstForm_RM8_IMM8 , opcode_rm_imm - 1 , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM16_IMM8 : EmitInst ( BeMCInstForm_RM16_IMM16 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM32_IMM8 : EmitInst ( BeMCInstForm_RM32_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM64_IMM8 : EmitInst ( BeMCInstForm_RM64_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM32_IMM16 : EmitInst ( BeMCInstForm_RM32_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM16_IMM16 : EmitInst ( BeMCInstForm_RM16_IMM16 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM64_IMM16 : EmitInst ( BeMCInstForm_RM64_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM32_IMM32 : EmitInst ( BeMCInstForm_RM32_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM64_IMM32 : EmitInst ( BeMCInstForm_RM64_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
default :
Print ( ) ;
NotImpl ( ) ;
}
}
void BeMCContext : : EmitStdInst ( BeMCInstForm instForm , BeMCInst * inst , uint8 opcode_rm_r , uint8 opcode_r_rm , uint8 opcode_rm_imm , uint8 opcode_rm_imm_rx , uint8 opcode_rm_imm8 , uint8 opcode_rm_imm8_rx )
{
switch ( instForm )
{
case BeMCInstForm_R8_RM8 : EmitInst ( instForm , opcode_r_rm - 1 , inst ) ; break ;
case BeMCInstForm_R16_RM16 :
case BeMCInstForm_R32_RM32 :
case BeMCInstForm_R64_RM64 : EmitInst ( instForm , opcode_r_rm , inst ) ; break ;
case BeMCInstForm_RM8_R8 : EmitInst ( instForm , opcode_rm_r - 1 , inst ) ; break ;
case BeMCInstForm_RM16_R16 :
case BeMCInstForm_RM32_R32 :
case BeMCInstForm_RM64_R64 : EmitInst ( instForm , opcode_rm_r , inst ) ; break ;
case BeMCInstForm_RM8_IMM8 : EmitInst ( BeMCInstForm_RM8_IMM8 , opcode_rm_imm - 1 , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM16_IMM8 : EmitInst ( BeMCInstForm_RM16_IMM8 , opcode_rm_imm8 , opcode_rm_imm8_rx , inst ) ; break ;
case BeMCInstForm_RM32_IMM8 : EmitInst ( BeMCInstForm_RM32_IMM8 , opcode_rm_imm8 , opcode_rm_imm8_rx , inst ) ; break ;
case BeMCInstForm_RM64_IMM8 : EmitInst ( BeMCInstForm_RM64_IMM8 , opcode_rm_imm8 , opcode_rm_imm8_rx , inst ) ; break ;
// These immediate forms assume an expansion to imm32, OR the register size for 8 and 16 bit registers
case BeMCInstForm_RM32_IMM16 : EmitInst ( BeMCInstForm_RM32_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM16_IMM16 : EmitInst ( BeMCInstForm_RM16_IMM16 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM64_IMM16 : EmitInst ( BeMCInstForm_RM64_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM32_IMM32 : EmitInst ( BeMCInstForm_RM32_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
case BeMCInstForm_RM64_IMM32 : EmitInst ( BeMCInstForm_RM64_IMM32 , opcode_rm_imm , opcode_rm_imm_rx , inst ) ; break ;
default :
NotImpl ( ) ;
}
}
bool BeMCContext : : EmitStdXMMInst ( BeMCInstForm instForm , BeMCInst * inst , uint8 opcode )
{
bool is64Bit = false ;
switch ( instForm )
{
case BeMCInstForm_R64_F32 :
case BeMCInstForm_XMM32_RM64 :
is64Bit = true ;
// Fallthrough
case BeMCInstForm_R32_F32 :
case BeMCInstForm_XMM32_RM32 :
case BeMCInstForm_XMM32_IMM :
case BeMCInstForm_XMM32_FRM32 :
case BeMCInstForm_XMM64_FRM32 : // CVTSS2SD
Emit ( 0xF3 ) ; EmitREX ( inst - > mArg0 , inst - > mArg1 , is64Bit ) ;
Emit ( 0x0F ) ; Emit ( opcode ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
return true ;
case BeMCInstForm_R64_F64 :
case BeMCInstForm_XMM64_RM64 :
is64Bit = true ;
// Fallthrough
case BeMCInstForm_R32_F64 :
case BeMCInstForm_XMM64_RM32 :
case BeMCInstForm_XMM64_IMM :
case BeMCInstForm_XMM64_FRM64 :
case BeMCInstForm_XMM32_FRM64 : // CVTSD2SS
Emit ( 0xF2 ) ; EmitREX ( inst - > mArg0 , inst - > mArg1 , is64Bit ) ;
Emit ( 0x0F ) ; Emit ( opcode ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
return true ;
}
return false ;
}
bool BeMCContext : : EmitStdXMMInst ( BeMCInstForm instForm , BeMCInst * inst , uint8 opcode , uint8 opcode_dest_frm )
{
bool is64Bit = false ;
switch ( instForm )
{
case BeMCInstForm_FRM32_XMM32 :
Emit ( 0xF3 ) ; EmitREX ( inst - > mArg1 , inst - > mArg0 , is64Bit ) ;
Emit ( 0x0F ) ; Emit ( opcode_dest_frm ) ;
EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
return true ;
case BeMCInstForm_FRM64_XMM64 :
Emit ( 0xF2 ) ; EmitREX ( inst - > mArg1 , inst - > mArg0 , is64Bit ) ;
Emit ( 0x0F ) ; Emit ( opcode_dest_frm ) ;
EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
return true ;
default :
return EmitStdXMMInst ( instForm , inst , opcode ) ;
}
return false ;
}
bool BeMCContext : : EmitPackedXMMInst ( BeMCInstForm instForm , BeMCInst * inst , uint8 opcode )
{
bool is64Bit = false ;
switch ( instForm )
{
case BeMCInstForm_R64_F32 :
case BeMCInstForm_XMM32_RM64 :
is64Bit = true ;
// Fallthrough
case BeMCInstForm_R32_F32 :
case BeMCInstForm_XMM32_RM32 :
case BeMCInstForm_XMM32_IMM :
case BeMCInstForm_XMM32_FRM32 :
case BeMCInstForm_XMM64_FRM32 : // CVTSS2SD
EmitREX ( inst - > mArg0 , inst - > mArg1 , is64Bit ) ;
Emit ( 0x0F ) ; Emit ( opcode ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
return true ;
case BeMCInstForm_R64_F64 :
case BeMCInstForm_XMM64_RM64 :
is64Bit = true ;
// Fallthrough
case BeMCInstForm_R32_F64 :
case BeMCInstForm_XMM64_RM32 :
case BeMCInstForm_XMM64_IMM :
case BeMCInstForm_XMM64_FRM64 :
case BeMCInstForm_XMM32_FRM64 : // CVTSD2SS
EmitREX ( inst - > mArg0 , inst - > mArg1 , is64Bit ) ;
Emit ( 0x0F ) ; Emit ( opcode ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
return true ;
}
return false ;
}
void BeMCContext : : EmitAggMov ( const BeMCOperand & dest , const BeMCOperand & src )
{
X64CPURegister regA = X64Reg_None ;
X64CPURegister regB = X64Reg_None ;
int bScale = 1 ;
int disp = 0 ;
auto form = GetRMParams ( dest , regA , regB , bScale , disp ) ;
BF_ASSERT ( src . mKind = = BeMCOperandKind_ConstAgg ) ;
auto aggConstant = src . mConstant ;
Array < uint8 > dataVec ;
aggConstant - > GetData ( dataVec ) ;
int memSize = dataVec . size ( ) ;
int curOfs = 0 ;
bool allowRep = dataVec . size ( ) > = BF_REP_MOV_LIMIT ;
2019-08-27 08:04:41 -07:00
if ( mDebugging )
{
NOP ;
}
2019-08-23 11:56:54 -07:00
union IntUnion
{
int64 mInt64 ;
int32 mInt32 ;
int16 mInt16 ;
int8 mInt8 ;
} ;
bool isValRegSet = false ;
int64 curVal = 0 ;
auto _EmitMovR11 = [ & ] ( int64 val )
{
int64 prevVal = curVal ;
bool wasValRegSet = isValRegSet ;
isValRegSet = true ;
curVal = val ;
if ( ( wasValRegSet ) & & ( val = = prevVal ) )
return ;
if ( val = = 0 )
{
// xor r11, r11
Emit ( 0x4D ) ; Emit ( 0x31 ) ; Emit ( 0xDB ) ;
return ;
}
if ( wasValRegSet )
{
if ( ( val & ~ 0xFF ) = = ( prevVal & ~ 0xFF ) )
{
// mov r11b, val8
Emit ( 0x41 ) ; Emit ( 0xB3 ) ;
Emit ( ( int8 ) val ) ;
return ;
}
if ( ( val & ~ 0xFFFF ) = = ( prevVal & ~ 0xFFFF ) )
{
// mov r11w, val16
Emit ( 0x66 ) ; Emit ( 0x41 ) ; Emit ( 0xBB ) ;
mOut . Write ( ( int16 ) val ) ;
return ;
}
}
if ( ( val > = 0 ) & & ( val < = 0x7FFFFFFF ) )
{
// mov r11d, val32
Emit ( 0x41 ) ; Emit ( 0xBB ) ;
mOut . Write ( ( int32 ) val ) ;
}
else if ( ( val > = - 0x80000000LL ) & & ( val < = 0x7FFFFFFF ) )
{
// mov r11, val32 (sign extended)
Emit ( 0x49 ) ; Emit ( 0xC7 ) ; Emit ( 0xC3 ) ;
mOut . Write ( ( int32 ) val ) ;
}
else
{
// movabs r11, val32
Emit ( 0x49 ) ; Emit ( 0xBB ) ;
mOut . Write ( ( int64 ) val ) ;
}
} ;
2019-08-27 08:04:41 -07:00
for ( ; curOfs < = memSize - 8 ; )
2019-08-23 11:56:54 -07:00
{
if ( allowRep )
{
uint8 val = dataVec [ curOfs ] ;
int repSize = 0 ;
int checkOfs = curOfs ;
while ( checkOfs < ( int ) dataVec . size ( ) )
{
if ( dataVec [ checkOfs ] ! = val )
break ;
checkOfs + + ;
repSize + + ;
}
if ( repSize > = 16 )
{
2019-08-27 08:04:41 -07:00
bool regSaved = false ;
if ( ( regA = = X64Reg_RAX ) | |
( regA = = X64Reg_RCX ) | |
( regA = = X64Reg_RDI ) )
{
BF_ASSERT ( regB = = X64Reg_None ) ;
// mov R11, regA
Emit ( 0x49 ) ; Emit ( 0x89 ) ;
EmitModRM ( BeMCOperand : : FromReg ( regA ) , BeMCOperand : : FromReg ( X64Reg_R11 ) ) ;
regSaved = true ;
}
2019-08-23 11:56:54 -07:00
// lea rdi, <dest+curOfs>
EmitREX ( BeMCOperand : : FromReg ( X64Reg_RDI ) , dest , true ) ;
Emit ( 0x8D ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_RDI ) , regA , regB , 1 , disp + curOfs ) ;
2019-08-27 08:04:41 -07:00
// mov al, <val>
Emit ( 0xB0 ) ; Emit ( val ) ;
2019-08-23 11:56:54 -07:00
// mov edx, <repSize>
Emit ( 0xB9 ) ;
mOut . Write ( ( int32 ) repSize ) ;
// rep stosb
Emit ( 0xF3 ) ; Emit ( 0xAA ) ;
2019-08-27 08:04:41 -07:00
if ( regSaved )
{
// mov regA, R11
Emit ( 0x4C ) ; Emit ( 0x89 ) ;
EmitModRM ( BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand : : FromReg ( regA ) ) ;
}
2019-08-23 11:56:54 -07:00
curOfs + = repSize ;
continue ;
}
}
int64 newValReg = * ( ( int64 * ) & dataVec [ curOfs ] ) ;
_EmitMovR11 ( newValReg ) ;
// mov <dest+curOfs>, R11
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11 ) , dest , true ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11 ) , regA , regB , 1 , disp + curOfs ) ;
2019-08-27 08:04:41 -07:00
curOfs + = 8 ;
2019-08-23 11:56:54 -07:00
}
for ( ; curOfs < = memSize - 4 ; curOfs + = 4 )
{
int64 newValReg = curVal ;
* ( ( int32 * ) & newValReg ) = * ( ( int32 * ) & dataVec [ curOfs ] ) ;
_EmitMovR11 ( newValReg ) ;
// mov <dest+curOfs>, R11d
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11D ) , dest , false ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11D ) , regA , regB , 1 , disp + curOfs ) ;
}
for ( ; curOfs < = memSize - 2 ; curOfs + = 2 )
{
int64 newValReg = curVal ;
* ( ( int16 * ) & newValReg ) = * ( ( int16 * ) & dataVec [ curOfs ] ) ;
_EmitMovR11 ( newValReg ) ;
// mov <dest+curOfs>, R11w
Emit ( 0x66 ) ; EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11W ) , dest , false ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11W ) , regA , regB , 1 , disp + curOfs ) ;
}
for ( ; curOfs < = memSize - 1 ; curOfs + = 1 )
{
int64 newValReg = curVal ;
* ( ( int8 * ) & newValReg ) = * ( ( int8 * ) & dataVec [ curOfs ] ) ;
_EmitMovR11 ( newValReg ) ;
// mov <dest+curOfs>, R11b
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11B ) , dest , false ) ;
Emit ( 0x89 - 1 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11B ) , regA , regB , 1 , disp + curOfs ) ;
}
}
void BeMCContext : : DoCodeEmission ( )
{
BP_ZONE ( " BeMCContext::DoCodeEmission " ) ;
struct BeMCJump
{
public :
int mCodeOffset ;
int mLabelIdx ;
int mJumpKind ; // 0 = rel8, 1 = rel16 (not valid in x64), 2 = rel32
BeCmpKind mCmpKind ;
public :
BeMCJump ( )
{
mCmpKind = BeCmpKind_None ;
mCodeOffset = - 1 ;
mLabelIdx = - 1 ;
mJumpKind = - 1 ;
}
} ;
SizedArray < int , 64 > labelPositions ;
labelPositions . resize ( mCurLabelIdx ) ;
for ( int labelIdx = 0 ; labelIdx < mCurLabelIdx ; labelIdx + + )
labelPositions [ labelIdx ] = - 1 ;
SizedArray < BeMCJump , 64 > deferredJumps ;
auto & xdata = mCOFFObject - > mXDataSect . mData ;
int xdataStartPos = xdata . GetPos ( ) ;
int textSectStartPos = mOut . GetPos ( ) ;
int lastLabelIdx = - 1 ;
bool hasPData = mStackSize ! = 0 ;
if ( hasPData )
{
// xdata must be DWORD aligned
while ( ( xdataStartPos & 3 ) ! = 0 )
{
xdata . Write ( ( uint8 ) 0 ) ;
xdataStartPos + + ;
}
// pdata starting loc
BeMCRelocation reloc ;
reloc . mKind = BeMCRelocationKind_ADDR32NB ;
reloc . mOffset = mCOFFObject - > mPDataSect . mData . GetPos ( ) ;
reloc . mSymTableIdx = mCOFFObject - > GetSymbol ( mBeFunction ) - > mIdx ;
mCOFFObject - > mPDataSect . mRelocs . push_back ( reloc ) ;
mCOFFObject - > mPDataSect . mData . Write ( ( int32 ) 0 ) ;
xdata . Write ( ( uint8 ) 1 ) ; // version
xdata . Write ( ( uint8 ) 0 ) ; // prolog size (placeholder)
xdata . Write ( ( uint8 ) 0 ) ; // num unwind codes (placeholder)
if ( mUseBP )
{
int frameReg = 5 ;
int offset = 0 ; // scaled by 16
xdata . Write ( ( uint8 ) ( frameReg + ( offset < < 4 ) ) ) ; // frame register
}
else
xdata . Write ( ( uint8 ) 0 ) ; // frame register
}
struct BeMCDeferredUnwind
{
BeMCInst * mUnwindInst ;
int mCodePos ;
} ;
struct BeDbgInstPos
{
int mPos ;
int mOrigPos ;
BeMCInst * mInst ;
} ;
SizedArray < BeDbgInstPos , 64 > dbgInstPositions ;
String dbgStr ;
if ( mDebugging )
{
dbgStr + = " DoCodeEmission: \n " ;
}
SizedArray < BeMCDeferredUnwind , 64 > deferredUnwinds ;
mCurDbgLoc = NULL ;
bool needsInstructionAtDbgLoc = false ;
bool dbgExtendLifetime = false ;
int hotJumpLen = 5 ;
int funcCodePos = 0 ;
BeVTrackingList * vregsLive = mLivenessContext . AllocEmptyList ( ) ;
BeVTrackingList * vregsInitialized = mVRegInitializedContext . AllocEmptyList ( ) ;
for ( auto mcBlock : mBlocks )
{
for ( auto inst : mcBlock - > mInstructions )
{
if ( mDebugging )
{
ToString ( inst , dbgStr , true , true ) ;
}
auto checkInst = inst ;
int newFuncCodePos = mOut . GetPos ( ) - textSectStartPos ;
if ( newFuncCodePos ! = funcCodePos )
{
funcCodePos = newFuncCodePos ;
if ( dbgExtendLifetime )
{
dbgExtendLifetime = false ;
if ( mDebugging )
{
//
}
}
}
if ( ( inst - > mKind = = BeMCInstKind_LifetimeEnd ) | | ( inst - > mKind = = BeMCInstKind_LifetimeExtend ) )
{
if ( needsInstructionAtDbgLoc )
{
// This 'dbgExtendLifetime' is useful in cases like
// repeat { int a = 123; } while (false);
// Otherwise 'a' wouldn't be viewable
dbgExtendLifetime = true ;
}
}
switch ( inst - > mKind )
{
case BeMCInstKind_Label :
//Why did we set this FALSE here? Just because we have a label doesn't mean we've emitted any actual instructions....
// Leaving this out broke a test where we couldn't view a 'this' at the end of a particular method
//dbgExtendLifetime = false;
break ;
}
if ( ( inst - > mDbgLoc ! = NULL ) & & ( mDbgFunction ! = NULL ) )
{
if ( inst - > mDbgLoc ! = mCurDbgLoc )
{
bool allowEmission = true ;
if ( inst - > mKind = = BeMCInstKind_DbgDecl )
{
// We need to separate out dbgDecls if we are attempting to extend a previous one,
// otherwise wait for a real instruction
if ( dbgExtendLifetime )
{
mOut . Write ( ( uint8 ) 0x90 ) ;
funcCodePos + + ;
needsInstructionAtDbgLoc = false ;
dbgExtendLifetime = false ;
}
else
allowEmission = false ;
}
else
{
mCurDbgLoc = inst - > mDbgLoc ;
if ( ( ! mDbgFunction - > mEmissions . empty ( ) ) & & ( mDbgFunction - > mEmissions . back ( ) . mPos = = funcCodePos ) )
{
bool hadEmission = false ;
if ( needsInstructionAtDbgLoc )
{
// We only need an instruction on this LINE, not necessarily the same column or lexical scope
auto prevDbgLoc = mDbgFunction - > mEmissions . back ( ) . mDbgLoc ;
if ( ( prevDbgLoc - > mLine ! = mCurDbgLoc - > mLine ) | |
( prevDbgLoc - > GetDbgFile ( ) ! = mCurDbgLoc - > GetDbgFile ( ) ) | |
( ( prevDbgLoc - > mColumn ! = - 1 ) ! = ( mCurDbgLoc - > mColumn ! = - 1 ) ) )
{
// We didn't emit anything so we need to put a NOP in
mOut . Write ( ( uint8 ) 0x90 ) ;
funcCodePos + + ;
needsInstructionAtDbgLoc = false ;
hadEmission = true ;
dbgExtendLifetime = false ;
}
}
if ( ! hadEmission )
{
// No code emitted, take back the last codeEmission
mDbgFunction - > mEmissions . pop_back ( ) ;
}
}
}
if ( allowEmission )
{
BeDbgCodeEmission codeEmission ;
codeEmission . mDbgLoc = mCurDbgLoc ;
codeEmission . mPos = funcCodePos ;
mDbgFunction - > mEmissions . push_back ( codeEmission ) ;
}
}
}
BeMCJump jump ;
auto instForm = GetInstForm ( inst ) ;
// Generate gaps
if ( ( inst - > mVRegsInitialized ! = NULL ) & & ( vregsInitialized ! = inst - > mVRegsInitialized ) )
{
for ( int changeIdx = 0 ; changeIdx < inst - > mVRegsInitialized - > mNumChanges ; changeIdx + + )
{
int vregInitializedIdx = inst - > mVRegsInitialized - > GetChange ( changeIdx ) ;
if ( vregInitializedIdx > = 0 ) // Added
{
if ( vregInitializedIdx > = mVRegInitializedContext . mNumItems )
continue ;
auto dbgVar = mVRegInfo [ vregInitializedIdx ] - > mDbgVariable ;
if ( ( dbgVar ! = NULL ) & & ( ! dbgVar - > mDbgLifeEnded ) )
{
// Wasn't initialized, now it is
if ( ! dbgVar - > mGaps . empty ( ) )
{
auto & range = dbgVar - > mGaps . back ( ) ;
if ( dbgVar - > mDeclLifetimeExtend )
{
range . mOffset + + ;
dbgVar - > mDeclEnd = range . mOffset ;
dbgVar - > mDeclLifetimeExtend = false ;
if ( mDebugging )
{
dbgStr + = StrFormat ( " #### Dbg Applied LifetimeExtend %s \n " , dbgVar - > mName . c_str ( ) ) ;
}
}
// We have to check for this because it's possible we get multiple adds
if ( range . mLength = = - 1 )
{
range . mLength = funcCodePos - range . mOffset ;
if ( range . mLength < = 0 )
{
dbgVar - > mGaps . pop_back ( ) ;
}
}
if ( mDebugging )
{
dbgStr + = StrFormat ( " #### Dbg End Gap %s \n " , dbgVar - > mName . c_str ( ) ) ;
}
}
}
}
else // Removed
{
vregInitializedIdx = - vregInitializedIdx - 1 ;
if ( vregInitializedIdx > = mVRegInitializedContext . mNumItems )
continue ;
auto dbgVar = mVRegInfo [ vregInitializedIdx ] - > mDbgVariable ;
if ( ( dbgVar ! = NULL ) & & ( ! dbgVar - > mDbgLifeEnded ) )
{
bool inGap = false ;
if ( ! dbgVar - > mGaps . empty ( ) )
inGap = dbgVar - > mGaps . back ( ) . mLength = = - 1 ;
if ( ! inGap )
{
if ( mDebugging )
{
dbgStr + = StrFormat ( " #### Dbg Start Gap %s \n " , dbgVar - > mName . c_str ( ) ) ;
}
// Starting a new gap
BeDbgVariableRange range ;
range . mOffset = funcCodePos ;
range . mLength = - 1 ;
dbgVar - > mGaps . push_back ( range ) ;
}
}
}
}
vregsInitialized = inst - > mVRegsInitialized ;
}
// Finish range for variables exiting liveness. Since liveness is built in end-to-start order,
// an "add" means that the NEXT instruction won't have this entry (if the next entry doesn't share
// the exact liveness value)
if ( ( inst - > mLiveness ! = NULL ) & & ( inst - > mLiveness ! = vregsLive ) )
{
for ( int changeIdx = 0 ; changeIdx < vregsLive - > mNumChanges ; changeIdx + + )
{
int vregLiveIdx = vregsLive - > GetChange ( changeIdx ) ;
if ( vregLiveIdx < 0 )
continue ;
// Check for both the 'exists' flag changing and the 'in scope' flag changing
int vregIdx = vregLiveIdx % mLivenessContext . mNumItems ;
auto dbgVar = mVRegInfo [ vregIdx ] - > mDbgVariable ;
if ( ( dbgVar ! = NULL ) & & ( ! dbgVar - > mDbgLifeEnded ) )
{
//if (!mLivenessContext.IsSet(inst->mLiveness, vregIdx))
{
if ( ! dbgVar - > mGaps . empty ( ) )
{
auto & lastGap = dbgVar - > mGaps . back ( ) ;
if ( lastGap . mLength = = - 1 )
{
// Unused gap
//dbgVar->mGaps.pop_back();
// Don't remove this 'unused gap' because the variable may come back alive
// This can happen in blocks after NoReturn calls
}
}
if ( dbgVar - > mDeclStart ! = - 1 )
{
dbgVar - > mDeclEnd = funcCodePos ;
if ( mDebugging )
{
dbgStr + = StrFormat ( " #### Dbg Setting DeclEnd %s \n " , dbgVar - > mName . c_str ( ) ) ;
}
}
if ( ( mDebugging ) & & ( dbgExtendLifetime ) )
{
dbgStr + = StrFormat ( " #### Dbg Setting LifetimeExtend %s \n " , dbgVar - > mName . c_str ( ) ) ;
}
dbgVar - > mDeclLifetimeExtend = dbgExtendLifetime ;
//if (dbgExtendLifetime)
//dbgVar->mDeclEnd++;
}
}
}
vregsLive = inst - > mLiveness ;
}
if ( mDebugging )
{
BeDbgInstPos dbgInstPos = { funcCodePos , funcCodePos , inst } ;
dbgInstPositions . push_back ( dbgInstPos ) ;
}
switch ( inst - > mKind )
{
case BeMCInstKind_DbgDecl :
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
auto dbgVar = vregInfo - > mDbgVariable ;
dbgVar - > mDeclStart = funcCodePos ;
while ( vregInfo - > IsDirectRelTo ( ) )
{
if ( vregInfo - > mRelTo . IsNativeReg ( ) )
{
break ;
}
else
vregInfo = GetVRegInfo ( vregInfo - > mRelTo ) ;
}
BeMCOperand defArg = inst - > mArg0 ;
if ( ( vregInfo - > mIsRetVal ) & & ( mCompositeRetVRegIdx ! = - 1 ) & & ( mCompositeRetVRegIdx ! = inst - > mArg0 . mVRegIdx ) )
{
defArg = BeMCOperand : : FromVReg ( mCompositeRetVRegIdx ) ;
if ( ( inst - > mArg0 . mKind = = BeMCOperandKind_VReg ) & & ( vregInfo - > mType - > IsComposite ( ) ) )
defArg . mKind = BeMCOperandKind_VRegLoad ;
vregInfo = mVRegInfo [ mCompositeRetVRegIdx ] ;
}
if ( vregInfo - > mReg ! = X64Reg_None )
{
if ( ( defArg . mKind = = BeMCOperandKind_VRegAddr ) | | ( dbgVar - > mIsValue ) )
dbgVar - > mPrimaryLoc . mKind = BeDbgVariableLoc : : Kind_Reg ;
else
dbgVar - > mPrimaryLoc . mKind = BeDbgVariableLoc : : Kind_Indexed ;
dbgVar - > mPrimaryLoc . mReg = vregInfo - > mReg ;
dbgVar - > mPrimaryLoc . mOfs = 0 ;
}
else if ( vregInfo - > mRelTo . IsNativeReg ( ) )
{
if ( ( defArg . mKind = = BeMCOperandKind_VRegAddr ) | | ( dbgVar - > mIsValue ) )
dbgVar - > mPrimaryLoc . mKind = BeDbgVariableLoc : : Kind_Reg ;
else
dbgVar - > mPrimaryLoc . mKind = BeDbgVariableLoc : : Kind_Indexed ;
dbgVar - > mPrimaryLoc . mReg = vregInfo - > mRelTo . mReg ;
if ( vregInfo - > mRelOffset . IsImmediateInt ( ) )
{
dbgVar - > mPrimaryLoc . mOfs = vregInfo - > mRelOffset . mImmediate ;
}
else
BF_ASSERT ( ! vregInfo - > mRelOffset ) ;
}
else if ( vregInfo - > mRelTo . mKind = = BeMCOperandKind_SymbolAddr )
{
Fail ( " Not supported SymbolAddr " ) ;
//dbgVar->mPrimaryLoc.mKind = BeDbgVariableLoc::Kind_SymbolAddr;
//dbgVar->mPrimaryLoc.mOfs = vregInfo->mRelTo.mSymbolIdx;
}
if ( ( dbgVar - > mPrimaryLoc . mKind = = BeDbgVariableLoc : : Kind_None ) & & ( vregInfo - > mType - > mSize ! = 0 ) )
{
dbgVar - > mPrimaryLoc . mKind = BeDbgVariableLoc : : Kind_Indexed ;
GetValAddr ( BeMCOperand : : FromVRegAddr ( inst - > mArg0 . mVRegIdx ) , dbgVar - > mPrimaryLoc . mReg , dbgVar - > mPrimaryLoc . mOfs ) ;
}
if ( ! mVRegInitializedContext . IsSet ( vregsInitialized , inst - > mArg0 . mVRegIdx ) )
{
bool inGap = false ;
if ( ! dbgVar - > mGaps . empty ( ) )
inGap = dbgVar - > mGaps . back ( ) . mLength = = - 1 ;
if ( ! inGap )
{
if ( mDebugging )
{
dbgStr + = StrFormat ( " Dbg Starting Gap %s \n " , dbgVar - > mName . c_str ( ) ) ;
}
BeDbgVariableRange range ;
range . mOffset = funcCodePos ;
range . mLength = - 1 ;
dbgVar - > mGaps . push_back ( range ) ;
}
}
if ( vregInfo - > mVolatileVRegSave ! = - 1 )
{
auto savedVRegInfo = mVRegInfo [ vregInfo - > mVolatileVRegSave ] ;
dbgVar - > mSavedLoc . mKind = BeDbgVariableLoc : : Kind_Indexed ;
GetValAddr ( BeMCOperand : : FromVRegAddr ( vregInfo - > mVolatileVRegSave ) , dbgVar - > mSavedLoc . mReg , dbgVar - > mSavedLoc . mOfs ) ;
}
}
break ;
case BeMCInstKind_DbgRangeStart :
{
}
break ;
case BeMCInstKind_DbgRangeEnd :
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( vregInfo - > mDbgVariable ! = NULL )
{
auto dbgVar = vregInfo - > mDbgVariable ;
dbgVar - > mDeclEnd = funcCodePos ;
dbgVar - > mDeclLifetimeExtend = false ;
BF_ASSERT ( ( uint ) dbgVar - > mDeclEnd > = ( uint ) dbgVar - > mDeclStart ) ;
}
}
break ;
case BeMCInstKind_LifetimeExtend :
break ;
case BeMCInstKind_LifetimeStart :
break ;
case BeMCInstKind_LifetimeEnd :
break ;
case BeMCInstKind_ValueScopeSoftEnd :
break ;
case BeMCInstKind_ValueScopeHardEnd :
break ;
case BeMCInstKind_MemSet :
{
// This doesn't currently attempt to align properly
int memSize = inst - > mArg0 . mMemSetInfo . mSize ;
uint8 memValue = inst - > mArg0 . mMemSetInfo . mValue ;
int curOfs = 0 ;
X64CPURegister destReg = X64Reg_R10 ;
int destOfs = 0 ;
if ( inst - > mArg1 . IsNativeReg ( ) )
{
destReg = inst - > mArg1 . mReg ;
}
else if ( inst - > mArg1 )
{
//BF_ASSERT(inst->mArg1.mKind == BeMCOperandKind_VReg);
GetValAddr ( inst - > mArg1 , destReg , destOfs ) ;
}
if ( memValue = = 0 )
{
// xor r11, r11
Emit ( 0x4D ) ; Emit ( 0x31 ) ; Emit ( 0xDB ) ;
}
else
{
// mov r11, memValue*8
Emit ( 0x49 ) ; Emit ( 0xBB ) ;
for ( int i = 0 ; i < 8 ; i + + )
Emit ( memValue ) ;
}
for ( ; curOfs < = memSize - 8 ; curOfs + = 8 )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand : : FromReg ( destReg ) , true ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11 ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
// If there's more that one 'small' write required then we just do another 64-bit move
// which overlaps partially into the previous 64-bit move
if ( ( memSize > 8 ) & & ( curOfs < memSize ) & & ( memSize - curOfs ! = 4 ) & &
( memSize - curOfs ! = 2 ) & & ( memSize - curOfs ! = 1 ) )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand : : FromReg ( destReg ) , true ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11 ) , destReg , X64Reg_None , 1 , ( memSize - 8 ) + destOfs ) ;
curOfs = memSize ;
}
for ( ; curOfs < = memSize - 4 ; curOfs + = 4 )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11D ) , BeMCOperand : : FromReg ( destReg ) , false ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11D ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
for ( ; curOfs < = memSize - 2 ; curOfs + = 2 )
{
Emit ( 0x66 ) ; EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11W ) , BeMCOperand : : FromReg ( destReg ) , false ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11W ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
for ( ; curOfs < = memSize - 1 ; curOfs + = 1 )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11B ) , BeMCOperand : : FromReg ( destReg ) , false ) ; //-V530
Emit ( 0x89 - 1 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11B ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
}
break ;
case BeMCInstKind_MemCpy :
{
// This doesn't currently attempt to align properly
int memSize = inst - > mArg0 . mMemCpyInfo . mSize ;
int curOfs = 0 ;
X64CPURegister destReg = X64Reg_R10 ;
X64CPURegister srcReg = X64Reg_R11 ;
int destOfs = 0 ;
int srcOfs = 0 ;
if ( inst - > mArg1 )
{
BF_ASSERT ( inst - > mArg1 . mKind = = BeMCOperandKind_VRegPair ) ;
GetValAddr ( BeMCOperand : : FromEncoded ( inst - > mArg1 . mVRegPair . mVRegIdx0 ) , destReg , destOfs ) ;
GetValAddr ( BeMCOperand : : FromEncoded ( inst - > mArg1 . mVRegPair . mVRegIdx1 ) , srcReg , srcOfs ) ;
}
if ( ( srcReg = = destReg ) & & ( srcOfs = = destOfs ) )
break ;
for ( ; curOfs < = memSize - 8 ; curOfs + = 8 )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand : : FromReg ( srcReg ) , true ) ;
Emit ( 0x8B ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11 ) , srcReg , X64Reg_None , 1 , curOfs + srcOfs ) ;
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand : : FromReg ( destReg ) , true ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11 ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
// If there's more that one 'small' write required then we just do another 64-bit move
// which overlaps partially into the previous 64-bit move
if ( ( memSize > 8 ) & & ( curOfs < memSize ) & & ( memSize - curOfs ! = 4 ) & &
( memSize - curOfs ! = 2 ) & & ( memSize - curOfs ! = 1 ) )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand : : FromReg ( srcReg ) , true ) ;
Emit ( 0x8B ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11 ) , srcReg , X64Reg_None , 1 , ( memSize - 8 ) + srcOfs ) ;
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11 ) , BeMCOperand : : FromReg ( destReg ) , true ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11 ) , destReg , X64Reg_None , 1 , ( memSize - 8 ) + destOfs ) ;
curOfs = memSize ;
}
for ( ; curOfs < = memSize - 4 ; curOfs + = 4 )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11D ) , BeMCOperand : : FromReg ( srcReg ) , false ) ;
Emit ( 0x8B ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11D ) , srcReg , X64Reg_None , 1 , curOfs + srcOfs ) ;
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11D ) , BeMCOperand : : FromReg ( destReg ) , false ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11D ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
for ( ; curOfs < = memSize - 2 ; curOfs + = 2 )
{
Emit ( 0x66 ) ; EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11W ) , BeMCOperand : : FromReg ( srcReg ) , false ) ;
Emit ( 0x8B ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11W ) , srcReg , X64Reg_None , 1 , curOfs + srcOfs ) ;
Emit ( 0x66 ) ; EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11W ) , BeMCOperand : : FromReg ( destReg ) , false ) ;
Emit ( 0x89 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11W ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
for ( ; curOfs < = memSize - 1 ; curOfs + = 1 )
{
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11B ) , BeMCOperand : : FromReg ( srcReg ) , false ) ;
Emit ( 0x8B - 1 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11B ) , srcReg , X64Reg_None , 1 , curOfs + srcOfs ) ;
EmitREX ( BeMCOperand : : FromReg ( X64Reg_R11B ) , BeMCOperand : : FromReg ( destReg ) , false ) ;
Emit ( 0x89 - 1 ) ;
EmitModRMRel ( EncodeRegNum ( X64Reg_R11B ) , destReg , X64Reg_None , 1 , curOfs + destOfs ) ;
}
}
break ;
case BeMCInstKind_FastCheckStack :
{
// CMP RSP, [RSP]
BF_ASSERT ( inst - > mArg0 = = BeMCOperand : : FromReg ( X64Reg_RSP ) ) ;
Emit ( 0x48 ) ;
Emit ( 0x3B ) ;
Emit ( 0x24 ) ;
Emit ( 0x24 ) ;
}
break ;
case BeMCInstKind_TLSSetup :
{
if ( inst - > mArg1 ) // Alt
{
BF_ASSERT ( inst - > mArg0 . IsNativeReg ( ) ) ;
// mov arg0, qword ptr gs:[0x58]
Emit ( 0x65 ) ;
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , true ) ;
Emit ( 0x8B ) ; Emit ( 0x04 | ( EncodeRegNum ( inst - > mArg0 . mReg ) < < 3 ) ) ; Emit ( 0x25 ) ;
mOut . Write ( ( int32 ) 0x58 ) ;
}
else
{
// mov eax, _tls_index
Emit ( 0x8B ) ; Emit ( 0x05 ) ;
auto sym = mCOFFObject - > GetSymbolRef ( " _tls_index " ) ;
BeMCRelocation reloc ;
reloc . mKind = BeMCRelocationKind_REL32 ;
reloc . mOffset = mOut . GetPos ( ) ;
reloc . mSymTableIdx = sym - > mIdx ;
mCOFFObject - > mTextSect . mRelocs . push_back ( reloc ) ;
mTextRelocs . push_back ( ( int ) mCOFFObject - > mTextSect . mRelocs . size ( ) - 1 ) ;
mOut . Write ( ( int32 ) 0 ) ;
auto vregInfo = mVRegInfo [ mTLSVRegIdx ] ;
BF_ASSERT ( vregInfo - > mReg ! = X64Reg_None ) ;
auto tlsOperand = BeMCOperand : : FromReg ( vregInfo - > mReg ) ;
// mov tlsReg, qword ptr gs:[0x58]
Emit ( 0x65 ) ;
EmitREX ( tlsOperand , BeMCOperand ( ) , true ) ;
Emit ( 0x8B ) ; Emit ( 0x04 | ( EncodeRegNum ( vregInfo - > mReg ) < < 3 ) ) ; Emit ( 0x25 ) ;
mOut . Write ( ( int32 ) 0x58 ) ;
// mov tlsReg, qword ptr [tlsReg + 8*rax]
EmitREX ( tlsOperand , tlsOperand , true ) ;
Emit ( 0x8B ) ;
EmitModRMRel ( EncodeRegNum ( vregInfo - > mReg ) , vregInfo - > mReg , X64Reg_RAX , 8 , 0 ) ;
}
}
break ;
case BeMCInstKind_PreserveVolatiles :
{
for ( int vregIdx : * inst - > mLiveness )
{
if ( vregIdx > = mLivenessContext . mNumItems )
continue ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg ! = vregInfo - > mReg ) )
continue ;
if ( vregInfo - > mVolatileVRegSave ! = - 1 )
{
if ( vregInfo - > mDbgVariable ! = NULL )
{
BeDbgVariableRange range ;
range . mOffset = funcCodePos ;
range . mLength = - 1 ;
vregInfo - > mDbgVariable - > mSavedRanges . push_back ( range ) ;
}
}
}
}
break ;
case BeMCInstKind_RestoreVolatiles :
{
for ( int vregIdx : * inst - > mLiveness )
{
if ( vregIdx > = mLivenessContext . mNumItems )
continue ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg ! = vregInfo - > mReg ) )
continue ;
if ( vregInfo - > mVolatileVRegSave ! = - 1 )
{
if ( ( vregInfo - > mDbgVariable ! = NULL ) & & ( ! vregInfo - > mDbgVariable - > mSavedRanges . empty ( ) ) )
{
BeDbgVariableRange & range = vregInfo - > mDbgVariable - > mSavedRanges . back ( ) ;
range . mLength = funcCodePos - range . mOffset ;
bool handled = false ;
if ( ! vregInfo - > mDbgVariable - > mGaps . empty ( ) )
{
auto & lastGap = vregInfo - > mDbgVariable - > mGaps . back ( ) ;
if ( lastGap . mLength = = - 1 )
{
lastGap . mLength = funcCodePos - lastGap . mOffset ;
handled = true ;
}
}
if ( ! handled )
{
BeDbgVariableRange gapRange = range ;
// gapRange.mOffset++;
// gapRange.mLength--;
vregInfo - > mDbgVariable - > mGaps . push_back ( gapRange ) ;
}
}
}
}
}
break ;
case BeMCInstKind_None :
break ;
case BeMCInstKind_Unwind_Alloc :
case BeMCInstKind_Unwind_PushReg :
case BeMCInstKind_Unwind_SaveXMM :
case BeMCInstKind_Unwind_SetBP :
{
BeMCDeferredUnwind deferredUnwind = { inst , funcCodePos } ;
deferredUnwinds . push_back ( deferredUnwind ) ;
}
break ;
case BeMCInstKind_Label :
while ( funcCodePos < hotJumpLen )
{
Emit ( 0x90 ) ;
funcCodePos + + ;
}
lastLabelIdx = inst - > mArg0 . mLabelIdx ;
labelPositions [ inst - > mArg0 . mLabelIdx ] = funcCodePos ;
// This ensures we can't jump back into the hot jump area
break ;
case BeMCInstKind_Nop :
Emit ( 0x90 ) ;
break ;
case BeMCInstKind_Unreachable :
// ud2
Emit ( 0x0F ) ; Emit ( 0x0B ) ;
break ;
case BeMCInstKind_EnsureInstructionAt :
needsInstructionAtDbgLoc = true ;
/*if (mDbgFunction != NULL)
{
// If this is true, this means that our instruction was the first one encountered at this mDbgPos,
// so we emit a NOP so we can step onto this line
if ( mDbgFunction - > mEmissions . back ( ) . mPos = = funcCodePos )
{
mOut . Write ( ( uint8 ) 0x90 ) ;
}
} */
break ;
case BeMCInstKind_DbgBreak :
Emit ( 0xCC ) ;
break ;
case BeMCInstKind_MFence :
Emit ( 0x0F ) ; Emit ( 0xAE ) ; Emit ( 0xF0 ) ;
break ;
case BeMCInstKind_Mov :
{
if ( inst - > mArg1 . mKind = = BeMCOperandKind_ConstAgg )
{
EmitAggMov ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
}
auto vregInfo = GetVRegInfo ( inst - > mArg1 ) ;
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
if ( instForm = = BeMCInstForm_Unknown )
{
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsNativeReg ( ) ) )
{
if ( ( arg0Type - > IsInt ( ) ) & & ( arg0Type - > mSize < arg1Type - > mSize ) )
{
// Reduce arg1 to arg0 size
BeMCOperand mcDest ;
BeMCOperand mcSrc = BeMCOperand : : FromReg ( ResizeRegister ( inst - > mArg1 . mReg , arg0Type - > mSize ) ) ;
BeTypeCode useTypeCode = BeTypeCode_None ;
if ( mcSrc . mReg ! = X64Reg_None )
{
// Reduction worked
mcDest = inst - > mArg0 ;
useTypeCode = arg0Type - > mTypeCode ;
}
else
{
Fail ( " Error in Mov " ) ;
}
switch ( useTypeCode )
{
case BeTypeCode_Int8 :
EmitREX ( mcDest , mcSrc , false ) ;
Emit ( 0x8A ) ; EmitModRM ( mcDest , mcSrc ) ;
break ;
case BeTypeCode_Int16 : Emit ( 0x66 ) ; // Fallthrough
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( mcDest , mcSrc , arg0Type - > mTypeCode = = BeTypeCode_Int64 ) ;
Emit ( 0x8B ) ; EmitModRM ( mcDest , mcSrc ) ;
break ;
default : NotImpl ( ) ;
}
break ;
}
}
if ( inst - > mArg0 . IsNativeReg ( ) )
{
BF_ASSERT ( arg0Type - > IsIntable ( ) ) ;
BF_ASSERT ( arg1Type - > IsIntable ( ) ) ;
BF_ASSERT ( arg0Type - > mSize > arg1Type - > mSize ) ;
// Zero extend 'dest' to 'src', emit movzx
switch ( arg0Type - > mTypeCode )
{
case BeTypeCode_Int16 :
BF_ASSERT ( arg1Type - > mTypeCode = = BeTypeCode_Int8 ) ;
Emit ( 0x66 ) ;
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xB6 ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int32 :
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Int8 :
case BeTypeCode_Boolean :
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xB6 ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int16 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xB7 ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
default : NotImpl ( ) ;
}
break ;
case BeTypeCode_Int64 :
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Int8 :
case BeTypeCode_Boolean :
EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
Emit ( 0x0F ) ; Emit ( 0xB6 ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ; break ;
case BeTypeCode_Int16 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
Emit ( 0x0F ) ; Emit ( 0xB7 ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ; break ;
case BeTypeCode_Int32 :
{
// We convert from a "mov r64, r32" to a "mov r32, r32", which forces high 32 bits of original r64 to zero
BeMCOperand mcDest = BeMCOperand : : FromReg ( ResizeRegister ( inst - > mArg0 . mReg , arg1Type - > mSize ) ) ;
EmitREX ( mcDest , inst - > mArg1 , false ) ;
Emit ( 0x8B ) ; EmitModRM ( mcDest , inst - > mArg1 ) ;
}
break ;
}
break ;
default :
NotImpl ( ) ;
}
break ; // from inst switch
}
if ( arg0Type - > mTypeCode = = BeTypeCode_Float )
{
if ( inst - > mArg1 . IsImmediate ( ) )
{
// Emit as a int32 mov
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0xC7 ) ;
EmitModRM ( 0 , inst - > mArg0 , - 4 ) ;
float val = inst - > mArg1 . mImmFloat ;
mOut . Write ( * ( int32 * ) & val ) ;
break ;
}
}
}
if ( inst - > mArg1 . mKind = = BeMCOperandKind_CmpResult )
{
BF_ASSERT ( instForm = = BeMCInstForm_R8_RM8 ) ;
// SETcc
auto & cmpResult = mCmpResults [ inst - > mArg1 . mCmpResultIdx ] ;
uint8 opCode = GetJumpOpCode ( cmpResult . mCmpKind , false ) + 0x20 ;
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0x0F ) ;
Emit ( opCode ) ;
EmitModRM ( 0 , inst - > mArg0 ) ;
break ;
}
bool isLEA = inst - > mArg1 . mKind = = BeMCOperandKind_SymbolAddr ;
if ( ( instForm = = BeMCInstForm_R64_RM64 ) & & ( inst - > mArg1 . IsVRegAny ( ) ) )
{
bool isMulti = false ;
if ( GetRMForm ( inst - > mArg1 , isMulti ) = = BeMCRMMode_Direct )
{
// LEA
auto modInst = * inst ;
BF_ASSERT ( modInst . mArg1 . IsVRegAny ( ) ) ;
modInst . mArg1 = BeMCOperand : : ToLoad ( modInst . mArg1 ) ;
EmitInst ( BeMCInstForm_R64_RM64 , 0x8D , & modInst ) ;
break ;
}
else
{
auto vregInfo1 = GetVRegInfo ( inst - > mArg1 ) ;
if ( vregInfo1 - > mRelTo . mKind = = BeMCOperandKind_SymbolAddr )
isLEA = true ;
}
}
if ( inst - > mArg1 . mKind = = BeMCOperandKind_VRegAddr )
{
NotImpl ( ) ;
/*// LEA
switch ( instForm )
{
case BeMCInstForm_R64_RM64 : EmitInst ( BeMCInstForm_R64_RM64 , 0x8D , inst ) ; break ;
default :
NotImpl ( ) ;
} */
}
else if ( isLEA )
{
// LEA
switch ( instForm )
{
case BeMCInstForm_R64_RM64 : EmitInst ( BeMCInstForm_R64_RM64 , 0x8D , inst ) ; break ;
default :
NotImpl ( ) ;
}
}
// ??
/*else if ((vregInfo != NULL) && (vregInfo->mIsExpr))
{
// LEA
switch ( instForm )
{
case BeMCInstForm_R64_RM64 : EmitInst ( BeMCInstForm_R64_RM64_ADDR , 0x8D , inst ) ; break ;
default :
NotImpl ( ) ;
}
} */
else if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsZero ( ) ) )
{
// Emit as XOR <arg0>, <arg0>
EmitREX ( inst - > mArg0 , inst - > mArg0 , GetType ( inst - > mArg0 ) - > mSize = = 8 ) ;
mOut . Write ( ( uint8 ) 0x33 ) ; // r, r/m64
EmitModRM ( inst - > mArg0 , inst - > mArg0 ) ;
}
else
{
switch ( instForm )
{
case BeMCInstForm_XMM32_RM32 :
{
BF_ASSERT ( inst - > mArg1 . IsNativeReg ( ) ) ;
// CVTSI2SS - use zero-extended 64-bit register
EmitStdXMMInst ( BeMCInstForm_XMM32_RM64 , inst , 0x2A ) ;
}
break ;
case BeMCInstForm_XMM64_RM32 :
{
BF_ASSERT ( inst - > mArg1 . IsNativeReg ( ) ) ;
// CVTSI2SD - use zero-extended 64-bit register
EmitStdXMMInst ( BeMCInstForm_XMM64_RM64 , inst , 0x2A ) ;
}
break ;
case BeMCInstForm_XMM64_RM64 :
case BeMCInstForm_XMM32_RM64 :
{
// uint64->xmm Not implemented
NotImpl ( ) ;
}
break ;
case BeMCInstForm_XMM64_FRM32 :
case BeMCInstForm_XMM32_FRM64 :
{
// CVTSS2SD / CVTSD2SS
EmitStdXMMInst ( instForm , inst , 0x5A ) ;
}
break ;
case BeMCInstForm_R32_F32 :
case BeMCInstForm_R32_F64 :
case BeMCInstForm_R64_F32 :
case BeMCInstForm_R64_F64 :
{
// CVTTSS2SI
EmitStdXMMInst ( instForm , inst , 0x2C ) ;
}
break ;
default :
{
// MOVSS/MOVSD
if ( EmitStdXMMInst ( instForm , inst , 0x10 , 0x11 ) )
break ;
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsImmediateInt ( ) ) )
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
if ( arg0Type - > mTypeCode = = BeTypeCode_Int16 )
{
Emit ( 0x66 ) ;
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0xB8 + EncodeRegNum ( inst - > mArg0 . mReg ) ) ;
mOut . Write ( ( int16 ) inst - > mArg1 . mImmediate ) ;
break ;
}
else if ( ( arg0Type - > mTypeCode = = BeTypeCode_Int32 ) | | ( arg0Type - > mTypeCode = = BeTypeCode_Int64 ) )
{
// For 64-bit writes, we would like to use 32-bit zero extension but we resort to the full
// 64-bits if necessary
auto arg0 = inst - > mArg0 ;
if ( arg0Type - > mTypeCode = = BeTypeCode_Int64 )
{
auto resizedReg = ResizeRegister ( arg0 . mReg , 4 ) ;
if ( ( resizedReg ! = X64Reg_None ) & & ( inst - > mArg1 . mImmediate < 0 ) & & ( inst - > mArg1 . mImmediate > = - 0x80000000LL ) )
{
// Do sign-extend R32 mov
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
Emit ( 0xC7 ) ;
EmitModRM ( 0 , inst - > mArg0 ) ;
mOut . Write ( ( int32 ) inst - > mArg1 . mImmediate ) ;
break ;
}
if ( ( resizedReg = = X64Reg_None ) | | ( inst - > mArg1 . mImmediate < 0 ) | | ( inst - > mArg1 . mImmediate > = 0x100000000LL ) )
{
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
Emit ( 0xB8 + EncodeRegNum ( inst - > mArg0 . mReg ) ) ;
mOut . Write ( ( int64 ) inst - > mArg1 . mImmediate ) ;
break ;
}
else
arg0 . mReg = resizedReg ;
}
EmitREX ( BeMCOperand ( ) , arg0 , false ) ;
Emit ( 0xB8 + EncodeRegNum ( arg0 . mReg ) ) ;
mOut . Write ( ( int32 ) inst - > mArg1 . mImmediate ) ;
break ;
}
}
EmitStdInst ( instForm , inst , 0x89 , 0x8B , 0xC7 , 0x0 ) ;
}
}
}
}
break ;
case BeMCInstKind_MovRaw :
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
if ( ( arg0Type - > mTypeCode = = BeTypeCode_Int64 ) & & ( arg1Type - > mTypeCode = = BeTypeCode_Double ) )
{
Emit ( 0x66 ) ; EmitREX ( inst - > mArg1 , inst - > mArg0 , true ) ;
Emit ( 0x0F ) ; Emit ( 0x7E ) ;
EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
}
else
{
NotImpl ( ) ;
}
}
break ;
case BeMCInstKind_MovSX :
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
switch ( arg0Type - > mTypeCode )
{
case BeTypeCode_Int16 :
BF_ASSERT ( arg1Type - > mTypeCode = = BeTypeCode_Int8 ) ;
Emit ( 0x66 ) ;
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xBE ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int32 :
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Int8 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xBE ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int16 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xBF ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Float :
// CVTSS2SI
Emit ( 0xF3 ) ; EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0x2C ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
default : NotImpl ( ) ;
}
break ;
case BeTypeCode_Int64 :
switch ( arg1Type - > mTypeCode )
{
case BeTypeCode_Int8 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
Emit ( 0x0F ) ; Emit ( 0xBE ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ; break ;
case BeTypeCode_Int16 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
Emit ( 0x0F ) ; Emit ( 0xBF ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ; break ;
case BeTypeCode_Int32 :
EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
Emit ( 0x63 ) ; EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ; break ;
case BeTypeCode_Float :
// CVTSS2SI
Emit ( 0xF3 ) ; EmitREX ( inst - > mArg0 , inst - > mArg1 , true ) ;
Emit ( 0x0F ) ; Emit ( 0x2C ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
default : NotImpl ( ) ;
}
break ;
case BeTypeCode_Float :
case BeTypeCode_Double :
{
if ( arg1Type - > IsInt ( ) )
{
// CVTSI2SS / CVTSI2SD
EmitStdXMMInst ( instForm , inst , 0x2A ) ;
}
else
NotImpl ( ) ;
/*switch (arg1Type->mTypeCode)
{
case BeTypeCode_Double :
EmitStdXMMInst ( BeMCInstForm_XMM32_RM64 , inst , 0x5A ) ;
break ;
default :
NotImpl ( ) ;
break ;
} */
}
break ;
default : NotImpl ( ) ;
}
}
break ;
case BeMCInstKind_CmpXChg :
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
if ( inst - > mArg1 . IsNativeReg ( ) )
{
switch ( instForm )
{
case BeMCInstForm_R8_RM8 :
instForm = BeMCInstForm_RM8_R8 ;
break ;
case BeMCInstForm_R16_RM16 :
instForm = BeMCInstForm_RM16_R16 ;
break ;
case BeMCInstForm_R32_RM32 :
instForm = BeMCInstForm_RM32_R32 ;
break ;
case BeMCInstForm_R64_RM64 :
instForm = BeMCInstForm_RM64_R64 ;
break ;
}
}
if ( ! inst - > mArg0 . IsNativeReg ( ) )
Emit ( 0xF0 ) ; // LOCK
switch ( instForm )
{
case BeMCInstForm_RM8_R8 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xB0 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM16_R16 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
Emit ( 0x66 ) ; Emit ( 0x0F ) ; Emit ( 0xB0 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM32_R32 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xB1 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM64_R64 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , true ) ;
Emit ( 0x0F ) ; Emit ( 0xB1 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
default :
Fail ( " Invalid CmpXChg args " ) ;
}
}
break ;
case BeMCInstKind_XAdd :
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
switch ( instForm )
{
case BeMCInstForm_R8_RM8 :
if ( inst - > mArg1 . IsNativeReg ( ) )
instForm = BeMCInstForm_RM8_R8 ;
break ;
case BeMCInstForm_R16_RM16 :
if ( inst - > mArg1 . IsNativeReg ( ) )
instForm = BeMCInstForm_RM16_R16 ;
break ;
case BeMCInstForm_R32_RM32 :
if ( inst - > mArg1 . IsNativeReg ( ) )
instForm = BeMCInstForm_RM32_R32 ;
break ;
case BeMCInstForm_R64_RM64 :
if ( inst - > mArg1 . IsNativeReg ( ) )
instForm = BeMCInstForm_RM64_R64 ;
break ;
}
if ( ! inst - > mArg0 . IsNativeReg ( ) )
Emit ( 0xF0 ) ; // LOCK
switch ( instForm )
{
case BeMCInstForm_RM8_R8 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xC0 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM16_R16 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
Emit ( 0x66 ) ; Emit ( 0x0F ) ; Emit ( 0xC0 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM32_R32 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
Emit ( 0x0F ) ; Emit ( 0xC1 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_RM64_R64 :
EmitREX ( inst - > mArg1 , inst - > mArg0 , true ) ;
Emit ( 0x0F ) ; Emit ( 0xC1 ) ; EmitModRM ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
default :
Fail ( " Invalid XAdd args " ) ;
}
}
break ;
case BeMCInstKind_XChg :
{
EmitStdInst ( instForm , inst , 0x87 , 0x87 , 0x00 , 0x0 ) ;
}
break ;
case BeMCInstKind_Load :
{
if ( inst - > mArg1 . IsSymbol ( ) )
{
BF_ASSERT ( inst - > mArg0 . IsNativeReg ( ) ) ;
}
switch ( instForm )
{
case BeMCInstForm_XMM64_FRM64 :
case BeMCInstForm_XMM32_FRM32 :
// MOVSS/MOVSD
EmitStdXMMInst ( instForm , inst , 0x10 ) ;
break ;
case BeMCInstForm_R32_RM32 :
EmitInst ( BeMCInstForm_R32_RM64_ADDR , 0x8B , inst ) ;
break ;
case BeMCInstForm_R64_RM64 :
EmitInst ( BeMCInstForm_R64_RM64_ADDR , 0x8B , inst ) ;
break ;
case BeMCInstForm_XMM32_RM64 :
case BeMCInstForm_XMM64_RM64 :
// MOVSS / MOVSD
BF_ASSERT ( inst - > mArg0 . IsNativeReg ( ) ) ;
if ( instForm = = BeMCInstForm_XMM64_RM64 )
Emit ( 0xF2 ) ;
else
Emit ( 0xF3 ) ;
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0x10 ) ;
EmitModRM_Addr ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
default :
{
BF_ASSERT ( inst - > mArg0 . IsNativeReg ( ) ) ;
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
BF_ASSERT ( arg1Type - > mSize = = 8 ) ;
switch ( arg0Type - > mSize )
{
case 1 : EmitInst ( BeMCInstForm_R8_RM64_ADDR , 0x8B , inst ) ; break ;
case 2 : EmitInst ( BeMCInstForm_R16_RM64_ADDR , 0x8B , inst ) ; break ;
case 4 : EmitInst ( BeMCInstForm_R32_RM64_ADDR , 0x8B , inst ) ; break ;
case 8 : EmitInst ( BeMCInstForm_R64_RM64_ADDR , 0x8B , inst ) ; break ;
}
}
}
/*BF_ASSERT(inst->mArg0.mKind == BeMCOperandKind_NativeReg);
BF_ASSERT ( inst - > mArg1 . mKind = = BeMCOperandKind_NativeReg ) ;
EmitREX ( inst - > mArg0 , inst - > mArg1 ) ;
mOut . Write ( ( uint8 ) 0x8B ) ;
uint8 modRM = ( 0x0 < < 6 ) | ( EncodeRegNum ( inst - > mArg1 . mReg ) ) | ( EncodeRegNum ( inst - > mArg0 . mReg ) ) ; // <arg0>, [<arg1>]
mOut . Write ( modRM ) ; */
}
break ;
case BeMCInstKind_Store :
{
switch ( instForm )
{
/*case BeMCInstForm_R64_RM64:
{
// Make sure its really R64, R64
BF_ASSERT ( inst - > mArg1 . IsNativeReg ( ) ) ;
EmitInst ( BeMCInstForm_RM64_R64_ADDR , 0x89 , inst ) ;
}
break ; */
case BeMCInstForm_RM64_R64 : EmitInst ( BeMCInstForm_RM64_R64_ADDR , 0x89 , inst ) ; break ;
case BeMCInstForm_FRM32_XMM32 :
case BeMCInstForm_FRM64_XMM64 :
// MOVSS/MOVSD
EmitStdXMMInst ( instForm , inst , 0x10 , 0x11 ) ;
break ;
case BeMCInstForm_R64_F64 :
case BeMCInstForm_R64_F32 :
// MOVSS / MOVSD
BF_ASSERT ( inst - > mArg1 . IsNativeReg ( ) ) ;
if ( instForm = = BeMCInstForm_R64_F64 )
Emit ( 0xF2 ) ;
else
Emit ( 0xF3 ) ;
EmitREX ( inst - > mArg1 , inst - > mArg0 , false ) ;
Emit ( 0x0F ) ; Emit ( 0x11 ) ;
EmitModRM_Addr ( inst - > mArg1 , inst - > mArg0 ) ;
break ;
default :
{
BF_ASSERT ( inst - > mArg0 . IsNativeReg ( ) ) ;
BF_ASSERT ( inst - > mArg1 . IsNativeReg ( ) ) ;
auto arg0Type = GetType ( inst - > mArg0 ) ;
auto arg1Type = GetType ( inst - > mArg1 ) ;
BF_ASSERT ( arg0Type - > mSize = = 8 ) ;
switch ( arg1Type - > mSize )
{
case 1 : EmitInst ( BeMCInstForm_RM64_R8_ADDR , 0x89 , inst ) ; break ;
case 2 : EmitInst ( BeMCInstForm_RM64_R16_ADDR , 0x89 , inst ) ; break ;
case 4 : EmitInst ( BeMCInstForm_RM64_R32_ADDR , 0x89 , inst ) ; break ;
case 8 : EmitInst ( BeMCInstForm_RM64_R64_ADDR , 0x89 , inst ) ; break ;
}
}
}
}
break ;
case BeMCInstKind_Push :
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
if ( inst - > mArg1 )
{
BF_ASSERT ( IsXMMReg ( inst - > mArg0 . mReg ) ) ;
BF_ASSERT ( inst - > mArg1 . IsImmediate ( ) ) ;
// Is there a performance benefit to properly using MOVAPD vs MOVAPS if we only
// use this register for double storage?
// MOVAPS
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , false ) ;
Emit ( 0x0F ) ; Emit ( 0x29 ) ;
EmitModRMRel ( EncodeRegNum ( inst - > mArg0 . mReg ) , X64Reg_RSP , X64Reg_None , 1 , ( int ) inst - > mArg1 . mImmediate ) ;
break ;
}
if ( arg0Type - > IsFloat ( ) )
{
BF_ASSERT ( IsXMMReg ( inst - > mArg0 . mReg ) ) ;
// sub rsp, sizeof(arg0)
Emit ( 0x48 ) ; Emit ( 0x83 ) ; Emit ( 0xEC ) ; Emit ( arg0Type - > mSize ) ;
Emit ( ( arg0Type - > mSize = = 4 ) ? 0xF3 : 0xF2 ) ;
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , false ) ;
Emit ( 0x0F ) ; Emit ( 0x11 ) ;
EmitModRMRel ( EncodeRegNum ( inst - > mArg0 . mReg ) , X64Reg_RSP , X64Reg_None , 1 , 0 ) ;
break ;
}
// This is used in combination with register pops, and we can't pop 32-bit registers
// so we need to make sure we're always pushing a full 64 bits
switch ( instForm )
{
case BeMCInstForm_R8 : // Always use full reg (always push 64 bits)
case BeMCInstForm_R16 :
case BeMCInstForm_R32 :
case BeMCInstForm_R64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
mOut . Write ( ( uint8 ) ( 0x50 + EncodeRegNum ( inst - > mArg0 . mReg ) ) ) ;
break ;
case BeMCInstForm_RM64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
Emit ( 0xFF ) ; EmitModRM ( 6 , inst - > mArg0 ) ;
break ;
case BeMCInstForm_IMM32 : // Always pad to 64 bits
{
Emit ( 0x68 ) ; mOut . Write ( ( int32 ) inst - > mArg0 . mImmediate ) ;
// Emit padded 0
Emit ( 0x68 ) ; mOut . Write ( ( int32 ) 0 ) ;
}
break ;
default :
NotImpl ( ) ;
}
}
break ;
case BeMCInstKind_Pop :
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
if ( inst - > mArg1 )
{
BF_ASSERT ( IsXMMReg ( inst - > mArg0 . mReg ) ) ;
BF_ASSERT ( inst - > mArg1 . IsImmediate ( ) ) ;
// Is there a performance benefit to properly using MOVAPD vs MOVAPS if we only
// use this register for double storage?
// MOVAPS
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , false ) ;
Emit ( 0x0F ) ; Emit ( 0x28 ) ;
// Push always uses RSP (required for stack unwinding), but Pop uses RBP when applicable
// because it is still set up at that point
EmitModRMRelStack ( EncodeRegNum ( inst - > mArg0 . mReg ) , ( int ) inst - > mArg1 . mImmediate , 1 ) ;
break ;
}
if ( arg0Type - > IsFloat ( ) )
{
BF_ASSERT ( IsXMMReg ( inst - > mArg0 . mReg ) ) ;
Emit ( ( arg0Type - > mSize = = 4 ) ? 0xF3 : 0xF2 ) ;
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , false ) ;
Emit ( 0x0F ) ; Emit ( 0x10 ) ;
EmitModRMRel ( EncodeRegNum ( inst - > mArg0 . mReg ) , X64Reg_RSP , X64Reg_None , 1 , 0 ) ;
// add rsp, sizeof(arg0)
Emit ( 0x48 ) ; Emit ( 0x83 ) ; Emit ( 0xC4 ) ; Emit ( arg0Type - > mSize ) ;
break ;
}
switch ( instForm )
{
case BeMCInstForm_R64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
mOut . Write ( ( uint8 ) ( 0x58 + EncodeRegNum ( inst - > mArg0 . mReg ) ) ) ;
break ;
case BeMCInstForm_RM64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
Emit ( 0x8F ) ; EmitModRM ( 0 , inst - > mArg0 ) ;
default :
NotImpl ( ) ;
}
}
break ;
case BeMCInstKind_Neg :
{
auto typeCode = GetType ( inst - > mArg0 ) - > mTypeCode ;
switch ( typeCode )
{
case BeTypeCode_Int8 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0xF6 ) ;
EmitModRM ( 3 , inst - > mArg0 ) ;
break ;
case BeTypeCode_Int16 : Emit ( 0x66 ) ; // Fallthrough
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , typeCode = = BeTypeCode_Int64 ) ;
Emit ( 0xF7 ) ; EmitModRM ( 3 , inst - > mArg0 ) ;
break ;
default :
NotImpl ( ) ;
}
}
break ;
case BeMCInstKind_Not :
{
auto typeCode = GetType ( inst - > mArg0 ) - > mTypeCode ;
switch ( typeCode )
{
case BeTypeCode_Int8 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0xF6 ) ;
EmitModRM ( 2 , inst - > mArg0 ) ;
break ;
case BeTypeCode_Int16 : Emit ( 0x66 ) ; // Fallthrough
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , typeCode = = BeTypeCode_Int64 ) ;
Emit ( 0xF7 ) ; EmitModRM ( 2 , inst - > mArg0 ) ;
break ;
default :
NotImpl ( ) ;
}
}
break ;
case BeMCInstKind_Add :
{
if ( ( inst - > mResult ) & & ( inst - > mResult ! = inst - > mArg0 ) )
{
BF_ASSERT ( inst - > mResult . IsNativeReg ( ) ) ;
BF_ASSERT ( inst - > mArg0 . IsNativeReg ( ) ) ;
BF_ASSERT ( inst - > mArg1 . IsImmediate ( ) ) ;
// LEA form
auto resultType = GetType ( inst - > mArg0 ) ;
switch ( resultType - > mTypeCode )
{
case BeTypeCode_Int16 :
Emit ( 0x66 ) ;
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( inst - > mResult , inst - > mArg0 , resultType - > mTypeCode = = BeTypeCode_Int64 ) ;
Emit ( 0x8D ) ; EmitModRMRel ( EncodeRegNum ( inst - > mResult . mReg ) , ResizeRegister ( inst - > mArg0 . mReg , 8 ) , X64Reg_None , 1 , ( int ) inst - > mArg1 . mImmediate ) ;
break ;
default :
NotImpl ( ) ;
}
}
else if ( ( inst - > mArg1 . IsImmediateInt ( ) ) & & ( inst - > mArg1 . mImmediate = = 1 ) )
{
// Emit as INC <arg0>
auto typeCode = GetType ( inst - > mArg0 ) - > mTypeCode ;
switch ( typeCode )
{
case BeTypeCode_Int8 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0xFE ) ; EmitModRM ( 0 , inst - > mArg0 ) ;
break ;
case BeTypeCode_Int16 : Emit ( 0x66 ) ; // Fallthrough
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , typeCode = = BeTypeCode_Int64 ) ;
Emit ( 0xFF ) ; EmitModRM ( 0 , inst - > mArg0 ) ;
break ;
default :
NotImpl ( ) ;
}
}
else if ( ( inst - > mArg1 . IsImmediateInt ( ) ) & & ( inst - > mArg1 . mImmediate = = - 1 ) )
{
// Emit as DEC <arg0>
auto typeCode = GetType ( inst - > mArg0 ) - > mTypeCode ;
switch ( typeCode )
{
case BeTypeCode_Int8 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0xFE ) ; EmitModRM ( 1 , inst - > mArg0 ) ;
break ;
case BeTypeCode_Int16 : Emit ( 0x66 ) ; // Fallthrough
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , typeCode = = BeTypeCode_Int64 ) ;
Emit ( 0xFF ) ; EmitModRM ( 1 , inst - > mArg0 ) ;
break ;
default :
NotImpl ( ) ;
}
}
else
{
if ( ( ( instForm = = BeMCInstForm_RM64_IMM16 ) | | ( instForm = = BeMCInstForm_RM64_IMM32 ) ) & &
( inst - > mArg0 . mReg = = X64Reg_RAX ) )
{
// Emit as ADD RAX, <imm32>
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , true ) ;
mOut . Write ( ( uint8 ) 0x05 ) ;
mOut . Write ( ( int32 ) inst - > mArg1 . mImmediate ) ;
break ;
}
if ( EmitStdXMMInst ( instForm , inst , 0x58 ) )
break ;
EmitStdInst ( instForm , inst , 0x01 , 0x03 , 0x81 , 0x0 , 0x83 , 0x0 ) ;
}
}
break ;
case BeMCInstKind_Sub :
{
if ( ( inst - > mArg1 . IsImmediateInt ( ) ) & & ( inst - > mArg1 . mImmediate = = 1 ) )
{
// Emit as DEC <arg0>
auto typeCode = GetType ( inst - > mArg0 ) - > mTypeCode ;
switch ( typeCode )
{
case BeTypeCode_Int8 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , false ) ;
Emit ( 0xFE ) ; EmitModRM ( 1 , inst - > mArg0 ) ;
break ;
case BeTypeCode_Int16 : Emit ( 0x66 ) ; // Fallthrough
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , typeCode = = BeTypeCode_Int64 ) ;
Emit ( 0xFF ) ; EmitModRM ( 1 , inst - > mArg0 ) ;
break ;
default :
NotImpl ( ) ;
}
}
else
{
if ( ( ( instForm = = BeMCInstForm_RM64_IMM16 ) | | ( instForm = = BeMCInstForm_RM64_IMM32 ) ) & &
( inst - > mArg0 . mReg = = X64Reg_RAX ) )
{
// Emit as SUB RAX, <imm32>
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , true ) ;
mOut . Write ( ( uint8 ) 0x2D ) ;
mOut . Write ( ( int32 ) inst - > mArg1 . mImmediate ) ;
break ;
}
if ( EmitStdXMMInst ( instForm , inst , 0x5C ) )
break ;
EmitStdInst ( instForm , inst , 0x29 , 0x2B , 0x81 , 0x5 , 0x83 , 0x5 ) ;
}
}
break ;
case BeMCInstKind_IMul :
{
if ( EmitStdXMMInst ( instForm , inst , 0x59 ) )
break ;
BeMCOperand result = inst - > mResult ;
if ( ( ! result ) & & ( inst - > mArg1 . IsImmediate ( ) ) )
result = inst - > mArg0 ; // Do as 3-form anyway
if ( result )
{
BF_ASSERT ( inst - > mArg1 . IsImmediate ( ) ) ;
if ( ( inst - > mArg0 . IsNativeReg ( ) ) & &
( ( inst - > mArg1 . mImmediate = = 2 ) | | ( inst - > mArg1 . mImmediate = = 4 ) | | ( inst - > mArg1 . mImmediate = = 8 ) ) )
{
// LEA form
auto resultType = GetType ( inst - > mArg0 ) ;
if ( resultType - > mTypeCode ! = BeTypeCode_Int8 )
{
switch ( resultType - > mTypeCode )
{
case BeTypeCode_Int16 :
Emit ( 0x66 ) ;
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
uint8 rex = GetREX ( result , inst - > mArg0 , resultType - > mTypeCode = = BeTypeCode_Int64 ) ;
if ( rex ! = 0 )
{
if ( rex & 1 )
{
// Mov REX flag from RM to SiB
rex = ( rex & ~ 1 ) | 2 ;
}
Emit ( rex ) ;
}
Emit ( 0x8D ) ; EmitModRMRel ( EncodeRegNum ( result . mReg ) , X64Reg_None , inst - > mArg0 . mReg , ( int ) inst - > mArg1 . mImmediate , 0 ) ;
break ;
}
break ;
}
}
auto typeCode = GetType ( inst - > mArg0 ) - > mTypeCode ;
switch ( typeCode )
{
case BeTypeCode_Int16 :
Emit ( 0x66 ) ;
// Fall through
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( result , inst - > mArg0 , typeCode = = BeTypeCode_Int64 ) ;
if ( ( inst - > mArg1 . mImmediate > = - 0x80 ) & & ( inst - > mArg1 . mImmediate < = 0x7F ) )
{
Emit ( 0x6B ) ;
EmitModRM ( result , inst - > mArg0 , - 1 ) ;
mOut . Write ( ( int8 ) inst - > mArg1 . mImmediate ) ;
}
else
{
Emit ( 0x69 ) ;
EmitModRM ( result , inst - > mArg0 , ( typeCode = = BeTypeCode_Int16 ) ? - 2 : - 4 ) ;
if ( typeCode = = BeTypeCode_Int16 )
mOut . Write ( ( int16 ) inst - > mArg1 . mImmediate ) ;
else
mOut . Write ( ( int32 ) inst - > mArg1 . mImmediate ) ;
}
break ;
default :
NotImpl ( ) ;
}
}
else
{
if ( inst - > mArg1 . IsImmediate ( ) )
{
int64 multVal = inst - > mArg1 . mImmediate ;
if ( IsPowerOfTwo ( multVal ) )
{
int shiftCount = 0 ;
while ( multVal > 1 )
{
shiftCount + + ;
multVal > > = 1 ;
}
auto arg0Type = GetType ( inst - > mArg0 ) ;
switch ( arg0Type - > mTypeCode )
{
case BeTypeCode_Int64 :
EmitREX ( inst - > mArg0 , BeMCOperand ( ) , true ) ;
// Fall through
case BeTypeCode_Int32 :
Emit ( 0xC1 ) ; Emit ( ( 0x4 < < 3 ) | ( 3 < < 6 ) | ( EncodeRegNum ( inst - > mArg0 . mReg ) ) ) ;
Emit ( ( uint8 ) shiftCount ) ;
break ;
default :
NotImpl ( ) ;
}
break ;
}
}
switch ( instForm )
{
case BeMCInstForm_R8_RM8 :
BF_ASSERT ( inst - > mArg0 . mReg = = X64Reg_AL ) ;
Emit ( 0xF6 ) ;
EmitModRM ( 0x5 , inst - > mArg1 ) ;
break ;
case BeMCInstForm_R16_RM16 :
case BeMCInstForm_R32_RM32 :
case BeMCInstForm_R64_RM64 : EmitInst ( instForm , 0xAF0F , inst ) ; break ;
default :
NotImpl ( ) ;
}
}
}
break ;
case BeMCInstKind_Div :
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
switch ( arg0Type - > mTypeCode )
{
case BeTypeCode_Int8 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_AL ) ) ;
// XOR ah, ah
Emit ( 0x30 ) ; Emit ( 0xE4 ) ;
// IDIV rm
EmitREX ( BeMCOperand : : FromReg ( X64Reg_AX ) , inst - > mArg1 , false ) ;
Emit ( 0xF6 ) ; EmitModRM ( 0x6 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int16 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_AX ) ) ;
// XOR eax, eax
Emit ( 0x31 ) ; Emit ( 0xC0 ) ;
// IDIV rm
Emit ( 0x66 ) ;
EmitREX ( BeMCOperand : : FromReg ( X64Reg_AX ) , inst - > mArg1 , false ) ;
Emit ( 0xF7 ) ; EmitModRM ( 0x6 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int32 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_EAX ) ) ;
// XOR edx, edx
Emit ( 0x31 ) ; Emit ( 0xD2 ) ;
// IDIV rm
EmitREX ( BeMCOperand : : FromReg ( X64Reg_EAX ) , inst - > mArg1 , false ) ;
Emit ( 0xF7 ) ; EmitModRM ( 0x6 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int64 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_RAX ) ) ;
// XOR rdx, rdx
Emit ( 0x48 ) ; Emit ( 0x31 ) ; Emit ( 0xD2 ) ;
EmitREX ( BeMCOperand : : FromReg ( X64Reg_RAX ) , inst - > mArg1 , true ) ;
// IDIV rm
EmitREX ( BeMCOperand : : FromReg ( X64Reg_RAX ) , inst - > mArg1 , true ) ;
Emit ( 0xF7 ) ; EmitModRM ( 0x6 , inst - > mArg1 ) ;
break ;
}
}
break ;
case BeMCInstKind_IDiv :
{
if ( EmitStdXMMInst ( instForm , inst , 0x5E ) )
break ;
auto arg0Type = GetType ( inst - > mArg0 ) ;
switch ( arg0Type - > mTypeCode )
{
case BeTypeCode_Int8 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_AL ) ) ;
// CBW
Emit ( 0x66 ) ; Emit ( 0x98 ) ;
// IDIV rm
EmitREX ( BeMCOperand : : FromReg ( X64Reg_AX ) , inst - > mArg1 , false ) ;
Emit ( 0xF6 ) ; EmitModRM ( 0x7 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int16 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_AX ) ) ;
// CWD
Emit ( 0x66 ) ; Emit ( 0x99 ) ;
// IDIV rm
Emit ( 0x66 ) ;
EmitREX ( BeMCOperand : : FromReg ( X64Reg_AX ) , inst - > mArg1 , false ) ;
Emit ( 0xF7 ) ; EmitModRM ( 0x7 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int32 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_EAX ) ) ;
// CDQ
Emit ( 0x99 ) ;
// IDIV rm
EmitREX ( BeMCOperand : : FromReg ( X64Reg_EAX ) , inst - > mArg1 , false ) ;
Emit ( 0xF7 ) ; EmitModRM ( 0x7 , inst - > mArg1 ) ;
break ;
case BeTypeCode_Int64 :
BF_ASSERT ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg0 . mReg = = X64Reg_RAX ) ) ;
// CQO
Emit ( 0x48 ) ; Emit ( 0x99 ) ;
// IDIV rm
EmitREX ( BeMCOperand : : FromReg ( X64Reg_RAX ) , inst - > mArg1 , true ) ;
Emit ( 0xF7 ) ; EmitModRM ( 0x7 , inst - > mArg1 ) ;
break ;
}
}
break ;
case BeMCInstKind_IRem :
{
NotImpl ( ) ;
//if (EmitStdXMMInst(instForm, inst, 0x5E))
//break;
}
break ;
case BeMCInstKind_Cmp :
{
switch ( instForm )
{
case BeMCInstForm_XMM32_FRM32 :
case BeMCInstForm_XMM32_IMM :
// COMISS
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0x2F ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
case BeMCInstForm_XMM64_FRM64 :
case BeMCInstForm_XMM64_IMM :
// COMISD
Emit ( 0x66 ) ;
EmitREX ( inst - > mArg0 , inst - > mArg1 , false ) ;
Emit ( 0x0F ) ; Emit ( 0x2F ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg1 ) ;
break ;
default :
{
if ( ( ( inst - > mArg0 . IsNativeReg ( ) ) & & ( inst - > mArg1 . IsImmediateInt ( ) ) ) & & ( inst - > mArg1 . GetImmediateInt ( ) = = 0 ) )
{
// Test
auto typeCode = GetType ( inst - > mArg0 ) - > mTypeCode ;
switch ( typeCode )
{
case BeTypeCode_Int8 :
EmitREX ( inst - > mArg0 , inst - > mArg0 , false ) ;
Emit ( 0x84 ) ;
EmitModRM ( inst - > mArg0 , inst - > mArg0 ) ;
break ;
case BeTypeCode_Int16 : Emit ( 0x66 ) ; // Fallthrough
case BeTypeCode_Int32 :
case BeTypeCode_Int64 :
EmitREX ( inst - > mArg0 , inst - > mArg0 , typeCode = = BeTypeCode_Int64 ) ;
Emit ( 0x85 ) ; EmitModRM ( inst - > mArg0 , inst - > mArg0 ) ;
break ;
default :
NotImpl ( ) ;
}
/*if (arg0Type->mSize == 2)
Emit ( 0x66 ) ;
// Emit as TEST <arg0>, <arg0>
EmitREX ( inst - > mArg0 , inst - > mArg0 , GetType ( inst - > mArg0 ) - > mSize = = 8 ) ;
mOut . Write ( ( uint8 ) 0x85 ) ; // r/m64, r
EmitModRM ( inst - > mArg0 , inst - > mArg0 ) ; */
}
else
{
EmitStdInst ( instForm , inst , 0x39 , 0x3B , 0x81 , 0x7 , 0x83 , 0x7 ) ;
}
}
}
}
break ;
case BeMCInstKind_And :
{
BeMCInst modInst = * inst ;
bool isZeroing = false ;
// Use a shorter form if the upper bytes are masked in
if ( ( instForm = = BeMCInstForm_RM32_IMM32 ) | | ( instForm = = BeMCInstForm_RM64_IMM32 ) )
{
if ( ( modInst . mArg1 . mImmediate & 0xFFFF0000 ) = = 0xFFFF0000 )
{
if ( modInst . mArg0 . IsNativeReg ( ) )
modInst . mArg0 . mReg = ResizeRegister ( modInst . mArg0 . mReg , 2 ) ;
instForm = BeMCInstForm_RM16_IMM16 ;
if ( ( modInst . mArg1 . mImmediate & 0xFFFF ) = = 0 )
isZeroing = true ;
}
else if ( instForm = = BeMCInstForm_RM32_IMM32 )
{
if ( ( modInst . mArg1 . mImmediate & 0xFFFFFFFF ) = = 0 )
{
isZeroing = true ;
}
}
}
if ( ( instForm = = BeMCInstForm_RM16_IMM16 ) | | ( instForm = = BeMCInstForm_RM32_IMM16 ) | | ( instForm = = BeMCInstForm_RM64_IMM16 ) )
{
if ( ( modInst . mArg1 . mImmediate & 0xFFFFFF00 ) = = 0xFFFFFF00 )
{
if ( modInst . mArg0 . IsNativeReg ( ) )
modInst . mArg0 . mReg = ResizeRegister ( modInst . mArg0 . mReg , 1 ) ;
instForm = BeMCInstForm_RM8_IMM8 ;
}
else if ( instForm = = BeMCInstForm_RM16_IMM16 )
{
if ( ( modInst . mArg1 . mImmediate & 0xFFFF ) = = 0 )
{
isZeroing = true ;
}
}
}
if ( instForm = = BeMCInstForm_RM8_IMM8 )
{
// Are we really just zeroing out the lower byte?
if ( ( ( modInst . mArg1 . mImmediate & 0xFF ) = = 0 ) & & ( modInst . mArg0 . IsNativeReg ( ) ) )
{
isZeroing = true ;
}
}
if ( isZeroing )
{
int size = 0 ;
switch ( instForm )
{
case BeMCInstForm_RM32_IMM32 :
instForm = BeMCInstForm_RM32_R32 ;
break ;
case BeMCInstForm_RM16_IMM16 :
instForm = BeMCInstForm_RM16_R16 ;
break ;
case BeMCInstForm_RM8_IMM8 :
instForm = BeMCInstForm_RM8_R8 ;
break ;
default :
NotImpl ( ) ;
}
//XOR arg0, arg0
modInst . mKind = BeMCInstKind_Xor ;
modInst . mArg1 = modInst . mArg0 ;
EmitStdInst ( instForm , & modInst , 0x31 , 0x33 , 0x81 , 0x6 , 0x83 , 0x6 ) ;
break ;
}
EmitStdInst ( instForm , & modInst , 0x21 , 0x23 , 0x81 , 0x4 , 0x83 , 0x4 ) ;
}
break ;
case BeMCInstKind_Or :
{
EmitStdInst ( instForm , inst , 0x09 , 0x0B , 0x81 , 0x1 , 0x83 , 0x1 ) ;
}
break ;
case BeMCInstKind_Xor :
{
if ( EmitPackedXMMInst ( instForm , inst , 0x57 ) )
break ;
EmitStdInst ( instForm , inst , 0x31 , 0x33 , 0x81 , 0x6 , 0x83 , 0x6 ) ;
}
break ;
case BeMCInstKind_Shl :
case BeMCInstKind_Shr :
case BeMCInstKind_Sar :
{
int rx = 0 ;
switch ( inst - > mKind )
{
case BeMCInstKind_Shl :
rx = 4 ;
break ;
case BeMCInstKind_Shr :
rx = 5 ;
break ;
case BeMCInstKind_Sar :
rx = 7 ;
break ;
}
bool handled = false ;
switch ( instForm )
{
case BeMCInstForm_RM8_IMM8 :
case BeMCInstForm_RM16_IMM8 :
case BeMCInstForm_RM32_IMM8 :
case BeMCInstForm_RM64_IMM8 :
if ( inst - > mArg1 . mImmediate = = 1 )
{
// Shift by one has a short form
if ( instForm = = BeMCInstForm_RM16_IMM8 ) Emit ( 0x66 ) ;
EmitREX ( inst - > mArg1 , inst - > mArg0 , instForm = = BeMCInstForm_RM64_IMM8 ) ;
if ( instForm = = BeMCInstForm_RM8_IMM8 )
Emit ( 0xD0 ) ;
else
Emit ( 0xD1 ) ;
EmitModRM ( rx , inst - > mArg0 ) ;
handled = true ;
}
else
{
if ( instForm = = BeMCInstForm_RM16_IMM8 ) Emit ( 0x66 ) ;
EmitREX ( inst - > mArg1 , inst - > mArg0 , instForm = = BeMCInstForm_RM64_IMM8 ) ;
if ( instForm = = BeMCInstForm_RM8_IMM8 )
Emit ( 0xC0 ) ;
else
Emit ( 0xC1 ) ;
EmitModRM ( rx , inst - > mArg0 ) ;
Emit ( ( uint8 ) inst - > mArg1 . mImmediate ) ;
handled = true ;
}
break ;
default :
{
BF_ASSERT ( inst - > mArg1 . IsNativeReg ( ) ) ;
BF_ASSERT ( inst - > mArg1 . mReg = = X64Reg_RCX ) ;
int destSize = GetType ( inst - > mArg0 ) - > mSize ;
if ( destSize = = 2 ) Emit ( 0x66 ) ;
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , destSize = = 8 ) ;
if ( destSize = = 1 )
Emit ( 0xD2 ) ;
else
Emit ( 0xD3 ) ;
EmitModRM ( rx , inst - > mArg0 ) ;
handled = true ;
}
break ;
}
}
break ;
case BeMCInstKind_Test :
{
if ( instForm = = BeMCInstForm_R64_RM64 )
{
BF_SWAP ( inst - > mArg0 , inst - > mArg1 ) ;
instForm = BeMCInstForm_RM64_R64 ;
}
EmitStdInst ( instForm , inst , 0x85 , 0x00 , 0xF7 , 0x0 ) ;
}
break ;
case BeMCInstKind_CondBr :
{
BF_ASSERT ( inst - > mArg0 . mKind = = BeMCOperandKind_Label ) ;
BeMCJump jump ;
jump . mCodeOffset = funcCodePos ;
jump . mLabelIdx = inst - > mArg0 . mLabelIdx ;
// Speculative make it a short jump
jump . mJumpKind = 0 ;
jump . mCmpKind = inst - > mArg1 . mCmpKind ;
deferredJumps . push_back ( jump ) ;
mOut . Write ( GetJumpOpCode ( jump . mCmpKind , false ) ) ;
mOut . Write ( ( uint8 ) 0 ) ;
}
break ;
case BeMCInstKind_Br :
{
if ( inst - > mArg1 . mKind = = BeMCOperandKind_Immediate_i8 )
{
if ( inst - > mArg1 . mImmediate = = 2 ) // Fake?
break ;
}
if ( inst - > mArg0 . mKind = = BeMCOperandKind_Label )
{
BeMCJump jump ;
jump . mCodeOffset = funcCodePos ;
jump . mLabelIdx = inst - > mArg0 . mLabelIdx ;
// Speculatively make it a short jump
jump . mJumpKind = 0 ;
jump . mCmpKind = BeCmpKind_None ;
deferredJumps . push_back ( jump ) ;
mOut . Write ( ( uint8 ) 0xEB ) ;
mOut . Write ( ( uint8 ) 0 ) ;
}
else
{
auto arg0Type = GetType ( inst - > mArg0 ) ;
BF_ASSERT ( arg0Type - > mTypeCode = = BeTypeCode_Int64 ) ;
uint8 rex = GetREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
if ( rex ! = 0x40 )
Emit ( rex ) ;
Emit ( 0xFF ) ;
EmitModRM ( 4 , inst - > mArg0 ) ;
}
}
break ;
case BeMCInstKind_Ret :
mOut . Write ( ( uint8 ) 0xC3 ) ;
break ;
case BeMCInstKind_Call :
{
switch ( instForm )
{
case BeMCInstForm_Symbol :
{
// Call [rip+<X>]
mOut . Write ( ( uint8 ) 0xFF ) ;
mOut . Write ( ( uint8 ) 0x15 ) ;
BeMCRelocation reloc ;
reloc . mKind = BeMCRelocationKind_REL32 ;
reloc . mOffset = mOut . GetPos ( ) ;
reloc . mSymTableIdx = inst - > mArg0 . mSymbolIdx ;
mCOFFObject - > mTextSect . mRelocs . push_back ( reloc ) ;
mTextRelocs . push_back ( ( int ) mCOFFObject - > mTextSect . mRelocs . size ( ) - 1 ) ;
mOut . Write ( ( int32 ) 0 ) ;
}
break ;
case BeMCInstForm_SymbolAddr :
{
// Call <X>
mOut . Write ( ( uint8 ) 0xE8 ) ;
BeMCRelocation reloc ;
reloc . mKind = BeMCRelocationKind_REL32 ;
reloc . mOffset = mOut . GetPos ( ) ;
reloc . mSymTableIdx = inst - > mArg0 . mSymbolIdx ;
mCOFFObject - > mTextSect . mRelocs . push_back ( reloc ) ;
mTextRelocs . push_back ( ( int ) mCOFFObject - > mTextSect . mRelocs . size ( ) - 1 ) ;
mOut . Write ( ( int32 ) 0 ) ;
}
break ;
default :
{
EmitREX ( BeMCOperand ( ) , inst - > mArg0 , true ) ;
mOut . Write ( ( uint8 ) 0xFF ) ;
EmitModRM ( 0x2 , inst - > mArg0 ) ;
}
break ;
}
}
//mOut.Write((uint8)0xC3);
break ;
default :
NotImpl ( ) ;
}
}
}
if ( mDebugging )
{
dbgStr + = " \n EMISSIONS: \n " ;
}
// Finish range for all outstanding variables
for ( int vregLiveIdx : * vregsLive )
{
if ( vregLiveIdx > = mLivenessContext . mNumItems )
continue ;
int vregIdx = vregLiveIdx % mLivenessContext . mNumItems ;
auto dbgVar = mVRegInfo [ vregIdx ] - > mDbgVariable ;
if ( dbgVar ! = NULL )
{
dbgVar - > mDeclEnd = mOut . GetPos ( ) - textSectStartPos ;
dbgVar - > mDeclLifetimeExtend = false ;
BF_ASSERT ( dbgVar - > mDeclEnd > = dbgVar - > mDeclStart ) ;
if ( ! dbgVar - > mSavedRanges . empty ( ) )
{
auto & savedRange = dbgVar - > mSavedRanges . back ( ) ;
if ( savedRange . mLength = = - 1 )
{
savedRange . mLength = dbgVar - > mDeclEnd - savedRange . mOffset ;
dbgVar - > mGaps . push_back ( savedRange ) ;
}
}
}
}
auto & codeVec = mOut . mData ;
for ( int pass = 0 ; true ; pass + + )
{
bool didWidening = false ;
for ( auto & jump : deferredJumps )
{
int labelPos = labelPositions [ jump . mLabelIdx ] ;
int offsetRel = jump . mCodeOffset ;
if ( jump . mJumpKind = = 0 )
offsetRel + = 2 ;
else if ( jump . mJumpKind = = 1 )
offsetRel + = 4 ;
else if ( jump . mJumpKind = = 2 )
{
if ( jump . mCmpKind = = BeCmpKind_None )
offsetRel + = 5 ;
else
offsetRel + = 6 ;
}
int offset = labelPos - offsetRel ;
//BF_ASSERT((offset >= -128) && (offset <= 127));
if ( ( jump . mJumpKind = = 0 ) & &
( ( offset < - 0x80 ) | | ( offset > 0x7F ) ) )
{
// Extend this guy into a rel32
int adjustFrom = jump . mCodeOffset + 2 ;
int adjustBytes = 3 ;
if ( jump . mCmpKind ! = BeCmpKind_None )
adjustBytes + + ;
codeVec . Insert ( jump . mCodeOffset + 1 + textSectStartPos , ( uint8 ) 0xCC , adjustBytes ) ;
mOut . mPos + = adjustBytes ;
if ( jump . mCmpKind = = BeCmpKind_None )
{
codeVec [ jump . mCodeOffset + textSectStartPos ] = 0xE9 ;
}
else
{
codeVec [ jump . mCodeOffset + textSectStartPos ] = 0x0F ;
codeVec [ jump . mCodeOffset + 1 + textSectStartPos ] = GetJumpOpCode ( jump . mCmpKind , true ) ;
}
# define CODE_OFFSET_ADJUST(val) if (val >= adjustFrom) val += adjustBytes
for ( auto & labelPosition : labelPositions )
CODE_OFFSET_ADJUST ( labelPosition ) ;
for ( auto & checkJump : deferredJumps )
CODE_OFFSET_ADJUST ( checkJump . mCodeOffset ) ;
if ( mDbgFunction ! = NULL )
{
for ( auto & codeEmission : mDbgFunction - > mEmissions )
CODE_OFFSET_ADJUST ( codeEmission . mPos ) ;
for ( auto dbgVar : mDbgFunction - > mVariables )
{
if ( dbgVar = = NULL )
continue ;
CODE_OFFSET_ADJUST ( dbgVar - > mDeclStart ) ;
CODE_OFFSET_ADJUST ( dbgVar - > mDeclEnd ) ;
for ( auto & range : dbgVar - > mSavedRanges )
CODE_OFFSET_ADJUST ( range . mOffset ) ;
for ( auto & range : dbgVar - > mGaps )
CODE_OFFSET_ADJUST ( range . mOffset ) ;
}
}
for ( auto & deferredUnwind : deferredUnwinds )
CODE_OFFSET_ADJUST ( deferredUnwind . mCodePos ) ;
for ( int textRelocIdx : mTextRelocs )
{
auto & reloc = mCOFFObject - > mTextSect . mRelocs [ textRelocIdx ] ;
if ( reloc . mOffset - textSectStartPos > = adjustFrom )
reloc . mOffset + = adjustBytes ;
}
for ( auto & dbgInstPos : dbgInstPositions )
CODE_OFFSET_ADJUST ( dbgInstPos . mPos ) ;
# undef CODE_OFFSET_ADJUST
jump . mJumpKind = 2 ;
didWidening = true ;
}
//TODO: Test extending into a long jump
if ( jump . mJumpKind = = 0 )
codeVec [ jump . mCodeOffset + 1 + textSectStartPos ] = ( uint8 ) offset ;
else if ( jump . mCmpKind = = BeCmpKind_None )
* ( int32 * ) ( & codeVec [ jump . mCodeOffset + 1 + textSectStartPos ] ) = ( uint32 ) offset ;
else
* ( int32 * ) ( & codeVec [ jump . mCodeOffset + 2 + textSectStartPos ] ) = ( uint32 ) offset ;
}
if ( ! didWidening )
break ;
}
if ( ! mSwitchEntries . empty ( ) )
{
auto thisFuncSym = mCOFFObject - > GetSymbol ( mBeFunction ) ;
for ( auto & switchEntry : mSwitchEntries )
{
auto & sect = mCOFFObject - > mRDataSect ;
int32 * ofsPtr = ( int32 * ) ( ( uint8 * ) sect . mData . GetPtr ( ) + switchEntry . mOfs ) ;
* ofsPtr = labelPositions [ switchEntry . mBlock - > mLabelIdx ] ;
BeMCRelocation reloc ;
reloc . mKind = BeMCRelocationKind_ADDR32NB ;
reloc . mOffset = switchEntry . mOfs ;
reloc . mSymTableIdx = thisFuncSym - > mIdx ;
sect . mRelocs . push_back ( reloc ) ;
}
}
//for (auto& deferredUnwind : deferredUnwinds)
for ( int deferredUnwindIdx = ( int ) deferredUnwinds . size ( ) - 1 ; deferredUnwindIdx > = 0 ; deferredUnwindIdx - - )
{
auto inst = deferredUnwinds [ deferredUnwindIdx ] . mUnwindInst ;
int codePos = deferredUnwinds [ deferredUnwindIdx ] . mCodePos ;
switch ( inst - > mKind )
{
case BeMCInstKind_Unwind_Alloc :
{
xdata . Write ( ( uint8 ) ( codePos ) ) ;
int allocSize = ( int ) inst - > mArg0 . mImmediate ;
if ( allocSize < = 128 )
{
// UWOP_ALLOC_SMALL
xdata . Write ( ( uint8 ) ( ( 2 ) | ( ( allocSize / 8 - 1 ) < < 4 ) ) ) ;
}
else if ( ( allocSize < = 0x7FFF8 ) & & ( ( allocSize & 7 ) = = 0 ) ) // up to 512k-8 bytes
{
// UWOP_ALLOC_LARGE
xdata . Write ( ( uint8 ) ( ( 1 ) | ( ( 0 ) < < 4 ) ) ) ;
xdata . Write ( ( uint8 ) ( ( allocSize / 8 ) & 0xFF ) ) ;
xdata . Write ( ( uint8 ) ( ( allocSize / 8 ) > > 8 ) ) ;
}
else
{
// UWOP_ALLOC_LARGE+
xdata . Write ( ( uint8 ) ( ( 1 ) | ( ( 1 ) < < 4 ) ) ) ;
xdata . Write ( ( int32 ) allocSize ) ;
}
}
break ;
case BeMCInstKind_Unwind_PushReg :
{
xdata . Write ( ( uint8 ) ( codePos ) ) ;
int regNum = 0 ;
switch ( inst - > mArg0 . mReg )
{
case X64Reg_RAX : regNum = 0 ; break ;
case X64Reg_RCX : regNum = 1 ; break ;
case X64Reg_RDX : regNum = 2 ; break ;
case X64Reg_RBX : regNum = 3 ; break ;
case X64Reg_RSP : regNum = 4 ; break ;
case X64Reg_RBP : regNum = 5 ; break ;
case X64Reg_RSI : regNum = 6 ; break ;
case X64Reg_RDI : regNum = 7 ; break ;
case X64Reg_R8 : regNum = 8 ; break ;
case X64Reg_R9 : regNum = 9 ; break ;
case X64Reg_R10 : regNum = 10 ; break ;
case X64Reg_R11 : regNum = 11 ; break ;
case X64Reg_R12 : regNum = 12 ; break ;
case X64Reg_R13 : regNum = 13 ; break ;
case X64Reg_R14 : regNum = 14 ; break ;
case X64Reg_R15 : regNum = 15 ; break ;
default : NotImpl ( ) ;
}
// UWOP_PUSH_NONVOL
xdata . Write ( ( uint8 ) ( ( 0 ) | ( regNum < < 4 ) ) ) ;
}
break ;
case BeMCInstKind_Unwind_SaveXMM :
{
xdata . Write ( ( uint8 ) ( codePos ) ) ;
int regNum = 0 ;
switch ( inst - > mArg0 . mReg )
{
case X64Reg_XMM0_f64 : regNum = 0 ; break ;
case X64Reg_XMM1_f64 : regNum = 1 ; break ;
case X64Reg_XMM2_f64 : regNum = 2 ; break ;
case X64Reg_XMM3_f64 : regNum = 3 ; break ;
case X64Reg_XMM4_f64 : regNum = 4 ; break ;
case X64Reg_XMM5_f64 : regNum = 5 ; break ;
case X64Reg_XMM6_f64 : regNum = 6 ; break ;
case X64Reg_XMM7_f64 : regNum = 7 ; break ;
case X64Reg_XMM8_f64 : regNum = 8 ; break ;
case X64Reg_XMM9_f64 : regNum = 9 ; break ;
case X64Reg_XMM10_f64 : regNum = 10 ; break ;
case X64Reg_XMM11_f64 : regNum = 11 ; break ;
case X64Reg_XMM12_f64 : regNum = 12 ; break ;
case X64Reg_XMM13_f64 : regNum = 13 ; break ;
case X64Reg_XMM14_f64 : regNum = 14 ; break ;
case X64Reg_XMM15_f64 : regNum = 15 ; break ;
default : NotImpl ( ) ;
}
// UWOP_SAVE_XMM128
xdata . Write ( ( uint8 ) ( ( 8 ) | ( regNum < < 4 ) ) ) ;
xdata . Write ( ( int16 ) ( inst - > mArg1 . mImmediate / 16 ) ) ;
}
break ;
case BeMCInstKind_Unwind_SetBP :
{
xdata . Write ( ( uint8 ) ( codePos ) ) ;
// UWOP_SET_FPREG
xdata . Write ( ( uint8 ) ( ( 3 ) | ( 0 < < 4 ) ) ) ;
}
break ;
}
}
int codeLen = mOut . GetPos ( ) - textSectStartPos ;
int minCodeLen = hotJumpLen ;
int addCodeLen = minCodeLen - codeLen ;
for ( int i = 0 ; i < addCodeLen ; i + + )
{
mOut . Write ( ( uint8 ) 0x90 ) ;
}
if ( mDbgFunction ! = NULL )
{
mDbgFunction - > mCodeLen = mOut . GetPos ( ) - textSectStartPos ;
}
if ( hasPData )
{
int codeLen = mOut . GetPos ( ) - textSectStartPos ;
// PDATA end addr
BeMCRelocation reloc ;
reloc . mKind = BeMCRelocationKind_ADDR32NB ;
reloc . mOffset = mCOFFObject - > mPDataSect . mData . GetPos ( ) ;
reloc . mSymTableIdx = mCOFFObject - > GetSymbol ( mBeFunction ) - > mIdx ;
mCOFFObject - > mPDataSect . mRelocs . push_back ( reloc ) ;
mCOFFObject - > mPDataSect . mData . Write ( ( int32 ) codeLen ) ;
// XDATA pos
reloc . mKind = BeMCRelocationKind_ADDR32NB ;
reloc . mOffset = mCOFFObject - > mPDataSect . mData . GetPos ( ) ;
reloc . mSymTableIdx = mCOFFObject - > mXDataSect . mSymbolIdx ;
mCOFFObject - > mPDataSect . mRelocs . push_back ( reloc ) ;
mCOFFObject - > mPDataSect . mData . Write ( ( int32 ) xdataStartPos ) ;
int numCodes = ( xdata . GetPos ( ) - xdataStartPos - 4 ) / 2 ;
if ( numCodes > 0 )
{
xdata . mData [ xdataStartPos + 1 ] = ( uint8 ) ( deferredUnwinds . back ( ) . mCodePos ) ; // prolog size
xdata . mData [ xdataStartPos + 2 ] = ( uint8 ) numCodes ;
}
}
for ( auto & dbgInstPos : dbgInstPositions )
{
auto inst = dbgInstPos . mInst ;
dbgStr + = StrFormat ( " %d[%d] " , dbgInstPos . mPos , dbgInstPos . mOrigPos ) ;
if ( inst - > mDbgLoc ! = NULL )
dbgStr + = StrFormat ( " @%d " , inst - > mDbgLoc - > mIdx ) ;
if ( inst - > mResult . mKind ! = BeMCOperandKind_None )
{
dbgStr + = " " ;
dbgStr + = ToString ( inst - > mResult ) ;
dbgStr + = " = " ;
}
dbgStr + = " " ;
dbgStr + = gOpName [ ( int ) inst - > mKind ] ;
if ( inst - > mArg0 . mKind ! = BeMCOperandKind_None )
{
dbgStr + = " " ;
dbgStr + = ToString ( inst - > mArg0 ) ;
}
if ( inst - > mArg1 . mKind ! = BeMCOperandKind_None )
{
dbgStr + = " , " ;
dbgStr + = ToString ( inst - > mArg1 ) ;
}
dbgStr + = " \n " ;
}
if ( mDebugging )
{
dbgStr + = " \n Debug Variables: \n " ;
for ( auto dbgVar : mDbgFunction - > mVariables )
{
if ( dbgVar = = NULL )
continue ;
dbgStr + = StrFormat ( " %s %d to %d " , dbgVar - > mName . c_str ( ) , dbgVar - > mDeclStart , dbgVar - > mDeclEnd ) ;
if ( dbgVar - > mDeclLifetimeExtend )
dbgStr + = " LifetimeExtend " ;
dbgStr + = " \n " ;
for ( auto & gap : dbgVar - > mGaps )
{
if ( gap . mLength = = - 1 )
dbgStr + = StrFormat ( " Gap %d to <unterminated> \n " , gap . mOffset ) ;
else
dbgStr + = StrFormat ( " Gap %d to %d \n " , gap . mOffset , gap . mOffset + gap . mLength ) ;
}
for ( auto & gap : dbgVar - > mSavedRanges )
{
if ( gap . mLength = = - 1 )
dbgStr + = StrFormat ( " SavedRange %d to <unterminated> \n " , gap . mOffset ) ;
else
dbgStr + = StrFormat ( " SavedRange %d to %d \n " , gap . mOffset , gap . mOffset + gap . mLength ) ;
}
}
}
if ( ! dbgStr . empty ( ) )
{
dbgStr + = " \n " ;
OutputDebugStr ( dbgStr ) ;
}
}
void BeMCContext : : HandleParams ( )
{
auto beModule = mBeFunction - > mModule ;
int regIdxOfs = 0 ;
int paramOfs = 0 ;
auto retType = mBeFunction - > GetFuncType ( ) - > mReturnType ;
X64CPURegister compositeRetReg = X64Reg_None ;
bool flipFirstRegs = false ;
if ( mBeFunction - > HasStructRet ( ) )
{
flipFirstRegs = mBeFunction - > mCallingConv = = BfIRCallingConv_ThisCall ;
//paramOfs = 1;
/*auto ptrType = (BePointerType*)mBeFunction->mFuncType->mParams[0].mType;
BF_ASSERT ( ptrType - > mTypeCode = = BeTypeCode_Pointer ) ;
retType = ptrType - > mElementType ; */
//retType = mBeFunction->mFuncType->mParams[0].mType;
//flipFirstRegs = mBeFunction->mCallingConv == BfIRCallingConv_ThisCall;
/*
auto beArg = beModule - > GetArgument ( 0 ) ;
compositeRetReg = ( mBeFunction - > mCallingConv = = BfIRCallingConv_ThisCall ) ? X64Reg_RDX : X64Reg_RCX ;
mParamsUsedRegs . push_back ( compositeRetReg ) ;
BeMCOperand mcOperand ;
mcOperand . mReg = compositeRetReg ;
mcOperand . mKind = BeMCOperandKind_NativeReg ;
auto ptrType = ( BePointerType * ) mBeFunction - > mFuncType - > mParams [ 0 ] . mType ;
BF_ASSERT ( ptrType - > mTypeCode = = BeTypeCode_Pointer ) ;
auto paramVReg = AllocVirtualReg ( ptrType - > mElementType ) ;
auto paramVRegInfo = GetVRegInfo ( paramVReg ) ;
CreateDefineVReg ( paramVReg ) ;
paramVRegInfo - > mNaturalReg = compositeRetReg ;
AllocInst ( BeMCInstKind_Mov , paramVReg , mcOperand ) ;
mValueToOperand [ beArg ] = paramVReg ; */
}
else if ( retType - > IsComposite ( ) )
{
compositeRetReg = ( mBeFunction - > mCallingConv = = BfIRCallingConv_ThisCall ) ? X64Reg_RDX : X64Reg_RCX ;
auto retVReg = AllocVirtualReg ( mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ) ;
auto retVRegInfo = GetVRegInfo ( retVReg ) ;
retVRegInfo - > mNaturalReg = compositeRetReg ;
retVRegInfo - > mForceReg = true ;
retVRegInfo - > mMustExist = true ;
AllocInst ( BeMCInstKind_Mov , retVReg , BeMCOperand : : FromReg ( compositeRetReg ) ) ;
mCompositeRetVRegIdx = retVReg . mVRegIdx ;
mParamsUsedRegs . push_back ( compositeRetReg ) ;
}
for ( int paramIdx = 0 ; paramIdx < ( int ) mBeFunction - > mParams . size ( ) - paramOfs ; paramIdx + + )
{
if ( ( ( paramIdx = = 0 ) & & ( compositeRetReg = = X64Reg_RCX ) ) | |
( ( paramIdx = = 1 ) & & ( compositeRetReg = = X64Reg_RDX ) ) )
regIdxOfs = 1 ;
auto funcType = mBeFunction - > GetFuncType ( ) ;
auto & typeParam = funcType - > mParams [ paramIdx + paramOfs ] ;
auto & param = mBeFunction - > mParams [ paramIdx + paramOfs ] ;
auto beArg = beModule - > GetArgument ( paramIdx + paramOfs ) ;
BeMCOperand mcOperand ;
mcOperand . mReg = X64Reg_None ;
mcOperand . mKind = BeMCOperandKind_NativeReg ;
int regIdx = paramIdx + regIdxOfs ;
if ( typeParam . mType - > IsFloat ( ) )
{
switch ( regIdx )
{
case 0 :
mcOperand . mReg = X64Reg_XMM0_f64 ;
break ;
case 1 :
mcOperand . mReg = X64Reg_XMM1_f64 ;
break ;
case 2 :
mcOperand . mReg = X64Reg_XMM2_f64 ;
break ;
case 3 :
mcOperand . mReg = X64Reg_XMM3_f64 ;
break ;
}
}
else
{
switch ( regIdx )
{
case 0 :
mcOperand . mReg = ! flipFirstRegs ? X64Reg_RCX : X64Reg_RDX ;
break ;
case 1 :
mcOperand . mReg = ! flipFirstRegs ? X64Reg_RDX : X64Reg_RCX ;
break ;
case 2 :
mcOperand . mReg = X64Reg_R8 ;
break ;
case 3 :
mcOperand . mReg = X64Reg_R9 ;
break ;
}
}
if ( mcOperand . mReg ! = X64Reg_None )
{
mParamsUsedRegs . push_back ( mcOperand . mReg ) ;
mcOperand . mReg = ResizeRegister ( mcOperand . mReg , typeParam . mType - > mSize ) ;
}
BeMCOperand paramVReg ;
/*if ((paramIdx == 0) && (mBeFunction->mStructRet))
{
auto ptrType = ( BePointerType * ) typeParam . mType ;
BF_ASSERT ( ptrType - > mTypeCode = = BeTypeCode_Pointer ) ;
paramVReg = AllocVirtualReg ( ptrType - > mElementType ) ;
paramVReg . mKind = BeMCOperandKind_VRegAddr ;
//paramVReg.mR
}
else */
paramVReg = AllocVirtualReg ( typeParam . mType ) ;
auto paramVRegInfo = GetVRegInfo ( paramVReg ) ;
if ( ( mBeFunction - > HasStructRet ( ) ) & & ( paramIdx = = 0 ) )
{
paramVRegInfo - > SetRetVal ( ) ;
}
else
paramVRegInfo - > mForceMerge = true ;
CreateDefineVReg ( paramVReg ) ;
if ( mcOperand . mReg ! = X64Reg_None )
{
// This indirection allows us to NOT directly use a register for a parameter
// if the cost of saving/restoring this volatile reg is too great
paramVRegInfo - > mNaturalReg = mcOperand . mReg ;
AllocInst ( BeMCInstKind_Mov , paramVReg , mcOperand ) ;
}
else
{
paramVRegInfo - > mMustExist = true ;
paramVRegInfo - > mForceMem = true ;
paramVRegInfo - > mFrameOffset = paramIdx * 8 + 8 ;
CreateDefineVReg ( paramVReg ) ;
}
//paramVRegInfo->mDbgVariable = mDbgFunction->mParams[paramIdx];
mValueToOperand [ beArg ] = paramVReg ;
}
}
void BeMCContext : : ToString ( BeMCInst * inst , String & str , bool showVRegFlags , bool showVRegDetails )
{
if ( inst - > mKind = = BeMCInstKind_Label )
{
str + = ToString ( inst - > mArg0 ) ;
str + = " : " ;
}
else
{
str + = " " ;
if ( inst - > mResult )
{
str + = ToString ( inst - > mResult ) ;
str + = " = " ;
}
str + = gOpName [ ( int ) inst - > mKind ] ;
if ( inst - > mKind = = BeMCInstKind_DbgDecl )
{
auto vregInfo = GetVRegInfo ( inst - > mArg0 ) ;
if ( ( vregInfo ! = NULL ) & & ( vregInfo - > mDbgVariable ! = NULL ) )
{
if ( vregInfo - > mDbgVariable - > mIsValue )
str + = " <value> " ;
else
str + = " <addr> " ;
}
}
if ( inst - > mKind = = BeMCInstKind_DefPhi )
{
str + = " " ;
str + = ToString ( inst - > mArg0 ) ;
for ( auto & val : inst - > mArg0 . mPhi - > mValues )
{
str + = " , [ " ;
str + = ToString ( BeMCOperand : : FromBlock ( val . mBlockFrom ) ) ;
str + = " , " ;
str + = ToString ( val . mValue ) ;
str + = " ] " ;
}
}
else if ( inst - > mKind = = BeMCInstKind_Load )
{
str + = " " ;
str + = ToString ( inst - > mArg0 ) ;
str + = " , [ " ;
str + = ToString ( inst - > mArg1 ) ;
str + = " ] " ;
}
else if ( inst - > mKind = = BeMCInstKind_Store )
{
str + = " [ " ;
str + = ToString ( inst - > mArg0 ) ;
str + = " ], " ;
str + = ToString ( inst - > mArg1 ) ;
}
else if ( inst - > mArg0 . mKind ! = BeMCOperandKind_None )
{
str + = " " ;
str + = ToString ( inst - > mArg0 ) ;
if ( inst - > mArg1 . mKind ! = BeMCOperandKind_None )
{
str + = " , " ;
str + = ToString ( inst - > mArg1 ) ;
}
}
}
if ( inst - > IsDef ( ) )
{
auto vregInfo = mVRegInfo [ inst - > mArg0 . mVRegIdx ] ;
if ( vregInfo - > mRefCount ! = - 1 )
str + = StrFormat ( " : %d refs " , vregInfo - > mRefCount ) ;
}
/*if (inst->mKind == BeMCInstKind_Def)
{
str + = " " ;
str + = mModule - > ToString ( GetType ( inst - > mArg0 ) ) ;
} */
bool hadSemi = false ;
if ( inst - > mDbgLoc ! = NULL )
{
str + = StrFormat ( " ; @%d[%d:%d] " , inst - > mDbgLoc - > mIdx , inst - > mDbgLoc - > mLine + 1 , inst - > mDbgLoc - > mColumn + 1 ) ;
hadSemi = true ;
}
bool showLiveness = showVRegFlags ;
if ( ( showLiveness ) & & ( inst - > mLiveness ! = NULL ) )
{
if ( ! hadSemi )
{
str + = " ; " ;
hadSemi = true ;
}
bool isFirstInScope = true ;
str + = " live: " ;
int vregIdx = - 1 ;
for ( int nextVRegIdx : * inst - > mLiveness )
{
if ( nextVRegIdx > = mLivenessContext . mNumItems )
{
vregIdx = nextVRegIdx ;
int showVRegIdx = vregIdx - mLivenessContext . mNumItems ;
if ( ! mLivenessContext . IsSet ( inst - > mLiveness , showVRegIdx ) )
str + = StrFormat ( " , %d* " , showVRegIdx ) ;
continue ;
}
if ( vregIdx ! = - 1 )
str + = " , " ;
vregIdx = nextVRegIdx ;
str + = StrFormat ( " %d " , vregIdx ) ;
if ( showVRegDetails )
{
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mForceReg )
str + = " r " ;
if ( vregInfo - > mForceMem )
str + = " m " ;
if ( vregInfo - > mSpilled )
str + = " s " ;
}
}
if ( inst - > mLiveness - > mNumChanges > 0 )
{
str + = " | " ;
for ( int changeIdx = 0 ; changeIdx < inst - > mLiveness - > mNumChanges ; changeIdx + + )
{
if ( changeIdx ! = 0 )
str + = " , " ;
int vregIdx = inst - > mLiveness - > GetChange ( changeIdx ) ;
if ( vregIdx > = 0 )
str + = StrFormat ( " +%d " , vregIdx ) ;
else
str + = StrFormat ( " -%d " , - vregIdx - 1 ) ;
}
}
}
bool showInitialized = showVRegFlags ;
if ( ( showInitialized ) & & ( inst - > mVRegsInitialized ! = NULL ) )
{
if ( ! hadSemi )
{
str + = " ; " ;
hadSemi = true ;
}
bool isFirstUninit = true ;
str + = " init: " ;
int vregIdx = - 1 ;
for ( int nextVRegIdx : * inst - > mVRegsInitialized )
{
if ( nextVRegIdx > = mVRegInitializedContext . mNumItems )
{
if ( isFirstUninit )
str + = " uninit: " ;
else
str + = " , " ;
isFirstUninit = false ;
vregIdx = nextVRegIdx ;
str + = StrFormat ( " %d " , vregIdx - mVRegInitializedContext . mNumItems ) ;
}
else
{
if ( vregIdx ! = - 1 )
str + = " , " ;
vregIdx = nextVRegIdx ;
str + = StrFormat ( " %d " , vregIdx ) ;
auto vregInfo = mVRegInfo [ vregIdx ] ;
if ( vregInfo - > mValueScopeRetainedKind = = BeMCValueScopeRetainKind_Soft )
str + = " r " ;
else if ( vregInfo - > mValueScopeRetainedKind = = BeMCValueScopeRetainKind_Hard )
str + = " rh " ;
}
}
if ( inst - > mVRegsInitialized - > mNumChanges > 0 )
{
str + = " | " ;
for ( int changeIdx = 0 ; changeIdx < inst - > mVRegsInitialized - > mNumChanges ; changeIdx + + )
{
if ( changeIdx ! = 0 )
str + = " , " ;
int vregIdx = inst - > mVRegsInitialized - > GetChange ( changeIdx ) ;
if ( vregIdx > = 0 )
str + = StrFormat ( " +%d " , vregIdx ) ;
else
str + = StrFormat ( " -%d " , - vregIdx - 1 ) ;
}
}
}
str + = " \n " ;
}
String BeMCContext : : ToString ( bool showVRegFlags , bool showVRegDetails )
{
String str ;
str + = mBeFunction - > mName ;
str + = " \n " ;
str + = StrFormat ( " Stack Size: 0x%X \n " , mStackSize ) ;
str + = " Frame Objects: \n " ;
for ( int vregIdx = 0 ; vregIdx < ( int ) mVRegInfo . size ( ) ; vregIdx + + )
{
int showVRegIdx = vregIdx ;
auto vregInfo = mVRegInfo [ showVRegIdx ] ;
if ( ( vregInfo - > mIsRetVal ) & & ( mCompositeRetVRegIdx ! = - 1 ) )
{
showVRegIdx = mCompositeRetVRegIdx ;
vregInfo = mVRegInfo [ showVRegIdx ] ;
}
if ( vregInfo - > mFrameOffset ! = INT_MIN )
{
str + = " " ;
str + = ToString ( BeMCOperand : : FromVReg ( vregIdx ) ) ;
2019-11-26 13:11:17 -08:00
str + = StrFormat ( " : size=%d, align=%d, at " , vregInfo - > mType - > mSize , vregInfo - > mAlign ) ;
2019-08-23 11:56:54 -07:00
X64CPURegister reg ;
int offset ;
GetValAddr ( BeMCOperand : : FromVRegAddr ( showVRegIdx ) , reg , offset ) ;
str + = " [ " ;
str + = X64CPURegisters : : GetRegisterName ( reg ) ;
if ( offset ! = 0 )
str + = StrFormat ( " + 0x%X " , offset ) ;
str + = " ] " ;
str + = " \n " ;
}
}
str + = " \n " ;
for ( int blockIdx = 0 ; blockIdx < ( int ) mBlocks . size ( ) ; blockIdx + + )
{
auto mcBlock = mBlocks [ blockIdx ] ;
if ( blockIdx > 0 )
str + = " \n " ;
if ( mBlocks . size ( ) > 1 )
{
str + = mcBlock - > mName ;
str + = " : " ;
if ( mcBlock - > mIsLooped )
str + = " ; looped " ;
str + = " ; preds = " ;
for ( int predIdx = 0 ; predIdx < ( int ) mcBlock - > mPreds . size ( ) ; predIdx + + )
{
if ( predIdx ! = 0 )
str + = " , " ;
str + = " % " ;
str + = mcBlock - > mPreds [ predIdx ] - > mName ;
}
///
str + = " ; succs = " ;
for ( int succIdx = 0 ; succIdx < ( int ) mcBlock - > mSuccs . size ( ) ; succIdx + + )
{
if ( succIdx ! = 0 )
str + = " , " ;
str + = " % " ;
str + = mcBlock - > mSuccs [ succIdx ] - > mName ;
}
//
str + = " \n " ;
}
for ( auto inst : mcBlock - > mInstructions )
ToString ( inst , str , showVRegFlags , showVRegDetails ) ;
}
str + = " \n " ;
return str ;
}
void BeMCContext : : Print ( )
{
OutputDebugStr ( ToString ( true , false ) ) ;
}
void BeMCContext : : Print ( bool showVRegFlags , bool showVRegDetails )
{
OutputDebugStr ( ToString ( showVRegFlags , showVRegDetails ) ) ;
}
BeMCOperand BeMCContext : : AllocBinaryOp ( BeMCInstKind instKind , const BeMCOperand & lhs , const BeMCOperand & rhs , BeMCBinIdentityKind identityKind )
{
if ( ( lhs . IsImmediate ( ) ) & & ( lhs . mKind = = rhs . mKind ) )
{
if ( instKind = = BeMCInstKind_Add )
{
BeMCOperand result ;
result . mKind = lhs . mKind ;
switch ( lhs . mKind )
{
case BeMCOperandKind_Immediate_i32 :
result . mImmediate = lhs . mImmediate + rhs . mImmediate ;
return result ;
}
}
}
if ( identityKind = = BeMCBinIdentityKind_Any_IsOne )
{
if ( ( ( lhs . IsImmediateFloat ( ) ) & & ( lhs . mImmFloat = = 1.0 ) ) | |
( ( lhs . IsImmediateInt ( ) ) & & ( lhs . mImmediate = = 1 ) ) )
return rhs ;
}
if ( identityKind = = BeMCBinIdentityKind_Right_IsOne_Result_Zero )
{
if ( ( ( rhs . IsImmediateFloat ( ) ) & & ( rhs . mImmFloat = = 1.0 ) ) | |
( ( rhs . IsImmediateInt ( ) ) & & ( rhs . mImmediate = = 1 ) ) )
{
BeMCOperand operand = rhs ;
operand . mImmediate = 0 ;
return operand ;
}
}
if ( ( identityKind = = BeMCBinIdentityKind_Right_IsOne ) | | ( identityKind = = BeMCBinIdentityKind_Any_IsOne ) )
{
if ( ( ( rhs . IsImmediateFloat ( ) ) & & ( rhs . mImmFloat = = 1.0 ) ) | |
( ( rhs . IsImmediateInt ( ) ) & & ( rhs . mImmediate = = 1 ) ) )
return lhs ;
}
if ( ( identityKind = = BeMCBinIdentityKind_Right_IsZero ) | | ( identityKind = = BeMCBinIdentityKind_Any_IsZero ) )
{
if ( ( ( rhs . IsImmediateFloat ( ) ) & & ( rhs . mImmFloat = = 0.0 ) ) | |
( ( rhs . IsImmediateInt ( ) ) & & ( rhs . mImmediate = = 0 ) ) )
return lhs ;
}
if ( identityKind = = BeMCBinIdentityKind_Any_IsZero )
{
if ( ( ( lhs . IsImmediateFloat ( ) ) & & ( lhs . mImmFloat = = 0.0 ) ) | |
( ( lhs . IsImmediateInt ( ) ) & & ( lhs . mImmediate = = 0 ) ) )
return rhs ;
}
auto result = AllocVirtualReg ( GetType ( lhs ) ) ;
AllocInst ( BeMCInstKind_Def , result ) ;
auto mcInst = AllocInst ( instKind , lhs , rhs ) ;
mcInst - > mResult = result ;
return result ;
}
void BeMCContext : : Generate ( BeFunction * function )
{
BP_ZONE_F ( " BeMCContext::Generate %s " , function - > mName . c_str ( ) ) ;
mBeFunction = function ;
mDbgFunction = mBeFunction - > mDbgFunction ;
mModule = function - > mModule ;
//mDbgPreferredRegs[15] = X64Reg_RCX;
//mDbgPreferredRegs[7] = X64Reg_RCX;
/*mDbgPreferredRegs[14] = X64Reg_RAX;
mDbgPreferredRegs [ 15 ] = X64Reg_None ;
mDbgPreferredRegs [ 19 ] = X64Reg_None ;
mDbgPreferredRegs [ 31 ] = X64Reg_R8 ;
mDbgPreferredRegs [ 32 ] = X64Reg_R8 ; */
//mDbgPreferredRegs[8] = X64Reg_RAX;
2019-11-26 13:11:17 -08:00
mDebugging = function - > mName = =
//"?TestPrimitives@Nullable@Tests@bf@@SAXXZ"
" ?TestAlloc@Blurg@bf@@SAXXZ " ;
2019-08-23 11:56:54 -07:00
//"?Main@Program@bf@@CAHPEAV?$Array1@PEAVString@System@bf@@@System@2@@Z";
//"?Hey@Blurg@bf@@SAXXZ";
//"?get__Value@?$Nullable@ULineAndColumn@EditWidgetContent@widgets@Beefy@bf@@@System@bf@@QEAAULineAndColumn@EditWidgetContent@widgets@Beefy@3@XZ";
//"?__BfCtor@StructA@bf@@QEAAXXZ";
if ( mDebugging )
{
mModule - > Print ( mBeFunction ) ;
}
for ( auto beBlock : function - > mBlocks )
{
int blockIdx = ( int ) mMCBlockAlloc . size ( ) ;
auto mcBlock = mMCBlockAlloc . Alloc ( ) ;
mcBlock - > mName = beBlock - > mName + StrFormat ( " :%d " , blockIdx ) ;
mcBlock - > mBlockIdx = blockIdx ;
mcBlock - > mMaxDeclBlockId = blockIdx ;
BeMCOperand mcOperand ;
mcOperand . mKind = BeMCOperandKind_Block ;
mcOperand . mBlock = mcBlock ;
mValueToOperand [ beBlock ] = mcOperand ;
mBlocks . push_back ( mcBlock ) ;
}
SizedArray < int , 64 > dbgVarsAwaitingEnd ;
BeMDNode * curDbgScope = NULL ;
bool inHeadAlloca = true ;
SizedArray < int , 64 > stackSaveVRegs ;
// Scan pass
2019-11-26 13:11:17 -08:00
mMaxCallParamCount = 4 ;
2019-08-23 11:56:54 -07:00
for ( int blockIdx = 0 ; blockIdx < ( int ) function - > mBlocks . size ( ) ; blockIdx + + )
{
auto beBlock = function - > mBlocks [ blockIdx ] ;
auto mcBlock = mBlocks [ blockIdx ] ;
for ( int instIdx = 0 ; instIdx < ( int ) beBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = beBlock - > mInstructions [ instIdx ] ;
int instType = inst - > GetTypeId ( ) ;
switch ( instType )
{
case BeAllocaInst : : TypeId :
{
2019-11-26 13:11:17 -08:00
auto castedInst = ( BeAllocaInst * ) inst ;
if ( ( ! inHeadAlloca ) | | ( castedInst - > mAlign > 16 ) )
2019-08-23 11:56:54 -07:00
mUseBP = true ;
}
break ;
case BeNumericCastInst : : TypeId :
case BeBitCastInst : : TypeId :
break ;
case BeStackSaveInst : : TypeId :
{
auto stackVReg = AllocVirtualReg ( mNativeIntType ) ;
stackSaveVRegs . push_back ( stackVReg . mVRegIdx ) ;
}
break ;
2019-11-26 13:11:17 -08:00
case BeCallInst : : TypeId :
{
auto castedInst = ( BeCallInst * ) inst ;
mMaxCallParamCount = BF_MAX ( mMaxCallParamCount , ( int ) castedInst - > mArgs . size ( ) ) ;
}
break ;
2019-08-23 11:56:54 -07:00
default :
inHeadAlloca = false ;
break ;
}
}
}
int retCount = 0 ;
bool isFirstBlock = true ;
inHeadAlloca = true ;
SizedArray < int , 64 > valueScopeStack ;
// Generate pass
for ( int blockIdx = 0 ; blockIdx < ( int ) function - > mBlocks . size ( ) ; blockIdx + + )
{
auto beBlock = function - > mBlocks [ blockIdx ] ;
auto mcBlock = mBlocks [ blockIdx ] ;
mActiveBeBlock = beBlock ;
mActiveBlock = mcBlock ;
if ( isFirstBlock )
HandleParams ( ) ;
for ( int instIdx = 0 ; instIdx < ( int ) beBlock - > mInstructions . size ( ) ; instIdx + + )
{
auto inst = beBlock - > mInstructions [ instIdx ] ;
BeMCOperand result ;
mCurDbgLoc = inst - > mDbgLoc ;
int instType = inst - > GetTypeId ( ) ;
switch ( instType )
{
case BeAllocaInst : : TypeId :
case BeNumericCastInst : : TypeId :
case BeBitCastInst : : TypeId :
break ;
default :
inHeadAlloca = false ;
break ;
}
switch ( instType )
{
case BeNopInst : : TypeId :
{
auto mcInst = AllocInst ( ) ;
mcInst - > mKind = BeMCInstKind_Nop ;
}
break ;
case BeUnreachableInst : : TypeId :
{
auto mcInst = AllocInst ( ) ;
mcInst - > mKind = BeMCInstKind_Unreachable ;
}
break ;
case BeEnsureInstructionAtInst : : TypeId :
{
auto mcInst = AllocInst ( ) ;
mcInst - > mKind = BeMCInstKind_EnsureInstructionAt ;
}
break ;
case BeUndefValueInst : : TypeId :
{
auto castedInst = ( BeUndefValueInst * ) inst ;
result = AllocVirtualReg ( castedInst - > mType ) ;
CreateDefineVReg ( result ) ;
}
break ;
case BeExtractValueInst : : TypeId :
{
auto castedInst = ( BeExtractValueInst * ) inst ;
BeConstant * constant = BeValueDynCast < BeConstant > ( castedInst - > mAggVal ) ;
BeMCOperand mcAgg ;
if ( constant = = NULL )
{
mcAgg = GetOperand ( castedInst - > mAggVal ) ;
if ( mcAgg . mKind = = BeMCOperandKind_ConstAgg )
{
constant = mcAgg . mConstant ;
}
}
if ( constant ! = NULL )
{
result . mImmediate = 0 ;
BeType * wantDefaultType = NULL ;
if ( constant - > mType - > IsStruct ( ) )
{
BeStructType * structType = ( BeStructType * ) constant - > mType ;
auto & member = structType - > mMembers [ castedInst - > mIdx ] ;
wantDefaultType = member . mType ;
}
else if ( constant - > mType - > IsSizedArray ( ) )
{
BeSizedArrayType * arrayType = ( BeSizedArrayType * ) constant - > mType ;
wantDefaultType = arrayType - > mElementType ;
}
if ( wantDefaultType ! = NULL )
{
switch ( wantDefaultType - > mTypeCode )
{
case BeTypeCode_Boolean :
case BeTypeCode_Int8 :
result . mKind = BeMCOperandKind_Immediate_i8 ;
break ;
case BeTypeCode_Int16 :
result . mKind = BeMCOperandKind_Immediate_i16 ;
break ;
case BeTypeCode_Int32 :
result . mKind = BeMCOperandKind_Immediate_i32 ;
break ;
case BeTypeCode_Int64 :
result . mKind = BeMCOperandKind_Immediate_i64 ;
break ;
case BeTypeCode_Float :
result . mKind = BeMCOperandKind_Immediate_f32 ;
break ;
case BeTypeCode_Double :
result . mKind = BeMCOperandKind_Immediate_f64 ;
break ;
case BeTypeCode_Pointer :
result . mKind = BeMCOperandKind_Immediate_Null ;
result . mType = wantDefaultType ;
break ;
case BeTypeCode_Struct :
case BeTypeCode_SizedArray :
{
auto subConst = mAlloc . Alloc < BeConstant > ( ) ;
subConst - > mType = wantDefaultType ;
result . mConstant = subConst ;
result . mKind = BeMCOperandKind_ConstAgg ;
}
break ;
default :
NotImpl ( ) ;
}
}
break ;
}
auto aggType = GetType ( mcAgg ) ;
BF_ASSERT ( aggType - > IsStruct ( ) ) ;
BeStructType * structType = ( BeStructType * ) aggType ;
auto & structMember = structType - > mMembers [ castedInst - > mIdx ] ;
if ( mcAgg . mKind = = BeMCOperandKind_VReg )
mcAgg . mKind = BeMCOperandKind_VRegAddr ;
else if ( mcAgg . mKind = = BeMCOperandKind_VRegLoad )
mcAgg . mKind = BeMCOperandKind_VReg ;
else
NotImpl ( ) ;
auto memberPtrType = mModule - > mContext - > GetPointerTo ( structMember . mType ) ;
result = AllocRelativeVirtualReg ( memberPtrType , mcAgg , BeMCOperand : : FromImmediate ( structMember . mByteOffset ) , 1 ) ;
result . mKind = BeMCOperandKind_VRegLoad ;
CreateDefineVReg ( result ) ;
}
break ;
case BeInsertValueInst : : TypeId :
{
auto castedInst = ( BeInsertValueInst * ) inst ;
auto mcAgg = GetOperand ( castedInst - > mAggVal ) ;
auto mcValue = GetOperand ( castedInst - > mMemberVal ) ;
auto aggType = GetType ( mcAgg ) ;
BF_ASSERT ( aggType - > IsStruct ( ) ) ;
BeStructType * structType = ( BeStructType * ) aggType ;
auto & structMember = structType - > mMembers [ castedInst - > mIdx ] ;
BF_ASSERT ( mcAgg . mKind = BeMCOperandKind_VReg ) ;
auto mcAggRef = mcAgg ;
mcAggRef . mKind = BeMCOperandKind_VRegAddr ;
auto memberPtrType = mModule - > mContext - > GetPointerTo ( structMember . mType ) ;
auto mcMemberRef = AllocRelativeVirtualReg ( memberPtrType , mcAggRef , BeMCOperand : : FromImmediate ( structMember . mByteOffset ) , 1 ) ;
CreateDefineVReg ( mcMemberRef ) ;
mcMemberRef . mKind = BeMCOperandKind_VRegLoad ;
AllocInst ( BeMCInstKind_Mov , mcMemberRef , mcValue ) ;
// Our InsertValue always modifies the source aggregrate, it does not make a copy like LLVM's InsertValue would infer.
// This is okay because of Beef front end knowledge, but is not general purpose.
result = mcAgg ;
}
break ;
case BeNumericCastInst : : TypeId :
{
auto castedInst = ( BeNumericCastInst * ) inst ;
auto mcValue = GetOperand ( castedInst - > mValue ) ;
auto fromType = GetType ( mcValue ) ;
if ( fromType = = castedInst - > mToType )
{
// If it's just a sign change then leave it alone
result = mcValue ;
}
else
{
auto toType = castedInst - > mToType ;
auto toValue = AllocVirtualReg ( castedInst - > mToType ) ;
CreateDefineVReg ( toValue ) ;
if ( ( toType - > IsInt ( ) ) & & ( fromType - > IsInt ( ) ) & & ( toType - > mSize < fromType - > mSize ) )
{
// For truncating values, no actual instructions are needed, so we can just do a vreg relto ref
auto vregInfo = mVRegInfo [ toValue . mVRegIdx ] ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = mcValue ;
}
else
{
bool doSignExtension = ( toType - > IsInt ( ) ) & & ( fromType - > IsInt ( ) ) & & ( toType - > mSize > fromType - > mSize ) & & ( castedInst - > mToSigned ) & & ( castedInst - > mValSigned ) ;
if ( ( toType - > IsFloat ( ) ) & & ( fromType - > IsInt ( ) ) & & ( castedInst - > mValSigned ) )
doSignExtension = true ;
if ( doSignExtension )
{
AllocInst ( BeMCInstKind_MovSX , toValue , mcValue ) ;
}
else
AllocInst ( BeMCInstKind_Mov , toValue , mcValue ) ;
}
result = toValue ;
}
}
break ;
case BeNegInst : : TypeId :
{
auto castedInst = ( BeNumericCastInst * ) inst ;
auto mcValue = GetOperand ( castedInst - > mValue ) ;
result = AllocVirtualReg ( GetType ( mcValue ) ) ;
CreateDefineVReg ( result ) ;
AllocInst ( BeMCInstKind_Mov , result , mcValue ) ;
AllocInst ( BeMCInstKind_Neg , result ) ;
}
break ;
case BeNotInst : : TypeId :
{
auto castedInst = ( BeNumericCastInst * ) inst ;
auto mcValue = GetOperand ( castedInst - > mValue , true ) ;
// Phi's are easy - just make a new one with the true and false branches swapped
// if (mcValue.mKind == BeMCOperandKind_Phi)
// {
// BeMCPhi* origPhi = mcValue.mPhi;
// BeMCPhi* newPhi = mPhiAlloc.Alloc();
//
// *newPhi = *origPhi;
// BF_SWAP(newPhi->mBrTrue, newPhi->mBrFalse);
// result.mKind = BeMCOperandKind_Phi;
// result.mPhi = newPhi;
// break;
// }
//
// if (mcValue.mKind == BeMCOperandKind_CmpResult)
// {
// auto origCmpResult = mCmpResults[mcValue.mCmpResultIdx];
//
// auto cmpResultIdx = (int)mCmpResults.size();
// BeCmpResult cmpResult;
// cmpResult.mCmpKind = BeModule::InvertCmp(origCmpResult.mCmpKind);
// mCmpResults.push_back(cmpResult);
// result.mKind = BeMCOperandKind_CmpResult;
// result.mCmpResultIdx = cmpResultIdx;
// break;
// }
if ( mcValue . mKind = = BeMCOperandKind_NotResult )
{
// Double negative! Just unwrap the NotResult.
result = GetOperand ( mcValue . mNotResult - > mValue , true ) ;
break ;
}
else if ( ( mcValue . mKind = = BeMCOperandKind_Phi ) | | ( mcValue . mKind = = BeMCOperandKind_CmpResult ) )
{
auto notResult = mAlloc . Alloc < BeNotResult > ( ) ;
notResult - > mValue = castedInst - > mValue ;
result . mKind = BeMCOperandKind_NotResult ;
result . mNotResult = notResult ;
break ;
}
// LLVM does a weird thing for Not: val = (val ^ 0xFF) & 1
// Which turns a '2' into a '1' which is True to True - non-conformant to C standard?
// Visual Studio does an actual conditional branch. For Beef, bools are defined as 1 or 0.
/*result = AllocVirtualReg(GetType(mcValue));
CreateDefineVReg ( result ) ;
AllocInst ( BeMCInstKind_Mov , result , mcValue ) ;
BeMCOperand xorVal ;
xorVal . mKind = BeMCOperandKind_Immediate_i8 ;
xorVal . mImmediate = 0xFF ;
AllocInst ( BeMCInstKind_Xor , result , xorVal ) ;
BeMCOperand andVal ;
andVal . mKind = BeMCOperandKind_Immediate_i8 ;
andVal . mImmediate = 0x1 ;
AllocInst ( BeMCInstKind_And , result , andVal ) ; */
auto type = castedInst - > mValue - > GetType ( ) ;
result = AllocVirtualReg ( GetType ( mcValue ) ) ;
CreateDefineVReg ( result ) ;
AllocInst ( BeMCInstKind_Mov , result , mcValue ) ;
if ( type - > mTypeCode = = BeTypeCode_Boolean )
{
BeMCOperand xorVal ;
xorVal . mKind = BeMCOperandKind_Immediate_i8 ;
xorVal . mImmediate = 0x1 ;
AllocInst ( BeMCInstKind_Xor , result , xorVal ) ;
}
else
{
AllocInst ( BeMCInstKind_Not , result ) ;
}
}
break ;
case BeBinaryOpInst : : TypeId :
{
auto castedInst = ( BeBinaryOpInst * ) inst ;
auto mcLHS = GetOperand ( castedInst - > mLHS ) ;
auto mcRHS = GetOperand ( castedInst - > mRHS ) ;
if ( castedInst - > mOpKind = = BeBinaryOpKind_Subtract )
{
if ( ( ( mcLHS . IsImmediateFloat ( ) ) & & ( mcLHS . mImmFloat = = 0.0 ) ) | |
( ( mcLHS . IsImmediateInt ( ) ) & & ( mcLHS . mImmediate = = 0 ) ) )
{
auto castedInst = ( BeNumericCastInst * ) inst ;
result = AllocVirtualReg ( GetType ( mcRHS ) ) ;
CreateDefineVReg ( result ) ;
AllocInst ( BeMCInstKind_Mov , result , mcRHS ) ;
AllocInst ( BeMCInstKind_Neg , result ) ;
break ;
}
}
switch ( castedInst - > mOpKind )
{
case BeBinaryOpKind_Add : result = AllocBinaryOp ( BeMCInstKind_Add , mcLHS , mcRHS , BeMCBinIdentityKind_Any_IsZero ) ; break ;
case BeBinaryOpKind_Subtract : result = AllocBinaryOp ( BeMCInstKind_Sub , mcLHS , mcRHS , BeMCBinIdentityKind_Right_IsZero ) ; break ;
case BeBinaryOpKind_Multiply : result = AllocBinaryOp ( BeMCInstKind_IMul , mcLHS , mcRHS , BeMCBinIdentityKind_Any_IsOne ) ; break ;
case BeBinaryOpKind_SDivide : result = AllocBinaryOp ( BeMCInstKind_IDiv , mcLHS , mcRHS , BeMCBinIdentityKind_Right_IsOne ) ; break ;
case BeBinaryOpKind_UDivide : result = AllocBinaryOp ( BeMCInstKind_Div , mcLHS , mcRHS , BeMCBinIdentityKind_Right_IsOne ) ; break ;
case BeBinaryOpKind_SModulus : result = AllocBinaryOp ( BeMCInstKind_IRem , mcLHS , mcRHS , BeMCBinIdentityKind_Right_IsOne_Result_Zero ) ; break ;
case BeBinaryOpKind_UModulus : result = AllocBinaryOp ( BeMCInstKind_Rem , mcLHS , mcRHS , BeMCBinIdentityKind_Right_IsOne_Result_Zero ) ; break ;
case BeBinaryOpKind_BitwiseAnd : result = AllocBinaryOp ( BeMCInstKind_And , mcLHS , mcRHS , BeMCBinIdentityKind_None ) ; break ;
case BeBinaryOpKind_BitwiseOr : result = AllocBinaryOp ( BeMCInstKind_Or , mcLHS , mcRHS , BeMCBinIdentityKind_Any_IsZero ) ; break ;
case BeBinaryOpKind_ExclusiveOr : result = AllocBinaryOp ( BeMCInstKind_Xor , mcLHS , mcRHS , BeMCBinIdentityKind_Any_IsZero ) ; break ;
case BeBinaryOpKind_LeftShift : result = AllocBinaryOp ( BeMCInstKind_Shl , mcLHS , mcRHS , BeMCBinIdentityKind_Any_IsZero ) ; break ;
case BeBinaryOpKind_RightShift : result = AllocBinaryOp ( BeMCInstKind_Shr , mcLHS , mcRHS , BeMCBinIdentityKind_Any_IsZero ) ; break ;
case BeBinaryOpKind_ARightShift : result = AllocBinaryOp ( BeMCInstKind_Sar , mcLHS , mcRHS , BeMCBinIdentityKind_Any_IsZero ) ; break ;
}
}
break ;
case BeBitCastInst : : TypeId :
{
auto castedInst = ( BeBitCastInst * ) inst ;
auto mcValue = GetOperand ( castedInst - > mValue ) ;
if ( castedInst - > mToType - > IsInt ( ) )
{
BF_ASSERT ( castedInst - > mToType - > mSize = = 8 ) ;
}
else
BF_ASSERT ( castedInst - > mToType - > IsPointer ( ) ) ;
auto toType = castedInst - > mToType ;
if ( mcValue . IsImmediate ( ) )
{
if ( mcValue . mImmediate = = 0 )
{
BeMCOperand newImmediate ;
newImmediate . mKind = BeMCOperandKind_Immediate_Null ;
newImmediate . mType = toType ;
result = newImmediate ;
}
else
{
// Non-zero constant. Weird case, just do an actual MOV
result = AllocVirtualReg ( toType ) ;
CreateDefineVReg ( result ) ;
auto vregInfo = GetVRegInfo ( result ) ;
AllocInst ( BeMCInstKind_Mov , result , mcValue ) ;
if ( mcValue . mKind = = BeMCOperandKind_VRegAddr )
{
auto srcVRegInfo = GetVRegInfo ( mcValue ) ;
srcVRegInfo - > mForceMem = true ;
CheckForce ( srcVRegInfo ) ;
}
}
}
else
{
result = AllocVirtualReg ( toType ) ;
CreateDefineVReg ( result ) ;
auto vregInfo = GetVRegInfo ( result ) ;
vregInfo - > mRelTo = mcValue ;
vregInfo - > mIsExpr = true ;
if ( mcValue . mKind = = BeMCOperandKind_VRegAddr )
{
auto srcVRegInfo = GetVRegInfo ( mcValue ) ;
srcVRegInfo - > mForceMem = true ;
CheckForce ( srcVRegInfo ) ;
}
}
}
break ;
case BeCmpInst : : TypeId :
{
auto castedInst = ( BeCmpInst * ) inst ;
auto mcLHS = GetOperand ( castedInst - > mLHS ) ;
auto mcRHS = GetOperand ( castedInst - > mRHS ) ;
auto mcInst = AllocInst ( BeMCInstKind_Cmp , mcLHS , mcRHS ) ;
auto cmpResultIdx = ( int ) mCmpResults . size ( ) ;
BeCmpResult cmpResult ;
cmpResult . mCmpKind = castedInst - > mCmpKind ;
mCmpResults . push_back ( cmpResult ) ;
result . mKind = BeMCOperandKind_CmpResult ;
result . mCmpResultIdx = cmpResultIdx ;
mcInst - > mResult = result ;
}
break ;
case BeObjectAccessCheckInst : : TypeId :
{
auto castedInst = ( BeObjectAccessCheckInst * ) inst ;
auto mcValue = GetOperand ( castedInst - > mValue ) ;
auto int8Type = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int8 ) ;
auto int8PtrType = mModule - > mContext - > GetPointerTo ( int8Type ) ;
auto int8PtrVal = AllocVirtualReg ( int8PtrType ) ;
CreateDefineVReg ( int8PtrVal ) ;
auto vregInfo = GetVRegInfo ( int8PtrVal ) ;
vregInfo - > mRelTo = mcValue ;
vregInfo - > mIsExpr = true ;
int labelNum = mCurLabelIdx + + ;
BeMCOperand mcImm ;
mcImm . mKind = BeMCOperandKind_Immediate_i8 ;
mcImm . mImmediate = - 0x80 ;
BeMCOperand int8Val ;
int8Val . mKind = BeMCOperandKind_VRegLoad ;
int8Val . mVRegIdx = int8PtrVal . mVRegIdx ;
AllocInst ( BeMCInstKind_Cmp , int8Val , mcImm ) ;
AllocInst ( BeMCInstKind_CondBr , BeMCOperand : : FromLabel ( labelNum ) , BeMCOperand : : FromCmpKind ( BeCmpKind_ULT ) ) ;
AllocInst ( BeMCInstKind_DbgBreak ) ;
AllocInst ( BeMCInstKind_Label , BeMCOperand : : FromLabel ( labelNum ) ) ;
}
break ;
case BeAllocaInst : : TypeId :
2019-11-26 13:11:17 -08:00
{
if ( mDebugging )
{
NOP ;
}
int homeSize = BF_ALIGN ( BF_MAX ( mMaxCallParamCount , 4 ) * 8 , 16 ) ;
2019-08-23 11:56:54 -07:00
auto castedInst = ( BeAllocaInst * ) inst ;
auto mcSize = BeMCOperand : : FromImmediate ( castedInst - > mType - > mSize ) ;
bool isAligned16 = false ;
2019-11-26 13:11:17 -08:00
int align = castedInst - > mAlign ;
2019-08-23 11:56:54 -07:00
BeType * allocType = castedInst - > mType ;
bool preservedVolatiles = false ;
bool doPtrCast = false ;
if ( castedInst - > mArraySize ! = NULL )
{
auto mcArraySize = GetOperand ( castedInst - > mArraySize ) ;
if ( mcArraySize . IsImmediate ( ) )
{
mcSize . mImmediate = mcSize . mImmediate * mcArraySize . mImmediate ;
allocType = mModule - > mContext - > CreateSizedArrayType ( castedInst - > mType , mcArraySize . mImmediate ) ;
doPtrCast = true ;
}
else
{
preservedVolatiles = true ;
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
inHeadAlloca = false ;
if ( mcSize . mImmediate = = 1 )
{
mcSize = mcArraySize ;
2019-11-26 13:11:17 -08:00
}
2019-08-23 11:56:54 -07:00
else
{
auto mcInst = AllocInst ( BeMCInstKind_IMul , mcArraySize , mcSize ) ;
mcInst - > mResult = BeMCOperand : : FromReg ( X64Reg_RAX ) ;
mcSize = mcInst - > mResult ;
}
}
}
2019-11-26 13:11:17 -08:00
// The stack is 16-byte aligned on entry - we have to manually adjust for any alignment greater than that
if ( ( inHeadAlloca ) & & ( align < = 16 ) )
2019-08-23 11:56:54 -07:00
{
result = AllocVirtualReg ( allocType ) ;
auto vregInfo = mVRegInfo [ result . mVRegIdx ] ;
2019-11-26 13:11:17 -08:00
vregInfo - > mAlign = castedInst - > mAlign ;
2019-08-23 11:56:54 -07:00
vregInfo - > mHasDynLife = true ;
if ( castedInst - > mForceMem )
vregInfo - > mForceMem = true ;
if ( allocType - > IsComposite ( ) )
vregInfo - > mForceMem = true ;
result . mKind = BeMCOperandKind_VRegAddr ;
if ( doPtrCast )
{
BF_ASSERT ( allocType - > IsSizedArray ( ) ) ;
auto resultType = mModule - > mContext - > GetPointerTo ( castedInst - > mType ) ;
auto ptrResult = AllocVirtualReg ( resultType ) ;
auto vregInfo = mVRegInfo [ ptrResult . mVRegIdx ] ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = result ;
vregInfo - > mType = resultType ;
2019-11-26 13:11:17 -08:00
vregInfo - > mAlign = resultType - > mSize ;
2019-08-23 11:56:54 -07:00
CreateDefineVReg ( ptrResult ) ;
result = ptrResult ;
}
}
else
{
bool needsChkStk = ! castedInst - > mNoChkStk ;
bool doFastChkStk = false ;
if ( instIdx < ( int ) beBlock - > mInstructions . size ( ) - 1 )
{
if ( auto memSetInstr = BeValueDynCast < BeMemSetInst > ( beBlock - > mInstructions [ instIdx + 1 ] ) )
{
if ( memSetInstr - > mAddr = = inst )
{
// If we're clearing out this memory immediately after allocation then we don't
// need to do stack probing - the memset will ensure stack pages are committed
needsChkStk = false ;
}
}
}
2019-11-26 13:11:17 -08:00
int stackAlign = BF_MAX ( align , 16 ) ;
2019-08-23 11:56:54 -07:00
BeMCOperand mcFunc ;
mcFunc . mKind = BeMCOperandKind_SymbolAddr ;
mcFunc . mSymbolIdx = mCOFFObject - > GetSymbolRef ( " __chkstk " ) - > mIdx ;
if ( mcSize . IsImmediate ( ) )
{
// Align to 16 bytes
mcSize . mImmediate = ( mcSize . mImmediate + 0xF ) & ~ 0xF ;
}
if ( ( mcSize . IsImmediate ( ) ) & & ( ! preservedVolatiles ) & & ( ! needsChkStk ) )
2019-11-26 13:11:17 -08:00
{
2019-08-23 11:56:54 -07:00
AllocInst ( BeMCInstKind_Sub , BeMCOperand : : FromReg ( X64Reg_RSP ) , mcSize ) ;
}
else
{
if ( needsChkStk )
{
if ( ( mcSize . IsImmediate ( ) ) & & ( mcSize . mImmediate < 4096 ) )
{
// We can do a fast __chkstk in this case since we have a max of one page to handle
doFastChkStk = true ;
}
}
if ( doFastChkStk )
{
AllocInst ( BeMCInstKind_Sub , BeMCOperand : : FromReg ( X64Reg_RSP ) , mcSize ) ;
AllocInst ( BeMCInstKind_FastCheckStack , BeMCOperand : : FromReg ( X64Reg_RSP ) ) ;
}
else
{
if ( ! preservedVolatiles )
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
if ( mcSize . IsImmediate ( ) )
{
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_RAX ) , mcSize ) ;
// It's tempting to not do a chkstk when we do an alloc less than 4k, but this isn't valid
// because we could break the system by doing three 2k allocs and access the third one first
// and BOOM. We rely on the front-end to tell us when we can omit it.
}
else if ( ! isAligned16 )
2019-11-26 13:11:17 -08:00
{
2019-08-23 11:56:54 -07:00
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_RAX ) , mcSize ) ;
AllocInst ( BeMCInstKind_Add , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand : : FromImmediate ( 0xF ) ) ;
AllocInst ( BeMCInstKind_And , BeMCOperand : : FromReg ( X64Reg_RAX ) , BeMCOperand : : FromImmediate ( ~ 0xF ) ) ;
}
if ( ( needsChkStk ) & & ( ! doFastChkStk ) )
{
AllocInst ( BeMCInstKind_Call , mcFunc ) ;
}
2019-11-26 13:11:17 -08:00
AllocInst ( BeMCInstKind_Sub , BeMCOperand : : FromReg ( X64Reg_RSP ) , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
2019-08-23 11:56:54 -07:00
if ( doFastChkStk )
{
AllocInst ( BeMCInstKind_FastCheckStack , BeMCOperand : : FromReg ( X64Reg_RSP ) ) ;
}
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
}
}
auto resultType = mModule - > mContext - > GetPointerTo ( castedInst - > mType ) ;
auto intType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
BeMCOperand ptrValue ;
if ( mUseBP )
{
ptrValue = AllocVirtualReg ( intType ) ;
auto vregInfo = mVRegInfo [ ptrValue . mVRegIdx ] ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = BeMCOperand : : FromReg ( X64Reg_RSP ) ;
2019-11-26 13:11:17 -08:00
vregInfo - > mRelOffset = BeMCOperand : : FromImmediate ( homeSize ) ;
2019-08-23 11:56:54 -07:00
CreateDefineVReg ( ptrValue ) ;
}
else
{
ptrValue = BeMCOperand : : FromReg ( X64Reg_RSP ) ;
}
result = AllocVirtualReg ( resultType ) ;
auto vregInfo = mVRegInfo [ result . mVRegIdx ] ;
vregInfo - > mHasDynLife = true ;
CreateDefineVReg ( result ) ;
AllocInst ( BeMCInstKind_Mov , result , ptrValue ) ;
2019-11-26 13:11:17 -08:00
if ( stackAlign > 16 )
{
// We have to align after everything - note that we always have to keep the 'homeSize' space available from RSP for calls,
// so the ANDing for alignment must be done here
AllocInst ( BeMCInstKind_And , result , BeMCOperand : : FromImmediate ( ~ ( stackAlign - 1 ) ) ) ;
AllocInst ( BeMCInstKind_Sub , BeMCOperand : : FromReg ( X64Reg_RSP ) , BeMCOperand : : FromImmediate ( stackAlign - 16 ) ) ;
}
2019-08-23 11:56:54 -07:00
BF_ASSERT ( mUseBP ) ;
}
}
break ;
case BeAliasValueInst : : TypeId :
{
auto castedInst = ( BeAliasValueInst * ) inst ;
auto mcPtr = GetOperand ( castedInst - > mPtr , false , true ) ;
result = AllocVirtualReg ( GetType ( mcPtr ) ) ;
auto vregInfo = mVRegInfo [ result . mVRegIdx ] ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = mcPtr ;
vregInfo - > mHasDynLife = true ;
CreateDefineVReg ( result ) ;
}
break ;
case BeLifetimeStartInst : : TypeId :
{
auto castedInst = ( BeLifetimeEndInst * ) inst ;
auto mcPtr = GetOperand ( castedInst - > mPtr , false , true ) ;
if ( mcPtr )
AllocInst ( BeMCInstKind_LifetimeStart , mcPtr ) ;
}
break ;
case BeLifetimeExtendInst : : TypeId :
{
auto castedInst = ( BeLifetimeEndInst * ) inst ;
auto mcPtr = GetOperand ( castedInst - > mPtr , false , true ) ;
if ( mcPtr )
AllocInst ( BeMCInstKind_LifetimeExtend , mcPtr ) ;
}
break ;
case BeLifetimeEndInst : : TypeId :
{
auto castedInst = ( BeLifetimeEndInst * ) inst ;
auto mcPtr = GetOperand ( castedInst - > mPtr , false , true ) ;
if ( mcPtr . IsVRegAny ( ) )
{
AllocInst ( BeMCInstKind_LifetimeEnd , mcPtr ) ;
}
}
break ;
case BeValueScopeStartInst : : TypeId :
{
result = BeMCOperand : : FromImmediate ( ( int ) mVRegInfo . size ( ) ) ;
}
break ;
case BeValueScopeRetainInst : : TypeId :
{
auto castedInst = ( BeValueScopeRetainInst * ) inst ;
auto mcValue = GetOperand ( castedInst - > mValue , false , true ) ;
auto vregInfo = GetVRegInfo ( mcValue ) ;
vregInfo - > mValueScopeRetainedKind = BeMCValueScopeRetainKind_Soft ;
}
break ;
case BeValueScopeEndInst : : TypeId :
{
auto castedInst = ( BeValueScopeEndInst * ) inst ;
BeMCOperand mcScopeStart = GetOperand ( castedInst - > mScopeStart , false , true ) ;
// There are some recordering cases where we have a ValueScopeStart moved after a ValueScopeEnd
// Just ignore those. This is just an optimization anyway.
if ( mcScopeStart )
AllocInst ( castedInst - > mIsSoft ? BeMCInstKind_ValueScopeSoftEnd : BeMCInstKind_ValueScopeHardEnd , mcScopeStart , BeMCOperand : : FromImmediate ( ( int ) mVRegInfo . size ( ) ) ) ;
}
break ;
case BeLifetimeFenceInst : : TypeId :
{
auto castedInst = ( BeLifetimeFenceInst * ) inst ;
auto mcPtr = GetOperand ( castedInst - > mPtr , false ) ;
auto fenceBlock = GetOperand ( castedInst - > mFenceBlock ) ;
auto vregInfo = GetVRegInfo ( mcPtr ) ;
vregInfo - > mChainLifetimeEnd = true ;
SetAndRestoreValue < BeMCBlock * > prevBlock ( mActiveBlock , fenceBlock . mBlock ) ;
auto lifetimeStart = AllocInst ( BeMCInstKind_LifetimeStart , mcPtr ) ;
lifetimeStart - > mDbgLoc = NULL ;
}
break ;
case BeLoadInst : : TypeId :
{
auto castedInst = ( BeLoadInst * ) inst ;
auto mcTarget = GetOperand ( castedInst - > mTarget ) ;
result = CreateLoad ( mcTarget ) ;
}
break ;
case BeStoreInst : : TypeId :
{
auto castedInst = ( BeStoreInst * ) inst ;
auto mcVal = GetOperand ( castedInst - > mVal ) ;
auto mcPtr = GetOperand ( castedInst - > mPtr ) ;
bool handled = false ;
CreateStore ( BeMCInstKind_Mov , mcVal , mcPtr ) ;
}
break ;
case BeSetCanMergeInst : : TypeId :
{
auto castedInst = ( BeSetCanMergeInst * ) inst ;
auto mcVal = GetOperand ( castedInst - > mVal ) ;
auto vregInfo = GetVRegInfo ( mcVal ) ;
vregInfo - > mForceMerge = true ;
}
break ;
case BeMemSetInst : : TypeId :
{
auto castedInst = ( BeMemSetInst * ) inst ;
if ( auto constVal = BeValueDynCast < BeConstant > ( castedInst - > mVal ) )
{
if ( auto constSize = BeValueDynCast < BeConstant > ( castedInst - > mSize ) )
{
CreateMemSet ( GetOperand ( castedInst - > mAddr ) , constVal - > mUInt8 , constSize - > mInt64 , castedInst - > mAlignment ) ;
break ;
}
}
SizedArray < BeValue * , 3 > args = { castedInst - > mAddr , castedInst - > mVal , castedInst - > mSize } ;
auto mcFunc = BeMCOperand : : FromSymbolAddr ( mCOFFObject - > GetSymbolRef ( " memset " ) - > mIdx ) ;
CreateCall ( mcFunc , args ) ;
}
break ;
case BeFenceInst : : TypeId :
{
AllocInst ( BeMCInstKind_MFence ) ;
}
break ;
case BeStackSaveInst : : TypeId :
{
auto stackVReg = BeMCOperand : : FromVReg ( stackSaveVRegs . back ( ) ) ;
stackSaveVRegs . pop_back ( ) ;
CreateDefineVReg ( stackVReg ) ;
AllocInst ( BeMCInstKind_Mov , stackVReg , BeMCOperand : : FromReg ( X64Reg_RSP ) ) ;
result = stackVReg ;
}
break ;
case BeStackRestoreInst : : TypeId :
{
auto castedInst = ( BeStackRestoreInst * ) inst ;
auto mcStackVal = GetOperand ( castedInst - > mStackVal ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( X64Reg_RSP ) , mcStackVal ) ;
}
break ;
case BeGEPInst : : TypeId :
{
auto castedInst = ( BeGEPInst * ) inst ;
auto mcVal = GetOperand ( castedInst - > mPtr ) ;
auto mcIdx0 = GetOperand ( castedInst - > mIdx0 ) ;
BePointerType * ptrType = ( BePointerType * ) GetType ( mcVal ) ;
BF_ASSERT ( ptrType - > mTypeCode = = BeTypeCode_Pointer ) ;
result = mcVal ;
if ( castedInst - > mIdx1 ! = NULL )
{
// We assume we never do both an idx0 and idx1 at once. Fix if we change that.
BF_ASSERT ( castedInst - > mIdx0 ) ;
auto mcIdx1 = GetOperand ( castedInst - > mIdx1 ) ;
if ( ! mcIdx1 . IsImmediate ( ) )
{
// This path is used when we have a const array that gets indexed by a non-const index value
if ( ptrType - > mElementType - > mTypeCode = = BeTypeCode_SizedArray )
{
auto arrayType = ( BeSizedArrayType * ) ptrType - > mElementType ;
auto elementPtrType = mModule - > mContext - > GetPointerTo ( arrayType - > mElementType ) ;
auto ptrValue = AllocVirtualReg ( elementPtrType ) ;
auto ptrInfo = GetVRegInfo ( ptrValue ) ;
ptrInfo - > mIsExpr = true ;
ptrInfo - > mRelTo = result ;
CreateDefineVReg ( ptrValue ) ;
result = ptrValue ;
BeMCOperand mcRelOffset ;
int relScale = 1 ;
if ( mcIdx1 . IsImmediate ( ) )
{
mcRelOffset = BeMCOperand : : FromImmediate ( mcIdx1 . mImmediate * arrayType - > mElementType - > mSize ) ;
}
else
{
mcRelOffset = mcIdx1 ;
relScale = arrayType - > mElementType - > mSize ;
}
result = AllocRelativeVirtualReg ( elementPtrType , result , mcRelOffset , relScale ) ;
// The def is primary to create a single 'master location' for the GEP vreg to become legalized before use
CreateDefineVReg ( result ) ;
//TODO: Always correct?
result . mKind = BeMCOperandKind_VReg ;
}
else
Fail ( " Invalid GEP " ) ;
}
else
{
BF_ASSERT ( mcIdx1 . IsImmediate ( ) ) ;
int byteOffset = 0 ;
BeType * elementType = NULL ;
if ( ptrType - > mElementType - > mTypeCode = = BeTypeCode_Struct )
{
BeStructType * structType = ( BeStructType * ) ptrType - > mElementType ;
auto & structMember = structType - > mMembers [ mcIdx1 . mImmediate ] ;
elementType = structMember . mType ;
byteOffset = structMember . mByteOffset ;
}
else
{
BF_ASSERT ( ptrType - > mElementType - > mTypeCode = = BeTypeCode_SizedArray ) ;
auto arrayType = ( BeSizedArrayType * ) ptrType - > mElementType ;
elementType = arrayType - > mElementType ;
byteOffset = mcIdx1 . mImmediate * elementType - > mSize ;
}
auto elementPtrType = mModule - > mContext - > GetPointerTo ( elementType ) ;
result = AllocRelativeVirtualReg ( elementPtrType , result , GetImmediate ( byteOffset ) , 1 ) ;
// The def is primary to create a single 'master location' for the GEP vreg to become legalized before use
CreateDefineVReg ( result ) ;
result . mKind = BeMCOperandKind_VReg ;
}
}
else
{
// It's temping to do a (IsNonZero) precondition, but if we make a reference to a VReg that is NOT in Addr form,
// then this will encode that so we will know we need to do a Load on that value at the Def during legalization
BeMCOperand mcRelOffset ;
int relScale = 1 ;
if ( mcIdx0 . IsImmediate ( ) )
{
mcRelOffset = BeMCOperand : : FromImmediate ( mcIdx0 . mImmediate * ptrType - > mElementType - > mSize ) ;
}
else
{
mcRelOffset = mcIdx0 ;
relScale = ptrType - > mElementType - > mSize ;
}
result = AllocRelativeVirtualReg ( ptrType , result , mcRelOffset , relScale ) ;
// The def is primary to create a single 'master location' for the GEP vreg to become legalized before use
CreateDefineVReg ( result ) ;
//TODO: Always correct?
result . mKind = BeMCOperandKind_VReg ;
}
}
break ;
case BeBrInst : : TypeId :
{
auto castedInst = ( BeBrInst * ) inst ;
auto mcInst = AllocInst ( ) ;
mcInst - > mKind = BeMCInstKind_Br ;
mcInst - > mArg0 = GetOperand ( castedInst - > mTargetBlock ) ;
mcInst - > mArg0 . mBlock - > AddPred ( mcBlock ) ;
if ( castedInst - > mNoCollapse )
{
mcInst - > mArg1 . mKind = BeMCOperandKind_Immediate_i8 ;
mcInst - > mArg1 . mImmediate = 1 ;
}
else if ( castedInst - > mIsFake )
{
mcInst - > mArg1 . mKind = BeMCOperandKind_Immediate_i8 ;
mcInst - > mArg1 . mImmediate = 2 ;
}
}
break ;
case BeCondBrInst : : TypeId :
{
if ( mDebugging )
{
NOP ;
}
auto castedInst = ( BeCondBrInst * ) inst ;
auto testVal = GetOperand ( castedInst - > mCond , true ) ;
auto trueBlock = GetOperand ( castedInst - > mTrueBlock ) ;
auto falseBlock = GetOperand ( castedInst - > mFalseBlock ) ;
trueBlock . mBlock - > AddPred ( mcBlock ) ;
falseBlock . mBlock - > AddPred ( mcBlock ) ;
CreateCondBr ( mcBlock , testVal , trueBlock , falseBlock ) ;
}
break ;
case BePhiInst : : TypeId :
{
auto castedInst = ( BePhiInst * ) inst ;
BeMCPhi * mcPhi = mPhiAlloc . Alloc ( ) ;
mcPhi - > mBlock = mcBlock ;
mcPhi - > mIdx = mCurPhiIdx + + ;
//if (mDebugging)
{
for ( auto phiIncoming : castedInst - > mIncoming )
{
auto blockFrom = GetOperand ( phiIncoming - > mBlock ) . mBlock ; ;
int insertIdx = blockFrom - > mInstructions . size ( ) - 1 ;
SetAndRestoreValue < BeMCBlock * > prevActiveBlock ( mActiveBlock , blockFrom ) ;
SetAndRestoreValue < int * > prevInsertIdxPtr ( mInsertInstIdxRef , & insertIdx ) ;
BeMCPhiValue phiVal ;
phiVal . mBlockFrom = blockFrom ;
phiVal . mValue = GetOperand ( phiIncoming - > mValue , true ) ;
if ( phiVal . mValue . mKind = = BeMCOperandKind_VRegAddr )
{
auto vregInfo = GetVRegInfo ( phiVal . mValue ) ;
if ( ! vregInfo - > mIsExpr )
{
vregInfo - > mForceMem = true ;
CheckForce ( vregInfo ) ;
}
}
mcPhi - > mValues . push_back ( phiVal ) ;
}
}
/*else
{
for ( auto phiIncoming : castedInst - > mIncoming )
{
BeMCPhiValue phiVal ;
phiVal . mBlockFrom = GetOperand ( phiIncoming - > mBlock ) . mBlock ;
phiVal . mValue = GetOperand ( phiIncoming - > mValue , true ) ;
mcPhi - > mValues . push_back ( phiVal ) ;
}
} */
result . mKind = BeMCOperandKind_Phi ;
result . mPhi = mcPhi ;
// DefPhi is important because when we convert a CondBr of a PHI, because we will need to create jumps to the correct
// location when we create it as a value (specifically in the bool case)
AllocInst ( BeMCInstKind_DefPhi , result ) ;
}
break ;
case BeSwitchInst : : TypeId :
{
auto castedInst = ( BeSwitchInst * ) inst ;
std : : stable_sort ( castedInst - > mCases . begin ( ) , castedInst - > mCases . end ( ) , [ & ] ( const BeSwitchCase & lhs , const BeSwitchCase & rhs )
{
return lhs . mValue - > mInt64 < rhs . mValue - > mInt64 ;
} ) ;
int numVals = castedInst - > mCases . size ( ) ;
if ( numVals > 0 )
{
int64 loVal = castedInst - > mCases . front ( ) . mValue - > mInt64 ;
int64 hiVal = castedInst - > mCases . back ( ) . mValue - > mInt64 ;
uint64 avgSpacing = ( uint64 ) ( hiVal - loVal ) / numVals ;
// Only use a table if we have a lot of values and the values are 'tight' enough
if ( ( numVals > 6 ) & & ( avgSpacing < = 8 ) )
CreateTableSwitchSection ( castedInst , 0 , castedInst - > mCases . size ( ) ) ;
else
CreateBinarySwitchSection ( castedInst , 0 , castedInst - > mCases . size ( ) ) ;
}
auto mcDefaultBlock = GetOperand ( castedInst - > mDefaultBlock ) ;
AllocInst ( BeMCInstKind_Br , mcDefaultBlock ) ;
mcDefaultBlock . mBlock - > AddPred ( mActiveBlock ) ;
}
break ;
case BeRetInst : : TypeId :
{
auto castedInst = ( BeRetInst * ) inst ;
if ( castedInst - > mRetValue ! = NULL )
{
auto retVal = GetOperand ( castedInst - > mRetValue ) ;
auto retType = GetType ( retVal ) ;
if ( retType - > IsComposite ( ) )
{
BF_ASSERT ( mCompositeRetVRegIdx ! = - 1 ) ;
BF_ASSERT ( retVal . IsVReg ( ) ) ;
auto vregInfo = GetVRegInfo ( retVal ) ;
vregInfo - > SetRetVal ( ) ;
}
else
{
X64CPURegister reg = X64Reg_RAX ;
if ( retType - > IsIntable ( ) )
{
if ( retType - > mSize = = 4 )
reg = X64Reg_EAX ;
else if ( retType - > mSize = = 2 )
reg = X64Reg_AX ;
else if ( retType - > mSize = = 1 )
reg = X64Reg_AL ;
}
else if ( retType - > mTypeCode = = BeTypeCode_Float )
reg = X64Reg_XMM0_f32 ;
else if ( retType - > mTypeCode = = BeTypeCode_Double )
reg = X64Reg_XMM0_f64 ;
auto movInst = AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( reg ) , retVal ) ;
}
}
auto mcInst = AllocInst ( ) ;
mcInst - > mKind = BeMCInstKind_Ret ;
retCount + + ;
}
break ;
case BeCallInst : : TypeId :
{
auto castedInst = ( BeCallInst * ) inst ;
BeMCOperand mcFunc ;
BeType * returnType = NULL ;
bool isVarArg = false ;
bool useAltArgs = false ;
SizedArray < BeValue * , 6 > args ;
if ( auto intrin = BeValueDynCast < BeIntrinsic > ( castedInst - > mFunc ) )
{
switch ( intrin - > mKind )
{
case BfIRIntrinsic_AtomicAdd :
case BfIRIntrinsic_AtomicSub :
{
auto mcPtr = GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) ;
auto mcVal = GetOperand ( castedInst - > mArgs [ 1 ] . mValue ) ;
auto valType = GetType ( mcVal ) ;
if ( ! valType - > IsFloat ( ) )
{
auto mcMemKind = GetOperand ( castedInst - > mArgs [ 2 ] . mValue ) ;
if ( ! mcMemKind . IsImmediateInt ( ) )
{
SoftFail ( " Non-constant success ordering on AtomicLoad " , castedInst - > mDbgLoc ) ;
break ;
}
BeMCOperand scratchReg = AllocVirtualReg ( valType ) ;
auto vregInfo = GetVRegInfo ( scratchReg ) ;
vregInfo - > mMustExist = true ;
CreateDefineVReg ( scratchReg ) ;
if ( ( intrin - > mKind = = BfIRIntrinsic_AtomicSub ) & & ( mcVal . IsImmediate ( ) ) )
{
BeMCOperand mcNeg = mcVal ;
if ( mcVal . IsImmediateFloat ( ) )
mcNeg . mImmFloat = - mcNeg . mImmFloat ;
else
mcNeg . mImmediate = - mcNeg . mImmediate ;
AllocInst ( BeMCInstKind_Mov , scratchReg , mcNeg ) ;
}
else
{
AllocInst ( BeMCInstKind_Mov , scratchReg , mcVal ) ;
if ( intrin - > mKind = = BfIRIntrinsic_AtomicSub )
AllocInst ( BeMCInstKind_Neg , scratchReg ) ;
}
CreateStore ( BeMCInstKind_XAdd , scratchReg , mcPtr ) ;
if ( ( mcMemKind . mImmediate & BfIRAtomicOrdering_ReturnModified ) ! = 0 )
{
if ( intrin - > mKind = = BfIRIntrinsic_AtomicSub )
AllocInst ( BeMCInstKind_Sub , scratchReg , mcVal ) ;
else
AllocInst ( BeMCInstKind_Add , scratchReg , mcVal ) ;
}
result = scratchReg ;
break ;
}
}
case BfIRIntrinsic_AtomicAnd :
case BfIRIntrinsic_AtomicMax :
case BfIRIntrinsic_AtomicMin :
case BfIRIntrinsic_AtomicNAnd :
case BfIRIntrinsic_AtomicOr :
case BfIRIntrinsic_AtomicUMax :
case BfIRIntrinsic_AtomicUMin :
case BfIRIntrinsic_AtomicXor :
{
auto mcPtr = GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) ;
auto mcVal = GetOperand ( castedInst - > mArgs [ 1 ] . mValue ) ;
auto mcMemKind = GetOperand ( castedInst - > mArgs [ 2 ] . mValue ) ;
if ( ! mcMemKind . IsImmediateInt ( ) )
{
SoftFail ( " Non-constant ordering on atomic instruction " , castedInst - > mDbgLoc ) ;
break ;
}
auto valType = GetType ( mcVal ) ;
auto origValType = valType ;
bool isFloat = valType - > IsFloat ( ) ;
auto mcTarget = BeMCOperand : : ToLoad ( mcPtr ) ;
auto useReg = ResizeRegister ( X64Reg_RAX , valType - > mSize ) ;
auto origTarget = mcTarget ;
if ( isFloat )
{
if ( valType - > mSize = = 4 )
valType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int32 ) ;
else
valType = mModule - > mContext - > GetPrimitiveType ( BeTypeCode_Int64 ) ;
BeMCOperand castedTarget = AllocVirtualReg ( valType ) ;
auto vregInfo = GetVRegInfo ( castedTarget ) ;
vregInfo - > mMustExist = true ;
vregInfo - > mType = valType ;
2019-11-26 13:11:17 -08:00
vregInfo - > mAlign = valType - > mAlign ;
2019-08-23 11:56:54 -07:00
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = mcTarget ;
CreateDefineVReg ( castedTarget ) ;
mcTarget = castedTarget ;
}
int labelIdx = CreateLabel ( ) ;
BeMCOperand mcPrev = AllocVirtualReg ( valType ) ;
auto vregInfo = GetVRegInfo ( mcPrev ) ;
vregInfo - > mMustExist = true ;
CreateDefineVReg ( mcPrev ) ;
BeMCOperand mcNext = AllocVirtualReg ( valType ) ;
vregInfo = GetVRegInfo ( mcNext ) ;
vregInfo - > mMustExist = true ;
vregInfo - > mForceMem = isFloat ;
CreateDefineVReg ( mcNext ) ;
AllocInst ( BeMCInstKind_Mov , mcPrev , mcTarget ) ;
AllocInst ( BeMCInstKind_Mov , mcNext , mcPrev ) ;
if ( isFloat )
{
BeMCOperand mcFNext = AllocVirtualReg ( origValType ) ;
vregInfo = GetVRegInfo ( mcFNext ) ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = mcNext ;
CreateDefineVReg ( mcFNext ) ;
if ( intrin - > mKind = = BfIRIntrinsic_AtomicAdd )
AllocInst ( BeMCInstKind_Add , mcFNext , mcVal ) ;
else
AllocInst ( BeMCInstKind_Sub , mcFNext , mcVal ) ;
}
else
{
switch ( intrin - > mKind )
{
case BfIRIntrinsic_AtomicAdd :
AllocInst ( BeMCInstKind_Add , mcNext , mcVal ) ;
break ;
case BfIRIntrinsic_AtomicAnd :
AllocInst ( BeMCInstKind_Or , mcNext , mcVal ) ;
break ;
case BfIRIntrinsic_AtomicMax :
case BfIRIntrinsic_AtomicMin :
case BfIRIntrinsic_AtomicUMax :
case BfIRIntrinsic_AtomicUMin :
{
int cmpLabelIdx = mCurLabelIdx + + ;
AllocInst ( BeMCInstKind_Cmp , mcNext , mcVal ) ;
BeCmpKind cmpKind = BeCmpKind_None ;
switch ( intrin - > mKind )
{
case BfIRIntrinsic_AtomicMax :
cmpKind = BeCmpKind_SGE ;
break ;
case BfIRIntrinsic_AtomicMin :
cmpKind = BeCmpKind_SLE ;
break ;
case BfIRIntrinsic_AtomicUMax :
cmpKind = BeCmpKind_UGE ;
break ;
case BfIRIntrinsic_AtomicUMin :
cmpKind = BeCmpKind_ULE ;
break ;
}
AllocInst ( BeMCInstKind_CondBr , BeMCOperand : : FromLabel ( cmpLabelIdx ) , BeMCOperand : : FromCmpKind ( cmpKind ) ) ;
AllocInst ( BeMCInstKind_Mov , mcNext , mcVal ) ;
CreateLabel ( - 1 , cmpLabelIdx ) ;
}
break ;
case BfIRIntrinsic_AtomicNAnd :
AllocInst ( BeMCInstKind_And , mcNext , mcVal ) ;
AllocInst ( BeMCInstKind_Not , mcNext ) ;
break ;
case BfIRIntrinsic_AtomicOr :
AllocInst ( BeMCInstKind_Or , mcNext , mcVal ) ;
break ;
case BfIRIntrinsic_AtomicSub :
AllocInst ( BeMCInstKind_Sub , mcNext , mcVal ) ;
break ;
case BfIRIntrinsic_AtomicXor :
AllocInst ( BeMCInstKind_Xor , mcNext , mcVal ) ;
break ;
}
}
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( useReg ) , mcPrev ) ;
auto cmpXChgInst = AllocInst ( BeMCInstKind_CmpXChg , mcTarget , mcNext ) ;
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
AllocInst ( BeMCInstKind_CondBr , BeMCOperand : : FromLabel ( labelIdx ) , BeMCOperand : : FromCmpKind ( BeCmpKind_NE ) ) ;
if ( ( mcMemKind . mImmediate & BfIRAtomicOrdering_ReturnModified ) ! = 0 )
result = mcNext ;
else
result = mcPrev ;
}
break ;
case BfIRIntrinsic_AtomicCmpStore :
case BfIRIntrinsic_AtomicCmpStore_Weak :
case BfIRIntrinsic_AtomicCmpXChg :
{
auto mcPtr = GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) ;
auto mcComparand = GetOperand ( castedInst - > mArgs [ 1 ] . mValue ) ;
auto mcVal = GetOperand ( castedInst - > mArgs [ 2 ] . mValue ) ;
auto mcMemKind = GetOperand ( castedInst - > mArgs [ 3 ] . mValue ) ;
if ( ! mcMemKind . IsImmediate ( ) )
{
SoftFail ( " Non-constant success ordering on atomic operation " , castedInst - > mDbgLoc ) ;
break ;
}
auto valType = GetType ( mcVal ) ;
auto useReg = ResizeRegister ( X64Reg_RAX , valType - > mSize ) ;
BeMCOperand scratchReg = AllocVirtualReg ( GetType ( mcVal ) ) ;
auto vregInfo = GetVRegInfo ( scratchReg ) ;
vregInfo - > mMustExist = true ;
CreateDefineVReg ( scratchReg ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , mcVal ) ;
AllocInst ( BeMCInstKind_PreserveVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
AllocInst ( BeMCInstKind_Mov , BeMCOperand : : FromReg ( useReg ) , mcComparand ) ;
auto cmpXChgInst = AllocInst ( BeMCInstKind_CmpXChg , BeMCOperand : : ToLoad ( mcPtr ) , scratchReg ) ;
if ( intrin - > mKind = = BfIRIntrinsic_AtomicCmpXChg )
{
AllocInst ( BeMCInstKind_Mov , scratchReg , BeMCOperand : : FromReg ( useReg ) ) ;
result = scratchReg ;
}
else
{
auto cmpResultIdx = ( int ) mCmpResults . size ( ) ;
BeCmpResult cmpResult ;
cmpResult . mCmpKind = BeCmpKind_EQ ;
mCmpResults . push_back ( cmpResult ) ;
result . mKind = BeMCOperandKind_CmpResult ;
result . mCmpResultIdx = cmpResultIdx ;
cmpXChgInst - > mResult = result ;
}
AllocInst ( BeMCInstKind_RestoreVolatiles , BeMCOperand : : FromReg ( X64Reg_RAX ) ) ;
}
break ;
case BfIRIntrinsic_AtomicLoad :
{
auto mcTarget = GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) ;
result = CreateLoad ( mcTarget ) ;
}
break ;
case BfIRIntrinsic_AtomicFence :
{
if ( castedInst - > mArgs . size ( ) = = 0 )
{
// Compiler fence- do nothing.
break ;
}
auto mcMemKind = GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) ;
if ( ! mcMemKind . IsImmediateInt ( ) )
{
SoftFail ( " Non-constant success ordering on AtomicFence " , castedInst - > mDbgLoc ) ;
break ;
}
if ( mcMemKind . mImmediate = = BfIRAtomicOrdering_SeqCst )
AllocInst ( BeMCInstKind_MFence ) ;
}
break ;
case BfIRIntrinsic_AtomicStore :
{
auto mcPtr = GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) ;
auto mcVal = GetOperand ( castedInst - > mArgs [ 1 ] . mValue ) ;
auto mcMemKind = GetOperand ( castedInst - > mArgs [ 2 ] . mValue ) ;
if ( ! mcMemKind . IsImmediateInt ( ) )
{
SoftFail ( " Non-constant success ordering on AtomicLoad " , castedInst - > mDbgLoc ) ;
break ;
}
if ( mcMemKind . mImmediate = = BfIRAtomicOrdering_SeqCst )
{
BeMCOperand scratchReg = AllocVirtualReg ( GetType ( mcVal ) ) ;
auto vregInfo = GetVRegInfo ( scratchReg ) ;
vregInfo - > mMustExist = true ;
CreateDefineVReg ( scratchReg ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , mcVal ) ;
CreateStore ( BeMCInstKind_XChg , scratchReg , mcPtr ) ;
}
else
{
CreateStore ( BeMCInstKind_Mov , mcVal , mcPtr ) ;
}
}
break ;
case BfIRIntrinsic_AtomicXChg :
{
auto mcPtr = GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) ;
auto mcVal = GetOperand ( castedInst - > mArgs [ 1 ] . mValue ) ;
auto mcMemKind = GetOperand ( castedInst - > mArgs [ 2 ] . mValue ) ;
if ( ! mcMemKind . IsImmediateInt ( ) )
{
SoftFail ( " Non-constant success ordering on AtomicXChg " , castedInst - > mDbgLoc ) ;
break ;
}
BeMCOperand scratchReg = AllocVirtualReg ( GetType ( mcVal ) ) ;
auto vregInfo = GetVRegInfo ( scratchReg ) ;
vregInfo - > mMustExist = true ;
CreateDefineVReg ( scratchReg ) ;
AllocInst ( BeMCInstKind_Mov , scratchReg , mcVal ) ;
CreateStore ( BeMCInstKind_XChg , scratchReg , mcPtr ) ;
result = scratchReg ;
}
break ;
case BfIRIntrinsic_MemSet :
{
if ( auto constVal = BeValueDynCast < BeConstant > ( castedInst - > mArgs [ 1 ] . mValue ) )
{
if ( auto constSize = BeValueDynCast < BeConstant > ( castedInst - > mArgs [ 2 ] . mValue ) )
{
if ( auto constAlign = BeValueDynCast < BeConstant > ( castedInst - > mArgs [ 3 ] . mValue ) )
{
CreateMemSet ( GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) , constVal - > mUInt8 , constSize - > mInt64 , constAlign - > mInt32 ) ;
break ;
}
}
}
mcFunc = BeMCOperand : : FromSymbolAddr ( mCOFFObject - > GetSymbolRef ( " memset " ) - > mIdx ) ;
for ( int i = 0 ; i < 3 ; i + + )
args . Add ( castedInst - > mArgs [ i ] . mValue ) ;
useAltArgs = true ;
}
break ;
case BfIRIntrinsic_MemCpy :
{
if ( auto constSize = BeValueDynCast < BeConstant > ( castedInst - > mArgs [ 2 ] . mValue ) )
{
if ( auto constAlign = BeValueDynCast < BeConstant > ( castedInst - > mArgs [ 3 ] . mValue ) )
{
CreateMemCpy ( GetOperand ( castedInst - > mArgs [ 0 ] . mValue ) , GetOperand ( castedInst - > mArgs [ 1 ] . mValue ) , constSize - > mInt64 , constAlign - > mInt32 ) ;
break ;
}
}
mcFunc = BeMCOperand : : FromSymbolAddr ( mCOFFObject - > GetSymbolRef ( " memcpy " ) - > mIdx ) ;
//altArgs.insert(altArgs.begin(), castedInst->mArgs.begin(), castedInst->mArgs.begin() + 3);
for ( int i = 0 ; i < 3 ; i + + )
args . Add ( castedInst - > mArgs [ i ] . mValue ) ;
useAltArgs = true ;
}
break ;
case BfIRIntrinsic_MemMove :
{
mcFunc = BeMCOperand : : FromSymbolAddr ( mCOFFObject - > GetSymbolRef ( " memmove " ) - > mIdx ) ;
//altArgs.insert(altArgs.begin(), castedInst->mArgs.begin(), castedInst->mArgs.begin() + 3);
for ( int i = 0 ; i < 3 ; i + + )
args . Add ( castedInst - > mArgs [ i ] . mValue ) ;
useAltArgs = true ;
}
break ;
default :
SoftFail ( " Intrinsic not handled " , castedInst - > mDbgLoc ) ;
break ;
}
}
else
{
if ( auto func = BeValueDynCast < BeFunction > ( castedInst - > mFunc ) )
{
auto funcPtrType = func - > mType ;
if ( funcPtrType - > IsPointer ( ) )
{
auto elementType = ( ( BePointerType * ) funcPtrType ) - > mElementType ;
if ( elementType - > mTypeCode = = BeTypeCode_Function )
{
isVarArg = ( ( BeFunctionType * ) elementType ) - > mIsVarArg ;
}
}
}
returnType = castedInst - > GetType ( ) ;
mcFunc = GetOperand ( castedInst - > mFunc ) ;
}
if ( mcFunc )
{
if ( ! useAltArgs )
{
BF_ASSERT ( args . IsEmpty ( ) ) ;
for ( auto & arg : castedInst - > mArgs )
args . Add ( arg . mValue ) ;
}
result = CreateCall ( mcFunc , args , returnType , castedInst - > mCallingConv , castedInst - > HasStructRet ( ) , castedInst - > mNoReturn , isVarArg ) ;
}
}
break ;
case BeDbgDeclareInst : : TypeId :
{
if ( mDebugging )
{
NOP ;
}
auto castedInst = ( BeDbgDeclareInst * ) inst ;
auto dbgVar = castedInst - > mDbgVar ;
auto mcValue = GetOperand ( castedInst - > mValue ) ;
auto mcVReg = mcValue ;
auto vregInfo = GetVRegInfo ( mcVReg ) ;
if ( ( vregInfo ! = NULL ) & & ( vregInfo - > mDbgVariable ! = NULL ) )
{
auto shadowVReg = AllocVirtualReg ( vregInfo - > mType ) ;
CreateDefineVReg ( shadowVReg ) ;
auto shadowVRegInfo = GetVRegInfo ( shadowVReg ) ;
shadowVRegInfo - > mIsExpr = true ;
shadowVRegInfo - > mValueScopeRetainedKind = BeMCValueScopeRetainKind_Hard ;
shadowVRegInfo - > mRelTo = mcVReg ;
shadowVRegInfo - > mRelTo . mKind = BeMCOperandKind_VReg ;
shadowVReg . mKind = mcVReg . mKind ;
mcValue = shadowVReg ;
mcVReg = shadowVReg ;
vregInfo = shadowVRegInfo ;
mValueToOperand [ dbgVar ] = shadowVReg ;
}
else
{
mValueToOperand [ dbgVar ] = mcVReg ;
}
dbgVar - > mValue = castedInst - > mValue ;
dbgVar - > mDeclDbgLoc = inst - > mDbgLoc ;
dbgVar - > mIsValue = castedInst - > mIsValue ;
if ( ! mcVReg . IsVRegAny ( ) )
{
mcVReg = AllocVirtualReg ( mModule - > mContext - > GetPrimitiveType ( BeTypeCode_None ) ) ;
vregInfo = GetVRegInfo ( mcVReg ) ;
// Allocate a vreg just to properly track the lifetime of this local constant
/*auto constType = GetType(mcVReg);
auto newVReg = AllocVirtualReg ( constType ) ;
vregInfo = GetVRegInfo ( newVReg ) ;
vregInfo - > mIsExpr = true ;
vregInfo - > mRelTo = mcVReg ;
mcVReg = newVReg ; */
}
vregInfo - > mDbgVariable = dbgVar ;
vregInfo - > mMustExist = true ;
dbgVar - > mScope = mCurDbgLoc - > mDbgScope ;
dbgVar - > mDeclMCBlockId = mcBlock - > mBlockIdx ;
AllocInst ( BeMCInstKind_DbgDecl , mcVReg ) ;
}
break ;
default :
NotImpl ( ) ;
break ;
}
if ( mFailed )
return ;
if ( result . mKind ! = BeMCOperandKind_None )
mValueToOperand [ inst ] = result ;
}
inHeadAlloca = false ;
isFirstBlock = false ;
}
mCurDbgLoc = NULL ;
BF_ASSERT ( valueScopeStack . size ( ) = = 0 ) ;
BF_ASSERT ( retCount = = 1 ) ;
bool wantDebug = mDebugging ;
//wantDebug |= function->mName == "?get__Yo@PoopBase@@UEAAUPloogB@@XZ";
//wantDebug |= function->mName == "?Testos@Fartso@@SAHPEA1@HH@Z";
//wantDebug |= function->mName == "?GetYoopA@Fartso@@QEAAUYoop@@XZ";
//"?TestVals@Fartso@@QEAATint@@XZ";
/*if (function->mName == "?TestVals@Fartso@@QEAATint@@PEA_J@Z")
{
//TODO: Temporary, force reg1 to spill
mVRegInfo [ 1 ] - > mSpilled = true ;
} */
String str ;
if ( wantDebug )
{
str = " -------- Initial -------- \n " + ToString ( ) ;
OutputDebugStr ( str . c_str ( ) ) ;
} // Hi
DoTLSSetup ( ) ;
DoChainedBlockMerge ( ) ;
DetectLoops ( ) ;
for ( int pass = 0 ; pass < 3 ; pass + + )
{
// Shouldn't have more than 2 passes
BF_ASSERT ( pass ! = 2 ) ;
if ( DoInitializedPass ( ) )
break ;
}
if ( wantDebug )
{
str = " -------- After DoDefPass -------- \n " + ToString ( ) ;
OutputDebugStr ( str . c_str ( ) ) ;
}
RefreshRefCounts ( ) ;
GenerateLiveness ( ) ;
if ( wantDebug )
{
str = " --------After GenerateLiveness -------- \n " + ToString ( ) ;
OutputDebugStr ( str . c_str ( ) ) ;
}
DoInstCombinePass ( ) ;
RefreshRefCounts ( ) ;
if ( wantDebug )
{
str = " --------After DoInstCombinePass-------- \n " + ToString ( ) ;
OutputDebugStr ( str . c_str ( ) ) ;
}
DoLoads ( ) ;
for ( int pass = 0 ; true ; pass + + )
{
DoRegAssignPass ( ) ;
DoSanityChecking ( ) ;
if ( wantDebug )
{
str = " --------After RegAssignPass-------- \n " + ToString ( ) ;
OutputDebugStr ( str . c_str ( ) ) ;
}
bool regsDone = DoLegalization ( ) ;
if ( wantDebug )
{
str = " --------After Legalization-------- \n " + ToString ( ) ;
OutputDebugStr ( str . c_str ( ) ) ;
}
if ( regsDone )
{
if ( pass = = 0 )
{
if ( wantDebug )
{
// The first pass may overestimate mem cost, so run another one
OutputDebugStr ( " Processing done on first pass, doing another register assign pass \n " ) ;
}
}
else
break ;
}
//FOR TESTING
if ( pass = = 4 )
{
NOP ;
}
if ( pass = = 16 )
{
Fail ( " Register assignment failed! " ) ;
}
// Reassign regs after legalization
//DoRegAssignPass();
}
DoRegFinalization ( ) ;
if ( wantDebug )
{
str = " --------After RegFinalization-------- \n " + ToString ( ) ;
OutputDebugStr ( str ) ;
}
DoBlockCombine ( ) ;
if ( wantDebug )
{
str = " --------After BlockCombine -------- \n " + ToString ( ) ;
OutputDebugStr ( str ) ;
}
while ( true )
{
bool didWork = DoJumpRemovePass ( ) ;
if ( ! didWork )
break ;
RefreshRefCounts ( ) ;
if ( wantDebug )
{
str = " --------After DoJumpRemovePass -------- \n " + ToString ( ) ;
OutputDebugStr ( str ) ;
}
}
DoFrameObjPass ( ) ; // Must be (almost) last
if ( wantDebug )
{
str = ToString ( ) ;
OutputDebugStr ( str . c_str ( ) ) ;
}
DoCodeEmission ( ) ;
}