2019-09-17 13:22:17 +02:00
/*
* Copyright © 2018 Valve Corporation
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE .
*
*/
# include "aco_ir.h"
2019-09-21 17:38:52 +02:00
# include <array>
2019-09-17 13:22:17 +02:00
# include <map>
2020-08-04 10:58:11 -07:00
# include "util/memstream.h"
2019-09-17 13:22:17 +02:00
namespace aco {
2020-08-14 13:59:16 +02:00
static void aco_log ( Program * program , enum radv_compiler_debug_level level ,
const char * prefix , const char * file , unsigned line ,
2020-08-14 10:42:27 +02:00
const char * fmt , va_list args )
2019-09-17 13:22:17 +02:00
{
2020-08-14 10:42:27 +02:00
char * msg ;
2019-09-17 13:22:17 +02:00
2020-08-14 10:42:27 +02:00
msg = ralloc_strdup ( NULL , prefix ) ;
ralloc_asprintf_append ( & msg , " In file %s:%u \n " , file , line ) ;
ralloc_asprintf_append ( & msg , " " ) ;
ralloc_vasprintf_append ( & msg , fmt , args ) ;
2020-08-14 13:59:16 +02:00
if ( program - > debug . func )
program - > debug . func ( program - > debug . private_data , level , msg ) ;
2020-08-14 10:42:27 +02:00
fprintf ( stderr , " %s \n " , msg ) ;
ralloc_free ( msg ) ;
}
void _aco_perfwarn ( Program * program , const char * file , unsigned line ,
const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
2020-08-14 13:59:16 +02:00
aco_log ( program , RADV_COMPILER_DEBUG_LEVEL_PERFWARN ,
" ACO PERFWARN: \n " , file , line , fmt , args ) ;
2020-08-14 10:42:27 +02:00
va_end ( args ) ;
2019-09-17 13:22:17 +02:00
}
2020-08-14 10:42:27 +02:00
void _aco_err ( Program * program , const char * file , unsigned line ,
const char * fmt , . . . )
{
va_list args ;
va_start ( args , fmt ) ;
2020-08-14 13:59:16 +02:00
aco_log ( program , RADV_COMPILER_DEBUG_LEVEL_ERROR ,
" ACO ERROR: \n " , file , line , fmt , args ) ;
2020-08-14 10:42:27 +02:00
va_end ( args ) ;
}
bool validate_ir ( Program * program )
2019-09-17 13:22:17 +02:00
{
bool is_valid = true ;
2020-11-03 14:40:05 +01:00
auto check = [ & program , & is_valid ] ( bool success , const char * msg , aco : : Instruction * instr ) - > void {
if ( ! success ) {
2020-08-14 10:42:27 +02:00
char * out ;
size_t outsize ;
2020-08-04 10:58:11 -07:00
struct u_memstream mem ;
u_memstream_open ( & mem , & out , & outsize ) ;
FILE * const memf = u_memstream_get ( & mem ) ;
2020-08-14 10:42:27 +02:00
fprintf ( memf , " %s: " , msg ) ;
aco_print_instr ( instr , memf ) ;
2020-08-04 10:58:11 -07:00
u_memstream_close ( & mem ) ;
2020-08-14 10:42:27 +02:00
2020-11-04 12:44:10 +01:00
aco_err ( program , " %s " , out ) ;
2020-08-14 10:42:27 +02:00
free ( out ) ;
2019-09-17 13:22:17 +02:00
is_valid = false ;
}
} ;
2020-08-14 10:42:27 +02:00
2020-11-03 14:40:05 +01:00
auto check_block = [ & program , & is_valid ] ( bool success , const char * msg , aco : : Block * block ) - > void {
if ( ! success ) {
2020-08-14 10:42:27 +02:00
aco_err ( program , " %s: BB%u " , msg , block - > index ) ;
2019-11-19 14:19:49 +00:00
is_valid = false ;
}
} ;
2019-09-17 13:22:17 +02:00
for ( Block & block : program - > blocks ) {
for ( aco_ptr < Instruction > & instr : block . instructions ) {
/* check base format */
Format base_format = instr - > format ;
base_format = ( Format ) ( ( uint32_t ) base_format & ~ ( uint32_t ) Format : : SDWA ) ;
base_format = ( Format ) ( ( uint32_t ) base_format & ~ ( uint32_t ) Format : : DPP ) ;
if ( ( uint32_t ) base_format & ( uint32_t ) Format : : VOP1 )
base_format = Format : : VOP1 ;
else if ( ( uint32_t ) base_format & ( uint32_t ) Format : : VOP2 )
base_format = Format : : VOP2 ;
else if ( ( uint32_t ) base_format & ( uint32_t ) Format : : VOPC )
base_format = Format : : VOPC ;
2020-05-08 16:18:55 +02:00
else if ( ( uint32_t ) base_format & ( uint32_t ) Format : : VINTRP ) {
if ( instr - > opcode = = aco_opcode : : v_interp_p1ll_f16 | |
instr - > opcode = = aco_opcode : : v_interp_p1lv_f16 | |
instr - > opcode = = aco_opcode : : v_interp_p2_legacy_f16 | |
instr - > opcode = = aco_opcode : : v_interp_p2_f16 ) {
/* v_interp_*_fp16 are considered VINTRP by the compiler but
* they are emitted as VOP3 .
*/
base_format = Format : : VOP3 ;
} else {
base_format = Format : : VINTRP ;
}
}
2019-09-17 13:22:17 +02:00
check ( base_format = = instr_info . format [ ( int ) instr - > opcode ] , " Wrong base format for instruction " , instr . get ( ) ) ;
/* check VOP3 modifiers */
2021-01-20 15:27:16 +00:00
if ( instr - > isVOP3 ( ) & & instr - > format ! = Format : : VOP3 ) {
2019-09-17 13:22:17 +02:00
check ( base_format = = Format : : VOP2 | |
base_format = = Format : : VOP1 | |
base_format = = Format : : VOPC | |
base_format = = Format : : VINTRP ,
2021-01-20 13:50:45 +00:00
" Format cannot have VOP3/VOP3B applied " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
}
2019-12-04 20:18:05 +00:00
/* check SDWA */
if ( instr - > isSDWA ( ) ) {
check ( base_format = = Format : : VOP2 | |
base_format = = Format : : VOP1 | |
base_format = = Format : : VOPC ,
" Format cannot have SDWA applied " , instr . get ( ) ) ;
check ( program - > chip_class > = GFX8 , " SDWA is GFX8+ only " , instr . get ( ) ) ;
2021-01-21 16:13:34 +00:00
SDWA_instruction & sdwa = instr - > sdwa ( ) ;
check ( sdwa . omod = = 0 | | program - > chip_class > = GFX9 , " SDWA omod only supported on GFX9+ " , instr . get ( ) ) ;
2019-12-04 20:18:05 +00:00
if ( base_format = = Format : : VOPC ) {
2021-01-21 16:13:34 +00:00
check ( sdwa . clamp = = false | | program - > chip_class = = GFX8 , " SDWA VOPC clamp only supported on GFX8 " , instr . get ( ) ) ;
2019-12-04 20:18:05 +00:00
check ( ( instr - > definitions [ 0 ] . isFixed ( ) & & instr - > definitions [ 0 ] . physReg ( ) = = vcc ) | |
program - > chip_class > = GFX9 ,
" SDWA+VOPC definition must be fixed to vcc on GFX8 " , instr . get ( ) ) ;
}
if ( instr - > operands . size ( ) > = 3 ) {
check ( instr - > operands [ 2 ] . isFixed ( ) & & instr - > operands [ 2 ] . physReg ( ) = = vcc ,
" 3rd operand must be fixed to vcc with SDWA " , instr . get ( ) ) ;
}
if ( instr - > definitions . size ( ) > = 2 ) {
check ( instr - > definitions [ 1 ] . isFixed ( ) & & instr - > definitions [ 1 ] . physReg ( ) = = vcc ,
" 2nd definition must be fixed to vcc with SDWA " , instr . get ( ) ) ;
}
check ( instr - > opcode ! = aco_opcode : : v_madmk_f32 & &
instr - > opcode ! = aco_opcode : : v_madak_f32 & &
instr - > opcode ! = aco_opcode : : v_madmk_f16 & &
instr - > opcode ! = aco_opcode : : v_madak_f16 & &
instr - > opcode ! = aco_opcode : : v_readfirstlane_b32 & &
instr - > opcode ! = aco_opcode : : v_clrexcp & &
instr - > opcode ! = aco_opcode : : v_swap_b32 ,
" SDWA can't be used with this opcode " , instr . get ( ) ) ;
if ( program - > chip_class ! = GFX8 ) {
check ( instr - > opcode ! = aco_opcode : : v_mac_f32 & &
instr - > opcode ! = aco_opcode : : v_mac_f16 & &
instr - > opcode ! = aco_opcode : : v_fmac_f32 & &
instr - > opcode ! = aco_opcode : : v_fmac_f16 ,
" SDWA can't be used with this opcode " , instr . get ( ) ) ;
}
2020-06-03 11:27:55 +01:00
if ( instr - > definitions [ 0 ] . regClass ( ) . is_subdword ( ) )
2021-01-21 16:13:34 +00:00
check ( ( sdwa . dst_sel & sdwa_asuint ) = = ( sdwa_isra | instr - > definitions [ 0 ] . bytes ( ) ) , " Unexpected SDWA sel for sub-dword definition " , instr . get ( ) ) ;
2019-12-04 20:18:05 +00:00
}
2020-01-30 11:41:34 +00:00
/* check opsel */
if ( instr - > isVOP3 ( ) ) {
2021-01-21 16:13:34 +00:00
VOP3_instruction & vop3 = instr - > vop3 ( ) ;
check ( vop3 . opsel = = 0 | | program - > chip_class > = GFX9 , " Opsel is only supported on GFX9+ " , instr . get ( ) ) ;
2020-06-03 11:27:55 +01:00
2020-06-17 14:50:10 +01:00
for ( unsigned i = 0 ; i < 3 ; i + + ) {
if ( i > = instr - > operands . size ( ) | |
( instr - > operands [ i ] . hasRegClass ( ) & & instr - > operands [ i ] . regClass ( ) . is_subdword ( ) & & ! instr - > operands [ i ] . isFixed ( ) ) )
2021-01-21 16:13:34 +00:00
check ( ( vop3 . opsel & ( 1 < < i ) ) = = 0 , " Unexpected opsel for operand " , instr . get ( ) ) ;
2020-06-03 11:27:55 +01:00
}
2020-06-16 19:13:34 +01:00
if ( instr - > definitions [ 0 ] . regClass ( ) . is_subdword ( ) & & ! instr - > definitions [ 0 ] . isFixed ( ) )
2021-01-21 16:13:34 +00:00
check ( ( vop3 . opsel & ( 1 < < 3 ) ) = = 0 , " Unexpected opsel for sub-dword definition " , instr . get ( ) ) ;
2020-01-30 11:41:34 +00:00
}
2019-09-17 13:22:17 +02:00
/* check for undefs */
for ( unsigned i = 0 ; i < instr - > operands . size ( ) ; i + + ) {
if ( instr - > operands [ i ] . isUndefined ( ) ) {
2021-01-20 15:27:16 +00:00
bool flat = instr - > isFlatLike ( ) ;
bool can_be_undef = is_phi ( instr ) | | instr - > isEXP ( ) | |
instr - > isReduction ( ) | |
2020-02-27 13:08:45 +01:00
instr - > opcode = = aco_opcode : : p_create_vector | |
2021-01-20 15:27:16 +00:00
( flat & & i = = 1 ) | | ( instr - > isMIMG ( ) & & ( i = = 1 | | i = = 2 ) ) | |
( ( instr - > isMUBUF ( ) | | instr - > isMTBUF ( ) ) & & i = = 1 ) ;
2019-09-17 13:22:17 +02:00
check ( can_be_undef , " Undefs can only be used in certain operands " , instr . get ( ) ) ;
2020-03-25 11:03:33 +01:00
} else {
check ( instr - > operands [ i ] . isFixed ( ) | | instr - > operands [ i ] . isTemp ( ) | | instr - > operands [ i ] . isConstant ( ) , " Uninitialized Operand " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
}
}
2020-03-24 18:24:23 +01:00
/* check subdword definitions */
for ( unsigned i = 0 ; i < instr - > definitions . size ( ) ; i + + ) {
if ( instr - > definitions [ i ] . regClass ( ) . is_subdword ( ) )
2021-01-20 15:27:16 +00:00
check ( instr - > isPseudo ( ) | | instr - > definitions [ i ] . bytes ( ) < = 4 , " Only Pseudo instructions can write subdword registers larger than 4 bytes " , instr . get ( ) ) ;
2020-03-24 18:24:23 +01:00
}
2019-09-17 13:22:17 +02:00
if ( instr - > isSALU ( ) | | instr - > isVALU ( ) ) {
2019-11-25 16:12:44 +00:00
/* check literals */
Operand literal ( s1 ) ;
2019-09-17 13:22:17 +02:00
for ( unsigned i = 0 ; i < instr - > operands . size ( ) ; i + + )
{
2019-11-25 16:12:44 +00:00
Operand op = instr - > operands [ i ] ;
if ( ! op . isLiteral ( ) )
continue ;
2021-01-20 15:27:16 +00:00
check ( instr - > isSOP1 ( ) | | instr - > isSOP2 ( ) | | instr - > isSOPC ( ) | |
instr - > isVOP1 ( ) | | instr - > isVOP2 ( ) | | instr - > isVOPC ( ) | |
2020-09-03 11:59:00 +01:00
( instr - > isVOP3 ( ) & & program - > chip_class > = GFX10 ) | |
2021-01-20 15:27:16 +00:00
( instr - > isVOP3P ( ) & & program - > chip_class > = GFX10 ) ,
2019-11-25 16:12:44 +00:00
" Literal applied on wrong instruction format " , instr . get ( ) ) ;
check ( literal . isUndefined ( ) | | ( literal . size ( ) = = op . size ( ) & & literal . constantValue ( ) = = op . constantValue ( ) ) , " Only 1 Literal allowed " , instr . get ( ) ) ;
literal = op ;
2021-01-20 15:27:16 +00:00
check ( instr - > isSALU ( ) | | instr - > isVOP3 ( ) | | instr - > isVOP3P ( ) | | i = = 0 | | i = = 2 , " Wrong source position for Literal argument " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
}
/* check num sgprs for VALU */
if ( instr - > isVALU ( ) ) {
2019-11-25 16:12:44 +00:00
bool is_shift64 = instr - > opcode = = aco_opcode : : v_lshlrev_b64 | |
instr - > opcode = = aco_opcode : : v_lshrrev_b64 | |
instr - > opcode = = aco_opcode : : v_ashrrev_i64 ;
unsigned const_bus_limit = 1 ;
if ( program - > chip_class > = GFX10 & & ! is_shift64 )
const_bus_limit = 2 ;
2021-01-20 15:27:16 +00:00
uint32_t scalar_mask = instr - > isVOP3 ( ) | | instr - > isVOP3P ( ) ? 0x7 : 0x5 ;
2019-12-04 20:18:05 +00:00
if ( instr - > isSDWA ( ) )
scalar_mask = program - > chip_class > = GFX9 ? 0x7 : 0x4 ;
2021-01-20 15:27:16 +00:00
if ( instr - > isVOPC ( ) | |
2020-06-22 09:17:02 +02:00
instr - > opcode = = aco_opcode : : v_readfirstlane_b32 | |
instr - > opcode = = aco_opcode : : v_readlane_b32 | |
instr - > opcode = = aco_opcode : : v_readlane_b32_e64 ) {
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : sgpr ,
" Wrong Definition type for VALU instruction " , instr . get ( ) ) ;
} else {
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : vgpr ,
" Wrong Definition type for VALU instruction " , instr . get ( ) ) ;
}
2019-11-25 16:12:44 +00:00
unsigned num_sgprs = 0 ;
unsigned sgpr [ ] = { 0 , 0 } ;
for ( unsigned i = 0 ; i < instr - > operands . size ( ) ; i + + )
{
Operand op = instr - > operands [ i ] ;
2019-11-07 18:02:33 +01:00
if ( instr - > opcode = = aco_opcode : : v_readfirstlane_b32 | |
instr - > opcode = = aco_opcode : : v_readlane_b32 | |
2020-06-22 09:17:02 +02:00
instr - > opcode = = aco_opcode : : v_readlane_b32_e64 ) {
check ( i ! = 1 | |
( op . isTemp ( ) & & op . regClass ( ) . type ( ) = = RegType : : sgpr ) | |
op . isConstant ( ) ,
" Must be a SGPR or a constant " , instr . get ( ) ) ;
check ( i = = 1 | |
( op . isTemp ( ) & & op . regClass ( ) . type ( ) = = RegType : : vgpr & & op . bytes ( ) < = 4 ) ,
" Wrong Operand type for VALU instruction " , instr . get ( ) ) ;
continue ;
}
if ( instr - > opcode = = aco_opcode : : v_writelane_b32 | |
2019-11-07 18:02:33 +01:00
instr - > opcode = = aco_opcode : : v_writelane_b32_e64 ) {
2020-06-22 09:17:02 +02:00
check ( i ! = 2 | |
( op . isTemp ( ) & & op . regClass ( ) . type ( ) = = RegType : : vgpr & & op . bytes ( ) < = 4 ) ,
" Wrong Operand type for VALU instruction " , instr . get ( ) ) ;
check ( i = = 2 | |
( op . isTemp ( ) & & op . regClass ( ) . type ( ) = = RegType : : sgpr ) | |
op . isConstant ( ) ,
" Must be a SGPR or a constant " , instr . get ( ) ) ;
2019-11-07 18:02:33 +01:00
continue ;
}
2019-11-25 16:12:44 +00:00
if ( op . isTemp ( ) & & instr - > operands [ i ] . regClass ( ) . type ( ) = = RegType : : sgpr ) {
2019-12-04 20:18:05 +00:00
check ( scalar_mask & ( 1 < < i ) , " Wrong source position for SGPR argument " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
2019-11-25 16:12:44 +00:00
if ( op . tempId ( ) ! = sgpr [ 0 ] & & op . tempId ( ) ! = sgpr [ 1 ] ) {
if ( num_sgprs < 2 )
sgpr [ num_sgprs + + ] = op . tempId ( ) ;
}
2019-09-17 13:22:17 +02:00
}
2019-11-25 16:12:44 +00:00
if ( op . isConstant ( ) & & ! op . isLiteral ( ) )
2019-12-04 20:18:05 +00:00
check ( scalar_mask & ( 1 < < i ) , " Wrong source position for constant argument " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
}
2019-11-25 16:12:44 +00:00
check ( num_sgprs + ( literal . isUndefined ( ) ? 0 : 1 ) < = const_bus_limit , " Too many SGPRs/literals " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
}
2021-01-20 15:27:16 +00:00
if ( instr - > isSOP1 ( ) | | instr - > isSOP2 ( ) ) {
2019-09-17 13:22:17 +02:00
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : sgpr , " Wrong Definition type for SALU instruction " , instr . get ( ) ) ;
for ( const Operand & op : instr - > operands ) {
check ( op . isConstant ( ) | | op . regClass ( ) . type ( ) < = RegType : : sgpr ,
" Wrong Operand type for SALU instruction " , instr . get ( ) ) ;
}
}
}
switch ( instr - > format ) {
case Format : : PSEUDO : {
if ( instr - > opcode = = aco_opcode : : p_create_vector ) {
unsigned size = 0 ;
for ( const Operand & op : instr - > operands ) {
2020-12-30 16:38:08 +00:00
check ( op . bytes ( ) < 4 | | size % 4 = = 0 , " Operand is not aligned " , instr . get ( ) ) ;
2020-02-27 13:08:45 +01:00
size + = op . bytes ( ) ;
2019-09-17 13:22:17 +02:00
}
2020-02-27 13:08:45 +01:00
check ( size = = instr - > definitions [ 0 ] . bytes ( ) , " Definition size does not match operand sizes " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
if ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : sgpr ) {
for ( const Operand & op : instr - > operands ) {
check ( op . isConstant ( ) | | op . regClass ( ) . type ( ) = = RegType : : sgpr ,
" Wrong Operand type for scalar vector " , instr . get ( ) ) ;
}
}
} else if ( instr - > opcode = = aco_opcode : : p_extract_vector ) {
check ( ( instr - > operands [ 0 ] . isTemp ( ) ) & & instr - > operands [ 1 ] . isConstant ( ) , " Wrong Operand types " , instr . get ( ) ) ;
2020-04-10 15:11:03 +01:00
check ( ( instr - > operands [ 1 ] . constantValue ( ) + 1 ) * instr - > definitions [ 0 ] . bytes ( ) < = instr - > operands [ 0 ] . bytes ( ) , " Index out of range " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : vgpr | | instr - > operands [ 0 ] . regClass ( ) . type ( ) = = RegType : : sgpr ,
" Cannot extract SGPR value from VGPR vector " , instr . get ( ) ) ;
2020-12-31 10:46:37 +00:00
check ( program - > chip_class > = GFX9 | | ! instr - > definitions [ 0 ] . regClass ( ) . is_subdword ( ) | |
instr - > operands [ 0 ] . regClass ( ) . type ( ) = = RegType : : vgpr , " Cannot extract subdword from SGPR before GFX9+ " , instr . get ( ) ) ;
2020-09-18 15:55:54 +01:00
} else if ( instr - > opcode = = aco_opcode : : p_split_vector ) {
check ( instr - > operands [ 0 ] . isTemp ( ) , " Operand must be a temporary " , instr . get ( ) ) ;
unsigned size = 0 ;
for ( const Definition & def : instr - > definitions ) {
size + = def . bytes ( ) ;
}
check ( size = = instr - > operands [ 0 ] . bytes ( ) , " Operand size does not match definition sizes " , instr . get ( ) ) ;
if ( instr - > operands [ 0 ] . getTemp ( ) . type ( ) = = RegType : : vgpr ) {
for ( const Definition & def : instr - > definitions )
check ( def . regClass ( ) . type ( ) = = RegType : : vgpr , " Wrong Definition type for VGPR split_vector " , instr . get ( ) ) ;
2020-12-31 10:46:37 +00:00
} else {
for ( const Definition & def : instr - > definitions )
check ( program - > chip_class > = GFX9 | | ! def . regClass ( ) . is_subdword ( ) , " Cannot split SGPR into subdword VGPRs before GFX9+ " , instr . get ( ) ) ;
2020-09-18 15:55:54 +01:00
}
2019-09-17 13:22:17 +02:00
} else if ( instr - > opcode = = aco_opcode : : p_parallelcopy ) {
check ( instr - > definitions . size ( ) = = instr - > operands . size ( ) , " Number of Operands does not match number of Definitions " , instr . get ( ) ) ;
for ( unsigned i = 0 ; i < instr - > operands . size ( ) ; i + + ) {
2020-12-31 10:43:43 +00:00
check ( instr - > definitions [ i ] . bytes ( ) = = instr - > operands [ i ] . bytes ( ) , " Operand and Definition size must match " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
if ( instr - > operands [ i ] . isTemp ( ) )
check ( ( instr - > definitions [ i ] . getTemp ( ) . type ( ) = = instr - > operands [ i ] . regClass ( ) . type ( ) ) | |
( instr - > definitions [ i ] . getTemp ( ) . type ( ) = = RegType : : vgpr & & instr - > operands [ i ] . regClass ( ) . type ( ) = = RegType : : sgpr ) ,
" Operand and Definition types do not match " , instr . get ( ) ) ;
}
} else if ( instr - > opcode = = aco_opcode : : p_phi ) {
check ( instr - > operands . size ( ) = = block . logical_preds . size ( ) , " Number of Operands does not match number of predecessors " , instr . get ( ) ) ;
2020-06-16 10:03:52 +01:00
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : vgpr , " Logical Phi Definition must be vgpr " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
} else if ( instr - > opcode = = aco_opcode : : p_linear_phi ) {
for ( const Operand & op : instr - > operands )
check ( ! op . isTemp ( ) | | op . getTemp ( ) . is_linear ( ) , " Wrong Operand type " , instr . get ( ) ) ;
check ( instr - > operands . size ( ) = = block . linear_preds . size ( ) , " Number of Operands does not match number of predecessors " , instr . get ( ) ) ;
}
break ;
}
2020-10-15 10:23:42 +02:00
case Format : : PSEUDO_REDUCTION : {
for ( const Operand & op : instr - > operands )
check ( op . regClass ( ) . type ( ) = = RegType : : vgpr , " All operands of PSEUDO_REDUCTION instructions must be in VGPRs. " , instr . get ( ) ) ;
2021-01-21 16:13:34 +00:00
if ( instr - > opcode = = aco_opcode : : p_reduce & & instr - > reduction ( ) . cluster_size = = program - > wave_size )
2020-10-15 10:23:42 +02:00
check ( instr - > definitions [ 0 ] . regClass ( ) . type ( ) = = RegType : : sgpr , " The result of unclustered reductions must go into an SGPR. " , instr . get ( ) ) ;
else
check ( instr - > definitions [ 0 ] . regClass ( ) . type ( ) = = RegType : : vgpr , " The result of scans and clustered reductions must go into a VGPR. " , instr . get ( ) ) ;
break ;
}
2019-09-17 13:22:17 +02:00
case Format : : SMEM : {
if ( instr - > operands . size ( ) > = 1 )
2020-08-19 09:41:42 +02:00
check ( ( instr - > operands [ 0 ] . isFixed ( ) & & ! instr - > operands [ 0 ] . isConstant ( ) ) | |
( instr - > operands [ 0 ] . isTemp ( ) & & instr - > operands [ 0 ] . regClass ( ) . type ( ) = = RegType : : sgpr ) , " SMEM operands must be sgpr " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
if ( instr - > operands . size ( ) > = 2 )
check ( instr - > operands [ 1 ] . isConstant ( ) | | ( instr - > operands [ 1 ] . isTemp ( ) & & instr - > operands [ 1 ] . regClass ( ) . type ( ) = = RegType : : sgpr ) ,
" SMEM offset must be constant or sgpr " , instr . get ( ) ) ;
if ( ! instr - > definitions . empty ( ) )
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : sgpr , " SMEM result must be sgpr " , instr . get ( ) ) ;
break ;
}
case Format : : MTBUF :
2020-01-16 16:54:35 +01:00
case Format : : MUBUF : {
2019-09-17 13:22:17 +02:00
check ( instr - > operands . size ( ) > 1 , " VMEM instructions must have at least one operand " , instr . get ( ) ) ;
2020-01-16 16:54:35 +01:00
check ( instr - > operands [ 1 ] . hasRegClass ( ) & & instr - > operands [ 1 ] . regClass ( ) . type ( ) = = RegType : : vgpr ,
2019-09-17 13:22:17 +02:00
" VADDR must be in vgpr for VMEM instructions " , instr . get ( ) ) ;
2020-01-16 16:54:35 +01:00
check ( instr - > operands [ 0 ] . isTemp ( ) & & instr - > operands [ 0 ] . regClass ( ) . type ( ) = = RegType : : sgpr , " VMEM resource constant must be sgpr " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
check ( instr - > operands . size ( ) < 4 | | ( instr - > operands [ 3 ] . isTemp ( ) & & instr - > operands [ 3 ] . regClass ( ) . type ( ) = = RegType : : vgpr ) , " VMEM write data must be vgpr " , instr . get ( ) ) ;
break ;
}
2020-01-16 16:54:35 +01:00
case Format : : MIMG : {
2021-01-14 19:58:13 +00:00
check ( instr - > operands . size ( ) > = 4 , " MIMG instructions must have at least 4 operands " , instr . get ( ) ) ;
2020-01-16 16:54:35 +01:00
check ( instr - > operands [ 0 ] . hasRegClass ( ) & & ( instr - > operands [ 0 ] . regClass ( ) = = s4 | | instr - > operands [ 0 ] . regClass ( ) = = s8 ) ,
" MIMG operands[0] (resource constant) must be in 4 or 8 SGPRs " , instr . get ( ) ) ;
2020-11-19 17:13:56 +00:00
if ( instr - > operands [ 1 ] . hasRegClass ( ) )
2020-01-16 16:54:35 +01:00
check ( instr - > operands [ 1 ] . regClass ( ) = = s4 , " MIMG operands[1] (sampler constant) must be 4 SGPRs " , instr . get ( ) ) ;
2021-01-14 17:46:50 +00:00
if ( ! instr - > operands [ 2 ] . isUndefined ( ) ) {
2020-11-19 17:13:56 +00:00
bool is_cmpswap = instr - > opcode = = aco_opcode : : image_atomic_cmpswap | |
instr - > opcode = = aco_opcode : : image_atomic_fcmpswap ;
2021-01-14 17:46:50 +00:00
check ( instr - > definitions . empty ( ) | | ( instr - > definitions [ 0 ] . regClass ( ) = = instr - > operands [ 2 ] . regClass ( ) | | is_cmpswap ) ,
" MIMG operands[2] (VDATA) must be the same as definitions[0] for atomics and TFE/LWE loads " , instr . get ( ) ) ;
2020-11-19 17:13:56 +00:00
}
2021-01-14 19:58:13 +00:00
check ( instr - > operands . size ( ) = = 4 | | program - > chip_class > = GFX10 , " NSA is only supported on GFX10+ " , instr . get ( ) ) ;
for ( unsigned i = 3 ; i < instr - > operands . size ( ) ; i + + ) {
if ( instr - > operands . size ( ) = = 4 ) {
check ( instr - > operands [ i ] . hasRegClass ( ) & & instr - > operands [ i ] . regClass ( ) . type ( ) = = RegType : : vgpr ,
" MIMG operands[3] (VADDR) must be VGPR " , instr . get ( ) ) ;
} else {
check ( instr - > operands [ i ] . regClass ( ) = = v1 , " MIMG VADDR must be v1 if NSA is used " , instr . get ( ) ) ;
}
}
2020-01-16 16:54:35 +01:00
check ( instr - > definitions . empty ( ) | | ( instr - > definitions [ 0 ] . isTemp ( ) & & instr - > definitions [ 0 ] . regClass ( ) . type ( ) = = RegType : : vgpr ) ,
" MIMG definitions[0] (VDATA) must be VGPR " , instr . get ( ) ) ;
break ;
}
2019-09-17 13:22:17 +02:00
case Format : : DS : {
for ( const Operand & op : instr - > operands ) {
check ( ( op . isTemp ( ) & & op . regClass ( ) . type ( ) = = RegType : : vgpr ) | | op . physReg ( ) = = m0 ,
" Only VGPRs are valid DS instruction operands " , instr . get ( ) ) ;
}
if ( ! instr - > definitions . empty ( ) )
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : vgpr , " DS instruction must return VGPR " , instr . get ( ) ) ;
break ;
}
case Format : : EXP : {
for ( unsigned i = 0 ; i < 4 ; i + + )
check ( instr - > operands [ i ] . hasRegClass ( ) & & instr - > operands [ i ] . regClass ( ) . type ( ) = = RegType : : vgpr ,
" Only VGPRs are valid Export arguments " , instr . get ( ) ) ;
break ;
}
case Format : : FLAT :
check ( instr - > operands [ 1 ] . isUndefined ( ) , " Flat instructions don't support SADDR " , instr . get ( ) ) ;
2020-12-01 09:54:31 +00:00
FALLTHROUGH ;
2019-09-17 13:22:17 +02:00
case Format : : GLOBAL :
case Format : : SCRATCH : {
check ( instr - > operands [ 0 ] . isTemp ( ) & & instr - > operands [ 0 ] . regClass ( ) . type ( ) = = RegType : : vgpr , " FLAT/GLOBAL/SCRATCH address must be vgpr " , instr . get ( ) ) ;
check ( instr - > operands [ 1 ] . hasRegClass ( ) & & instr - > operands [ 1 ] . regClass ( ) . type ( ) = = RegType : : sgpr ,
" FLAT/GLOBAL/SCRATCH sgpr address must be undefined or sgpr " , instr . get ( ) ) ;
if ( ! instr - > definitions . empty ( ) )
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : vgpr , " FLAT/GLOBAL/SCRATCH result must be vgpr " , instr . get ( ) ) ;
else
check ( instr - > operands [ 2 ] . regClass ( ) . type ( ) = = RegType : : vgpr , " FLAT/GLOBAL/SCRATCH data must be vgpr " , instr . get ( ) ) ;
break ;
}
default :
break ;
}
}
}
2019-11-19 14:19:49 +00:00
/* validate CFG */
for ( unsigned i = 0 ; i < program - > blocks . size ( ) ; i + + ) {
Block & block = program - > blocks [ i ] ;
check_block ( block . index = = i , " block.index must match actual index " , & block ) ;
/* predecessors/successors should be sorted */
for ( unsigned j = 0 ; j + 1 < block . linear_preds . size ( ) ; j + + )
check_block ( block . linear_preds [ j ] < block . linear_preds [ j + 1 ] , " linear predecessors must be sorted " , & block ) ;
for ( unsigned j = 0 ; j + 1 < block . logical_preds . size ( ) ; j + + )
check_block ( block . logical_preds [ j ] < block . logical_preds [ j + 1 ] , " logical predecessors must be sorted " , & block ) ;
for ( unsigned j = 0 ; j + 1 < block . linear_succs . size ( ) ; j + + )
check_block ( block . linear_succs [ j ] < block . linear_succs [ j + 1 ] , " linear successors must be sorted " , & block ) ;
for ( unsigned j = 0 ; j + 1 < block . logical_succs . size ( ) ; j + + )
check_block ( block . logical_succs [ j ] < block . logical_succs [ j + 1 ] , " logical successors must be sorted " , & block ) ;
/* critical edges are not allowed */
if ( block . linear_preds . size ( ) > 1 ) {
for ( unsigned pred : block . linear_preds )
check_block ( program - > blocks [ pred ] . linear_succs . size ( ) = = 1 , " linear critical edges are not allowed " , & program - > blocks [ pred ] ) ;
for ( unsigned pred : block . logical_preds )
check_block ( program - > blocks [ pred ] . logical_succs . size ( ) = = 1 , " logical critical edges are not allowed " , & program - > blocks [ pred ] ) ;
}
}
2020-01-30 11:49:20 +00:00
return is_valid ;
2019-09-17 13:22:17 +02:00
}
/* RA validation */
namespace {
struct Location {
Location ( ) : block ( NULL ) , instr ( NULL ) { }
Block * block ;
Instruction * instr ; //NULL if it's the block's live-in
} ;
struct Assignment {
Location defloc ;
Location firstloc ;
PhysReg reg ;
} ;
2020-08-14 10:42:27 +02:00
bool ra_fail ( Program * program , Location loc , Location loc2 , const char * fmt , . . . ) {
2019-09-17 13:22:17 +02:00
va_list args ;
va_start ( args , fmt ) ;
char msg [ 1024 ] ;
vsprintf ( msg , fmt , args ) ;
va_end ( args ) ;
2020-08-14 10:42:27 +02:00
char * out ;
size_t outsize ;
2020-08-04 10:58:11 -07:00
struct u_memstream mem ;
u_memstream_open ( & mem , & out , & outsize ) ;
FILE * const memf = u_memstream_get ( & mem ) ;
2020-08-14 10:42:27 +02:00
fprintf ( memf , " RA error found at instruction in BB%d: \n " , loc . block - > index ) ;
2019-09-17 13:22:17 +02:00
if ( loc . instr ) {
2020-08-14 10:42:27 +02:00
aco_print_instr ( loc . instr , memf ) ;
fprintf ( memf , " \n %s " , msg ) ;
2019-09-17 13:22:17 +02:00
} else {
2020-08-14 10:42:27 +02:00
fprintf ( memf , " %s " , msg ) ;
2019-09-17 13:22:17 +02:00
}
if ( loc2 . block ) {
2020-08-14 10:42:27 +02:00
fprintf ( memf , " in BB%d: \n " , loc2 . block - > index ) ;
aco_print_instr ( loc2 . instr , memf ) ;
2019-09-17 13:22:17 +02:00
}
2020-08-14 10:42:27 +02:00
fprintf ( memf , " \n \n " ) ;
2020-08-04 10:58:11 -07:00
u_memstream_close ( & mem ) ;
2020-08-14 10:42:27 +02:00
2020-11-04 12:44:10 +01:00
aco_err ( program , " %s " , out ) ;
2020-08-14 10:42:27 +02:00
free ( out ) ;
2019-09-17 13:22:17 +02:00
return true ;
}
2020-06-03 11:27:55 +01:00
bool validate_subdword_operand ( chip_class chip , const aco_ptr < Instruction > & instr , unsigned index )
2020-05-06 11:00:24 +01:00
{
2020-06-03 11:27:55 +01:00
Operand op = instr - > operands [ index ] ;
unsigned byte = op . physReg ( ) . byte ( ) ;
2020-07-23 09:58:11 +02:00
if ( instr - > opcode = = aco_opcode : : p_as_uniform )
return byte = = 0 ;
2021-01-20 15:27:16 +00:00
if ( instr - > isPseudo ( ) & & chip > = GFX8 )
2020-06-03 11:27:55 +01:00
return true ;
2021-04-19 11:32:56 +01:00
if ( instr - > isSDWA ( ) ) {
unsigned sel = instr - > sdwa ( ) . sel [ index ] & sdwa_asuint ;
return ( sel & sdwa_isra ) & & ( sel & sdwa_rasize ) < = op . bytes ( ) ;
}
2020-06-03 11:27:55 +01:00
if ( byte = = 2 & & can_use_opsel ( chip , instr - > opcode , index , 1 ) )
return true ;
switch ( instr - > opcode ) {
case aco_opcode : : v_cvt_f32_ubyte1 :
if ( byte = = 1 )
return true ;
break ;
case aco_opcode : : v_cvt_f32_ubyte2 :
if ( byte = = 2 )
return true ;
break ;
case aco_opcode : : v_cvt_f32_ubyte3 :
if ( byte = = 3 )
return true ;
break ;
case aco_opcode : : ds_write_b8_d16_hi :
case aco_opcode : : ds_write_b16_d16_hi :
if ( byte = = 2 & & index = = 1 )
return true ;
break ;
case aco_opcode : : buffer_store_byte_d16_hi :
case aco_opcode : : buffer_store_short_d16_hi :
if ( byte = = 2 & & index = = 3 )
return true ;
break ;
case aco_opcode : : flat_store_byte_d16_hi :
case aco_opcode : : flat_store_short_d16_hi :
case aco_opcode : : scratch_store_byte_d16_hi :
case aco_opcode : : scratch_store_short_d16_hi :
case aco_opcode : : global_store_byte_d16_hi :
case aco_opcode : : global_store_short_d16_hi :
if ( byte = = 2 & & index = = 2 )
return true ;
2021-04-13 17:21:56 +02:00
break ;
2020-06-03 11:27:55 +01:00
default :
break ;
}
return byte = = 0 ;
}
bool validate_subdword_definition ( chip_class chip , const aco_ptr < Instruction > & instr )
{
Definition def = instr - > definitions [ 0 ] ;
unsigned byte = def . physReg ( ) . byte ( ) ;
2021-01-20 15:27:16 +00:00
if ( instr - > isPseudo ( ) & & chip > = GFX8 )
2020-06-03 11:27:55 +01:00
return true ;
2021-01-21 16:13:34 +00:00
if ( instr - > isSDWA ( ) & & instr - > sdwa ( ) . dst_sel = = ( sdwa_isra | def . bytes ( ) ) )
2020-06-03 11:27:55 +01:00
return true ;
if ( byte = = 2 & & can_use_opsel ( chip , instr - > opcode , - 1 , 1 ) )
return true ;
switch ( instr - > opcode ) {
case aco_opcode : : buffer_load_ubyte_d16_hi :
case aco_opcode : : buffer_load_short_d16_hi :
case aco_opcode : : flat_load_ubyte_d16_hi :
case aco_opcode : : flat_load_short_d16_hi :
case aco_opcode : : scratch_load_ubyte_d16_hi :
case aco_opcode : : scratch_load_short_d16_hi :
case aco_opcode : : global_load_ubyte_d16_hi :
case aco_opcode : : global_load_short_d16_hi :
case aco_opcode : : ds_read_u8_d16_hi :
case aco_opcode : : ds_read_u16_d16_hi :
return byte = = 2 ;
default :
break ;
}
return byte = = 0 ;
}
unsigned get_subdword_bytes_written ( Program * program , const aco_ptr < Instruction > & instr , unsigned index )
{
chip_class chip = program - > chip_class ;
Definition def = instr - > definitions [ index ] ;
2021-01-20 15:27:16 +00:00
if ( instr - > isPseudo ( ) )
2020-06-03 11:27:55 +01:00
return chip > = GFX8 ? def . bytes ( ) : def . size ( ) * 4u ;
2021-01-21 16:13:34 +00:00
if ( instr - > isSDWA ( ) & & instr - > sdwa ( ) . dst_sel = = ( sdwa_isra | def . bytes ( ) ) )
2020-06-03 11:27:55 +01:00
return def . bytes ( ) ;
switch ( instr - > opcode ) {
case aco_opcode : : buffer_load_ubyte_d16 :
case aco_opcode : : buffer_load_short_d16 :
case aco_opcode : : flat_load_ubyte_d16 :
case aco_opcode : : flat_load_short_d16 :
case aco_opcode : : scratch_load_ubyte_d16 :
case aco_opcode : : scratch_load_short_d16 :
case aco_opcode : : global_load_ubyte_d16 :
case aco_opcode : : global_load_short_d16 :
case aco_opcode : : ds_read_u8_d16 :
case aco_opcode : : ds_read_u16_d16 :
case aco_opcode : : buffer_load_ubyte_d16_hi :
case aco_opcode : : buffer_load_short_d16_hi :
case aco_opcode : : flat_load_ubyte_d16_hi :
case aco_opcode : : flat_load_short_d16_hi :
case aco_opcode : : scratch_load_ubyte_d16_hi :
case aco_opcode : : scratch_load_short_d16_hi :
case aco_opcode : : global_load_ubyte_d16_hi :
case aco_opcode : : global_load_short_d16_hi :
case aco_opcode : : ds_read_u8_d16_hi :
case aco_opcode : : ds_read_u16_d16_hi :
2021-01-28 13:07:11 +00:00
return program - > dev . sram_ecc_enabled ? 4 : 2 ;
2020-06-03 11:27:55 +01:00
case aco_opcode : : v_mad_f16 :
case aco_opcode : : v_mad_u16 :
case aco_opcode : : v_mad_i16 :
case aco_opcode : : v_fma_f16 :
case aco_opcode : : v_div_fixup_f16 :
case aco_opcode : : v_interp_p2_f16 :
if ( chip > = GFX9 )
return 2 ;
2021-04-13 17:21:56 +02:00
break ;
2020-06-03 11:27:55 +01:00
default :
break ;
}
2020-05-15 15:25:44 +01:00
return MAX2 ( chip > = GFX10 ? def . bytes ( ) : 4 , instr_info . definition_size [ ( int ) instr - > opcode ] / 8u ) ;
2020-05-06 11:00:24 +01:00
}
2019-09-17 13:22:17 +02:00
} /* end namespace */
2020-10-08 10:12:58 +02:00
bool validate_ra ( Program * program ) {
2019-09-17 13:22:17 +02:00
if ( ! ( debug_flags & DEBUG_VALIDATE_RA ) )
return false ;
bool err = false ;
2021-04-20 17:35:41 +01:00
aco : : live live_vars = aco : : live_var_analysis ( program ) ;
2019-09-17 13:22:17 +02:00
std : : vector < std : : vector < Temp > > phi_sgpr_ops ( program - > blocks . size ( ) ) ;
2021-02-05 14:36:39 +01:00
uint16_t sgpr_limit = get_addr_sgpr_from_waves ( program , program - > num_waves ) ;
2019-09-17 13:22:17 +02:00
std : : map < unsigned , Assignment > assignments ;
for ( Block & block : program - > blocks ) {
Location loc ;
loc . block = & block ;
for ( aco_ptr < Instruction > & instr : block . instructions ) {
if ( instr - > opcode = = aco_opcode : : p_phi ) {
for ( unsigned i = 0 ; i < instr - > operands . size ( ) ; i + + ) {
if ( instr - > operands [ i ] . isTemp ( ) & &
instr - > operands [ i ] . getTemp ( ) . type ( ) = = RegType : : sgpr & &
instr - > operands [ i ] . isFirstKill ( ) )
phi_sgpr_ops [ block . logical_preds [ i ] ] . emplace_back ( instr - > operands [ i ] . getTemp ( ) ) ;
}
}
loc . instr = instr . get ( ) ;
for ( unsigned i = 0 ; i < instr - > operands . size ( ) ; i + + ) {
Operand & op = instr - > operands [ i ] ;
if ( ! op . isTemp ( ) )
continue ;
if ( ! op . isFixed ( ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Operand %d is not assigned a register " , i ) ;
2019-09-17 13:22:17 +02:00
if ( assignments . count ( op . tempId ( ) ) & & assignments [ op . tempId ( ) ] . reg ! = op . physReg ( ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , assignments . at ( op . tempId ( ) ) . firstloc , " Operand %d has an inconsistent register assignment with instruction " , i ) ;
2020-04-27 20:51:56 +01:00
if ( ( op . getTemp ( ) . type ( ) = = RegType : : vgpr & & op . physReg ( ) . reg_b + op . bytes ( ) > ( 256 + program - > config - > num_vgprs ) * 4 ) | |
2021-02-05 14:36:39 +01:00
( op . getTemp ( ) . type ( ) = = RegType : : sgpr & & op . physReg ( ) + op . size ( ) > program - > config - > num_sgprs & & op . physReg ( ) < sgpr_limit ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , assignments . at ( op . tempId ( ) ) . firstloc , " Operand %d has an out-of-bounds register assignment " , i ) ;
2020-02-21 12:23:28 +00:00
if ( op . physReg ( ) = = vcc & & ! program - > needs_vcc )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Operand %d fixed to vcc but needs_vcc=false " , i ) ;
2020-06-03 11:27:55 +01:00
if ( op . regClass ( ) . is_subdword ( ) & & ! validate_subdword_operand ( program - > chip_class , instr , i ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Operand %d not aligned correctly " , i ) ;
2019-09-17 13:22:17 +02:00
if ( ! assignments [ op . tempId ( ) ] . firstloc . block )
assignments [ op . tempId ( ) ] . firstloc = loc ;
if ( ! assignments [ op . tempId ( ) ] . defloc . block )
assignments [ op . tempId ( ) ] . reg = op . physReg ( ) ;
}
for ( unsigned i = 0 ; i < instr - > definitions . size ( ) ; i + + ) {
Definition & def = instr - > definitions [ i ] ;
if ( ! def . isTemp ( ) )
continue ;
if ( ! def . isFixed ( ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Definition %d is not assigned a register " , i ) ;
2019-09-17 13:22:17 +02:00
if ( assignments [ def . tempId ( ) ] . defloc . block )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , assignments . at ( def . tempId ( ) ) . defloc , " Temporary %%%d also defined by instruction " , def . tempId ( ) ) ;
2020-04-27 20:51:56 +01:00
if ( ( def . getTemp ( ) . type ( ) = = RegType : : vgpr & & def . physReg ( ) . reg_b + def . bytes ( ) > ( 256 + program - > config - > num_vgprs ) * 4 ) | |
2021-02-05 14:36:39 +01:00
( def . getTemp ( ) . type ( ) = = RegType : : sgpr & & def . physReg ( ) + def . size ( ) > program - > config - > num_sgprs & & def . physReg ( ) < sgpr_limit ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , assignments . at ( def . tempId ( ) ) . firstloc , " Definition %d has an out-of-bounds register assignment " , i ) ;
2020-02-21 12:23:28 +00:00
if ( def . physReg ( ) = = vcc & & ! program - > needs_vcc )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Definition %d fixed to vcc but needs_vcc=false " , i ) ;
2020-06-03 11:27:55 +01:00
if ( def . regClass ( ) . is_subdword ( ) & & ! validate_subdword_definition ( program - > chip_class , instr ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Definition %d not aligned correctly " , i ) ;
2019-09-17 13:22:17 +02:00
if ( ! assignments [ def . tempId ( ) ] . firstloc . block )
assignments [ def . tempId ( ) ] . firstloc = loc ;
assignments [ def . tempId ( ) ] . defloc = loc ;
assignments [ def . tempId ( ) ] . reg = def . physReg ( ) ;
}
}
}
for ( Block & block : program - > blocks ) {
Location loc ;
loc . block = & block ;
2020-03-25 11:32:47 +01:00
std : : array < unsigned , 2048 > regs ; /* register file in bytes */
2019-09-17 13:22:17 +02:00
regs . fill ( 0 ) ;
std : : set < Temp > live ;
2020-09-14 16:45:55 +01:00
for ( unsigned id : live_vars . live_out [ block . index ] )
live . insert ( Temp ( id , program - > temp_rc [ id ] ) ) ;
2019-09-17 13:22:17 +02:00
/* remove killed p_phi sgpr operands */
for ( Temp tmp : phi_sgpr_ops [ block . index ] )
live . erase ( tmp ) ;
/* check live out */
for ( Temp tmp : live ) {
PhysReg reg = assignments . at ( tmp . id ( ) ) . reg ;
2020-03-25 11:32:47 +01:00
for ( unsigned i = 0 ; i < tmp . bytes ( ) ; i + + ) {
if ( regs [ reg . reg_b + i ] ) {
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Assignment of element %d of %%%d already taken by %%%d in live-out " , i , tmp . id ( ) , regs [ reg . reg_b + i ] ) ;
2019-09-17 13:22:17 +02:00
}
2020-03-25 11:32:47 +01:00
regs [ reg . reg_b + i ] = tmp . id ( ) ;
2019-09-17 13:22:17 +02:00
}
}
regs . fill ( 0 ) ;
for ( auto it = block . instructions . rbegin ( ) ; it ! = block . instructions . rend ( ) ; + + it ) {
aco_ptr < Instruction > & instr = * it ;
/* check killed p_phi sgpr operands */
if ( instr - > opcode = = aco_opcode : : p_logical_end ) {
for ( Temp tmp : phi_sgpr_ops [ block . index ] ) {
PhysReg reg = assignments . at ( tmp . id ( ) ) . reg ;
2020-03-25 11:32:47 +01:00
for ( unsigned i = 0 ; i < tmp . bytes ( ) ; i + + ) {
if ( regs [ reg . reg_b + i ] )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , Location ( ) , " Assignment of element %d of %%%d already taken by %%%d in live-out " , i , tmp . id ( ) , regs [ reg . reg_b + i ] ) ;
2019-09-17 13:22:17 +02:00
}
live . emplace ( tmp ) ;
}
}
for ( const Definition & def : instr - > definitions ) {
if ( ! def . isTemp ( ) )
continue ;
live . erase ( def . getTemp ( ) ) ;
}
/* don't count phi operands as live-in, since they are actually
* killed when they are copied at the predecessor */
if ( instr - > opcode ! = aco_opcode : : p_phi & & instr - > opcode ! = aco_opcode : : p_linear_phi ) {
for ( const Operand & op : instr - > operands ) {
if ( ! op . isTemp ( ) )
continue ;
live . insert ( op . getTemp ( ) ) ;
}
}
}
for ( Temp tmp : live ) {
PhysReg reg = assignments . at ( tmp . id ( ) ) . reg ;
2020-03-25 11:32:47 +01:00
for ( unsigned i = 0 ; i < tmp . bytes ( ) ; i + + )
regs [ reg . reg_b + i ] = tmp . id ( ) ;
2019-09-17 13:22:17 +02:00
}
for ( aco_ptr < Instruction > & instr : block . instructions ) {
loc . instr = instr . get ( ) ;
/* remove killed p_phi operands from regs */
if ( instr - > opcode = = aco_opcode : : p_logical_end ) {
for ( Temp tmp : phi_sgpr_ops [ block . index ] ) {
PhysReg reg = assignments . at ( tmp . id ( ) ) . reg ;
2020-03-25 11:32:47 +01:00
for ( unsigned i = 0 ; i < tmp . bytes ( ) ; i + + )
regs [ reg . reg_b + i ] = 0 ;
2019-09-17 13:22:17 +02:00
}
}
if ( instr - > opcode ! = aco_opcode : : p_phi & & instr - > opcode ! = aco_opcode : : p_linear_phi ) {
for ( const Operand & op : instr - > operands ) {
if ( ! op . isTemp ( ) )
continue ;
2020-02-21 15:46:39 +00:00
if ( op . isFirstKillBeforeDef ( ) ) {
2020-03-25 11:32:47 +01:00
for ( unsigned j = 0 ; j < op . getTemp ( ) . bytes ( ) ; j + + )
regs [ op . physReg ( ) . reg_b + j ] = 0 ;
2019-09-17 13:22:17 +02:00
}
}
}
for ( unsigned i = 0 ; i < instr - > definitions . size ( ) ; i + + ) {
Definition & def = instr - > definitions [ i ] ;
if ( ! def . isTemp ( ) )
continue ;
Temp tmp = def . getTemp ( ) ;
PhysReg reg = assignments . at ( tmp . id ( ) ) . reg ;
2020-03-25 11:32:47 +01:00
for ( unsigned j = 0 ; j < tmp . bytes ( ) ; j + + ) {
if ( regs [ reg . reg_b + j ] )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , assignments . at ( regs [ reg . reg_b + j ] ) . defloc , " Assignment of element %d of %%%d already taken by %%%d from instruction " , i , tmp . id ( ) , regs [ reg . reg_b + j ] ) ;
2020-03-25 11:32:47 +01:00
regs [ reg . reg_b + j ] = tmp . id ( ) ;
2019-09-17 13:22:17 +02:00
}
2020-06-03 11:27:55 +01:00
if ( def . regClass ( ) . is_subdword ( ) & & def . bytes ( ) < 4 ) {
unsigned written = get_subdword_bytes_written ( program , instr , i ) ;
/* If written=4, the instruction still might write the upper half. In that case, it's the lower half that isn't preserved */
for ( unsigned j = reg . byte ( ) & ~ ( written - 1 ) ; j < written ; j + + ) {
unsigned written_reg = reg . reg ( ) * 4u + j ;
if ( regs [ written_reg ] & & regs [ written_reg ] ! = def . tempId ( ) )
2020-08-14 10:42:27 +02:00
err | = ra_fail ( program , loc , assignments . at ( regs [ written_reg ] ) . defloc , " Assignment of element %d of %%%d overwrites the full register taken by %%%d from instruction " , i , tmp . id ( ) , regs [ written_reg ] ) ;
2020-06-03 11:27:55 +01:00
}
2020-04-13 17:23:38 +01:00
}
2019-09-17 13:22:17 +02:00
}
for ( const Definition & def : instr - > definitions ) {
if ( ! def . isTemp ( ) )
continue ;
if ( def . isKill ( ) ) {
2020-03-25 11:32:47 +01:00
for ( unsigned j = 0 ; j < def . getTemp ( ) . bytes ( ) ; j + + )
regs [ def . physReg ( ) . reg_b + j ] = 0 ;
2019-09-17 13:22:17 +02:00
}
}
2020-02-21 15:46:39 +00:00
if ( instr - > opcode ! = aco_opcode : : p_phi & & instr - > opcode ! = aco_opcode : : p_linear_phi ) {
for ( const Operand & op : instr - > operands ) {
if ( ! op . isTemp ( ) )
continue ;
if ( op . isLateKill ( ) & & op . isFirstKill ( ) ) {
2020-03-25 11:32:47 +01:00
for ( unsigned j = 0 ; j < op . getTemp ( ) . bytes ( ) ; j + + )
regs [ op . physReg ( ) . reg_b + j ] = 0 ;
2020-02-21 15:46:39 +00:00
}
}
}
2019-09-17 13:22:17 +02:00
}
}
return err ;
}
}