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>
namespace aco {
# ifndef NDEBUG
void perfwarn ( bool cond , const char * msg , Instruction * instr )
{
if ( cond ) {
fprintf ( stderr , " ACO performance warning: %s \n " , msg ) ;
if ( instr ) {
fprintf ( stderr , " instruction: " ) ;
aco_print_instr ( instr , stderr ) ;
fprintf ( stderr , " \n " ) ;
}
if ( debug_flags & DEBUG_PERFWARN )
exit ( 1 ) ;
}
}
# endif
void validate ( Program * program , FILE * output )
{
if ( ! ( debug_flags & DEBUG_VALIDATE ) )
return ;
bool is_valid = true ;
auto check = [ & output , & is_valid ] ( bool check , const char * msg , aco : : Instruction * instr ) - > void {
if ( ! check ) {
fprintf ( output , " %s: " , msg ) ;
aco_print_instr ( instr , output ) ;
fprintf ( output , " \n " ) ;
is_valid = false ;
}
} ;
2019-11-19 14:19:49 +00:00
auto check_block = [ & output , & is_valid ] ( bool check , const char * msg , aco : : Block * block ) - > void {
if ( ! check ) {
fprintf ( output , " %s: BB%u \n " , msg , block - > index ) ;
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 */
if ( ( ( uint32_t ) instr - > format & ( uint32_t ) Format : : VOP3 ) & & instr - > format ! = Format : : VOP3 ) {
check ( base_format = = Format : : VOP2 | |
base_format = = Format : : VOP1 | |
base_format = = Format : : VOPC | |
base_format = = Format : : VINTRP ,
" Format cannot have VOP3A/VOP3B applied " , instr . get ( ) ) ;
}
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 ( ) ) ;
SDWA_instruction * sdwa = static_cast < SDWA_instruction * > ( instr . get ( ) ) ;
check ( sdwa - > omod = = 0 | | program - > chip_class > = GFX9 , " SDWA omod only supported on GFX9+ " , instr . get ( ) ) ;
if ( base_format = = Format : : VOPC ) {
check ( sdwa - > clamp = = false | | program - > chip_class = = GFX8 , " SDWA VOPC clamp only supported on GFX8 " , instr . get ( ) ) ;
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
for ( unsigned i = 0 ; i < MIN2 ( instr - > operands . size ( ) , 2 ) ; i + + ) {
2020-06-16 19:13:34 +01:00
if ( instr - > operands [ i ] . hasRegClass ( ) & & instr - > operands [ i ] . regClass ( ) . is_subdword ( ) )
2020-06-03 11:27:55 +01:00
check ( ( sdwa - > sel [ i ] & sdwa_asuint ) = = ( sdwa_isra | instr - > operands [ i ] . bytes ( ) ) , " Unexpected SDWA sel for sub-dword operand " , instr . get ( ) ) ;
}
if ( instr - > definitions [ 0 ] . regClass ( ) . is_subdword ( ) )
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 ( ) ) {
VOP3A_instruction * vop3 = static_cast < VOP3A_instruction * > ( instr . get ( ) ) ;
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 ( ) ) )
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 ( ) )
2020-06-03 11:27:55 +01: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 ( ) ) {
bool flat = instr - > format = = Format : : FLAT | | instr - > format = = Format : : SCRATCH | | instr - > format = = Format : : GLOBAL ;
bool can_be_undef = is_phi ( instr ) | | instr - > format = = Format : : EXP | |
instr - > format = = Format : : PSEUDO_REDUCTION | |
2020-02-27 13:08:45 +01:00
instr - > opcode = = aco_opcode : : p_create_vector | |
2020-01-16 16:54:35 +01:00
( flat & & i = = 1 ) | | ( instr - > format = = Format : : MIMG & & i = = 1 ) | |
( ( instr - > format = = Format : : MUBUF | | instr - > format = = Format : : MTBUF ) & & 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 ( ) )
2020-05-06 11:00:24 +01:00
check ( instr - > format = = Format : : PSEUDO | | 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 ;
check ( instr - > format = = Format : : SOP1 | |
instr - > format = = Format : : SOP2 | |
instr - > format = = Format : : SOPC | |
instr - > format = = Format : : VOP1 | |
instr - > format = = Format : : VOP2 | |
instr - > format = = Format : : VOPC | |
( instr - > isVOP3 ( ) & & program - > chip_class > = GFX10 ) ,
" 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 ;
check ( ! instr - > isVALU ( ) | | instr - > isVOP3 ( ) | | 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 ;
2019-12-04 20:18:05 +00:00
uint32_t scalar_mask = instr - > isVOP3 ( ) ? 0x7 : 0x5 ;
if ( instr - > isSDWA ( ) )
scalar_mask = program - > chip_class > = GFX9 ? 0x7 : 0x4 ;
2019-09-17 13:22:17 +02:00
check ( instr - > definitions [ 0 ] . getTemp ( ) . type ( ) = = RegType : : vgpr | |
( int ) instr - > format & ( int ) Format : : VOPC | |
instr - > opcode = = aco_opcode : : v_readfirstlane_b32 | |
2019-11-07 18:02:33 +01:00
instr - > opcode = = aco_opcode : : v_readlane_b32 | |
instr - > opcode = = aco_opcode : : v_readlane_b32_e64 ,
2019-09-17 13:22:17 +02:00
" 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 | |
instr - > opcode = = aco_opcode : : v_readlane_b32_e64 | |
instr - > opcode = = aco_opcode : : v_writelane_b32 | |
instr - > opcode = = aco_opcode : : v_writelane_b32_e64 ) {
2019-11-25 16:12:44 +00:00
check ( ! op . isLiteral ( ) , " No literal allowed on VALU instruction " , instr . get ( ) ) ;
2020-04-08 08:53:47 +02:00
check ( i = = 1 | | ( op . isTemp ( ) & & op . regClass ( ) . type ( ) = = RegType : : vgpr & & op . bytes ( ) < = 4 ) , " Wrong Operand type for VALU instruction " , 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
}
if ( instr - > format = = Format : : SOP1 | | instr - > format = = Format : : SOP2 ) {
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 : {
2020-05-19 11:45:12 +01:00
bool is_subdword = false ;
bool has_const_sgpr = false ;
bool has_literal = false ;
for ( Definition def : instr - > definitions )
is_subdword | = def . regClass ( ) . is_subdword ( ) ;
for ( unsigned i = 0 ; i < instr - > operands . size ( ) ; i + + ) {
if ( instr - > opcode = = aco_opcode : : p_extract_vector & & i = = 1 )
continue ;
Operand op = instr - > operands [ i ] ;
is_subdword | = op . hasRegClass ( ) & & op . regClass ( ) . is_subdword ( ) ;
has_const_sgpr | = op . isConstant ( ) | | ( op . hasRegClass ( ) & & op . regClass ( ) . type ( ) = = RegType : : sgpr ) ;
has_literal | = op . isLiteral ( ) ;
}
check ( ! is_subdword | | ! has_const_sgpr | | program - > chip_class > = GFX9 ,
" Sub-dword pseudo instructions can only take constants or SGPRs on GFX9+ " , instr . get ( ) ) ;
check ( ! is_subdword | | ! has_literal , " Sub-dword pseudo instructions cannot take literals " , instr . get ( ) ) ;
2019-09-17 13:22:17 +02:00
if ( instr - > opcode = = aco_opcode : : p_create_vector ) {
unsigned size = 0 ;
for ( const Operand & op : instr - > operands ) {
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 ( ) ) ;
} 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 + + ) {
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 ;
}
case Format : : SMEM : {
if ( instr - > operands . size ( ) > = 1 )
check ( instr - > operands [ 0 ] . isTemp ( ) & & instr - > operands [ 0 ] . regClass ( ) . type ( ) = = RegType : : sgpr , " SMEM operands must be sgpr " , instr . get ( ) ) ;
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 : {
check ( instr - > operands . size ( ) = = 3 , " MIMG instructions must have exactly 3 operands " , instr . get ( ) ) ;
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 ( ) ) ;
if ( instr - > operands [ 1 ] . hasRegClass ( ) & & instr - > operands [ 1 ] . regClass ( ) . type ( ) = = RegType : : sgpr )
check ( instr - > operands [ 1 ] . regClass ( ) = = s4 , " MIMG operands[1] (sampler constant) must be 4 SGPRs " , instr . get ( ) ) ;
else if ( instr - > operands [ 1 ] . hasRegClass ( ) & & instr - > operands [ 1 ] . regClass ( ) . type ( ) = = RegType : : vgpr )
2020-01-31 10:41:39 +01:00
check ( ( instr - > definitions . empty ( ) | | instr - > definitions [ 0 ] . regClass ( ) = = instr - > operands [ 1 ] . regClass ( ) | |
instr - > opcode = = aco_opcode : : image_atomic_cmpswap | | instr - > opcode = = aco_opcode : : image_atomic_fcmpswap ) ,
2020-01-16 16:54:35 +01:00
" MIMG operands[1] (VDATA) must be the same as definitions[0] for atomics " , instr . get ( ) ) ;
check ( instr - > operands [ 2 ] . hasRegClass ( ) & & instr - > operands [ 2 ] . regClass ( ) . type ( ) = = RegType : : vgpr ,
" MIMG operands[2] (VADDR) must be VGPR " , instr . get ( ) ) ;
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 ( ) ) ;
/* fallthrough */
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 ] ) ;
}
}
2019-09-17 13:22:17 +02:00
assert ( is_valid ) ;
}
/* 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 ;
} ;
bool ra_fail ( FILE * output , Location loc , Location loc2 , const char * fmt , . . . ) {
va_list args ;
va_start ( args , fmt ) ;
char msg [ 1024 ] ;
vsprintf ( msg , fmt , args ) ;
va_end ( args ) ;
fprintf ( stderr , " RA error found at instruction in BB%d: \n " , loc . block - > index ) ;
if ( loc . instr ) {
aco_print_instr ( loc . instr , stderr ) ;
fprintf ( stderr , " \n %s " , msg ) ;
} else {
fprintf ( stderr , " %s " , msg ) ;
}
if ( loc2 . block ) {
fprintf ( stderr , " in BB%d: \n " , loc2 . block - > index ) ;
aco_print_instr ( loc2 . instr , stderr ) ;
}
fprintf ( stderr , " \n \n " ) ;
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 ( ) ;
if ( instr - > format = = Format : : PSEUDO & & chip > = GFX8 )
return true ;
if ( instr - > isSDWA ( ) & & ( static_cast < SDWA_instruction * > ( instr . get ( ) ) - > sel [ index ] & sdwa_asuint ) = = ( sdwa_isra | op . bytes ( ) ) )
return true ;
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 ;
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 ( ) ;
if ( instr - > format = = Format : : PSEUDO & & chip > = GFX8 )
return true ;
if ( instr - > isSDWA ( ) & & static_cast < SDWA_instruction * > ( instr . get ( ) ) - > dst_sel = = ( sdwa_isra | def . bytes ( ) ) )
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 ] ;
if ( instr - > format = = Format : : PSEUDO )
return chip > = GFX8 ? def . bytes ( ) : def . size ( ) * 4u ;
if ( instr - > isSDWA ( ) & & static_cast < SDWA_instruction * > ( instr . get ( ) ) - > dst_sel = = ( sdwa_isra | def . bytes ( ) ) )
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 :
return program - > sram_ecc_enabled ? 4 : 2 ;
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 ;
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 */
bool validate_ra ( Program * program , const struct radv_nir_compiler_options * options , FILE * output ) {
if ( ! ( debug_flags & DEBUG_VALIDATE_RA ) )
return false ;
bool err = false ;
aco : : live live_vars = aco : : live_var_analysis ( program , options ) ;
std : : vector < std : : vector < Temp > > phi_sgpr_ops ( program - > blocks . size ( ) ) ;
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 ( ) )
err | = ra_fail ( output , loc , Location ( ) , " Operand %d is not assigned a register " , i ) ;
if ( assignments . count ( op . tempId ( ) ) & & assignments [ op . tempId ( ) ] . reg ! = op . physReg ( ) )
err | = ra_fail ( output , 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 ) | |
2019-09-17 13:22:17 +02:00
( op . getTemp ( ) . type ( ) = = RegType : : sgpr & & op . physReg ( ) + op . size ( ) > program - > config - > num_sgprs & & op . physReg ( ) < program - > sgpr_limit ) )
err | = ra_fail ( output , 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 )
err | = ra_fail ( output , 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 ) )
err | = ra_fail ( output , 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 ( ) )
err | = ra_fail ( output , loc , Location ( ) , " Definition %d is not assigned a register " , i ) ;
if ( assignments [ def . tempId ( ) ] . defloc . block )
err | = ra_fail ( output , 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 ) | |
2019-09-17 13:22:17 +02:00
( def . getTemp ( ) . type ( ) = = RegType : : sgpr & & def . physReg ( ) + def . size ( ) > program - > config - > num_sgprs & & def . physReg ( ) < program - > sgpr_limit ) )
err | = ra_fail ( output , 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 )
err | = ra_fail ( output , 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 ) )
err | = ra_fail ( output , 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 ;
live . insert ( live_vars . live_out [ block . index ] . begin ( ) , live_vars . live_out [ block . index ] . end ( ) ) ;
/* 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 ] ) {
err | = ra_fail ( output , 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 ] )
err | = ra_fail ( output , 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-04-13 17:23:38 +01:00
err | = ra_fail ( output , 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 ( ) )
err | = ra_fail ( output , 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-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 ;
}
}