static bool OpRefsReg( name *op, hw_reg_set reg ) /***********************************************/ { switch( op->n.class ) { case N_REGISTER: if( HW_Ovlap( op->r.reg, reg ) ) return( true ); break; case N_INDEXED: assert( op->i.index->n.class == N_REGISTER ); if( HW_Ovlap( op->i.index->r.reg, reg ) ) return( true ); break; } return( false ); }
extern void RegKill( score *scoreboard, hw_reg_set regs ) { /*************************************************************/ score_reg *entry; score_list *curr_list; score_list **owner; int i; list_head **free_heads; if( !HW_CEqual( regs, HW_EMPTY ) ) { entry = *ScoreList; free_heads = (list_head **)&scoreboard[ ScoreCount ]; for( i = ScoreCount; i > 0; --i ) { if( HW_Ovlap( entry->reg, regs ) ) { if( scoreboard->list != NULL ) { if( scoreboard->next_reg == scoreboard ) { ScoreFreeList( scoreboard ); } else { scoreboard->list = *free_heads; *free_heads = (list_head *)**free_heads; *scoreboard->list = NULL; } } scoreboard->prev_reg->next_reg = scoreboard->next_reg; scoreboard->next_reg->prev_reg = scoreboard->prev_reg; scoreboard->next_reg = scoreboard; scoreboard->prev_reg = scoreboard; scoreboard->generation = 0; } else { owner = scoreboard->list; for( ; ; ) { curr_list = *owner; if( curr_list == NULL ) break; if( curr_list->info.index_reg != NO_INDEX && HW_Ovlap( ScoreList[ curr_list->info.index_reg ]->reg, regs ) ) { *owner = curr_list->next; FreeScListEntry( curr_list ); } else { owner = &curr_list->next; } } } ++entry; ++scoreboard; } } }
bool ScAddOk( hw_reg_set reg1, hw_reg_set reg2 ) /********************************************************** Is it ok to say that "reg1" = "reg2"? This is not ok for unalterable registers since there may be hidden modifications of these registers. */ { if( HW_Ovlap( reg1, CurrProc->state.unalterable ) ) { if( !HW_CEqual( reg1, HW_DS ) && !HW_CEqual( reg1, HW_SS ) ) { return( false ); } } if( HW_Ovlap( reg2, CurrProc->state.unalterable ) ) { if( !HW_Equal( reg2, HW_DS ) && !HW_Equal( reg2, HW_SS ) ) { return( false ); } } return( true ); }
/* 370 */ static bool CanUseOp1( instruction *ins, name *op1 ) /******************************************************/ { name *name2; if( op1->n.class != N_REGISTER ) return( false ); if( HW_Ovlap( op1->r.reg, ins->head.next->head.live.regs ) ) return( false ); if( ins->result->n.class == N_INDEXED ) { name2 = ins->result->i.index; if( name2->n.class == N_REGISTER ) { if( HW_Ovlap( name2->r.reg, op1->r.reg ) ) { return( false ); } } } return( true ); }
static int CountRegs( hw_reg_set regs ) /*************************************/ { hw_reg_set *curr; int count; count = 0; for( curr = PushRegs; !HW_CEqual( *curr, HW_EMPTY ); ++curr ) { if( HW_Ovlap( *curr, regs ) ) { count++; } } return( count ); }
bool ScConvert( instruction *ins ) /******************************************** Get rid of instructions like CBW if the high part is not used in the next instruction. */ { hw_reg_set tmp; if( G( ins ) == G_SIGNEX ) { tmp = HighReg( ins->result->r.reg ); if( !HW_Ovlap( ins->head.next->head.live.regs, tmp ) ) { FreeIns( ins ); /* get rid of the pesky cwd or cbw instruction!*/ return( true ); } } return( false ); }
static bool BlockUses( block *blk, hw_reg_set reg ) /*************************************************/ { instruction *ins; int i; for( ins = blk->ins.hd.next; ins->head.opcode != OP_BLOCK; ins = ins->head.next ) { for( i = 0; i < ins->num_operands; i++ ) { if( OpRefsReg( ins->operands[i], reg ) ) return( true ); } if( ins->result != NULL && ins->head.opcode != OP_NOP ) { if( OpRefsReg( ins->result, reg ) ) return( true ); } if( ins->head.opcode != OP_NOP && HW_Ovlap( ins->zap->reg, reg ) ) return( true ); } return( false ); }
type_class_def CallState( aux_handle aux, type_def *tipe, call_state *state ) /****************************************************************************/ { type_class_def class; uint i; hw_reg_set parms[24]; hw_reg_set *parm_src; hw_reg_set *parm_dst; state->unalterable = FixedRegs(); HW_CAsgn( state->modify, HW_FULL ); HW_TurnOff( state->modify, SavedRegs() ); HW_CTurnOff( state->modify, HW_UNUSED ); state->used = state->modify; /* anything not saved is used*/ state->attr = 0; i = 0; parm_src = ParmRegs(); parm_dst = &parms[0]; for(;;) { *parm_dst = *parm_src; if( HW_CEqual( *parm_dst, HW_EMPTY ) ) break; if( HW_Ovlap( *parm_dst, state->unalterable ) ) { FEMessage( MSG_BAD_SAVE, aux ); } HW_CTurnOff( *parm_dst, HW_UNUSED ); parm_dst++; parm_src++; i++; } i++; state->parm.table = CGAlloc( i*sizeof( hw_reg_set ) ); Copy( parms, state->parm.table, i * sizeof( hw_reg_set ) ); HW_CAsgn( state->parm.used, HW_EMPTY ); state->parm.curr_entry = state->parm.table; state->parm.offset = 0; InitPPCParmState( state ); class = ReturnClass( tipe, state->attr ); if( *(call_class *)FEAuxInfo( aux, CALL_CLASS ) & HAS_VARARGS ) { state->attr |= ROUTINE_HAS_VARARGS; } UpdateReturn( state, tipe, class, aux ); return( class ); }
type_class_def CallState( aux_handle aux, type_def *tipe, call_state *state ) /****************************************************************************/ { call_class cclass; type_class_def type_class; uint i; hw_reg_set parms[10]; hw_reg_set *parm_src; hw_reg_set *parm_dst; hw_reg_set *pregs; hw_reg_set tmp; state->unalterable = FixedRegs(); pregs = FEAuxInfo( aux, SAVE_REGS ); HW_CAsgn( state->modify, HW_FULL ); HW_TurnOff( state->modify, *pregs ); HW_CTurnOff( state->modify, HW_UNUSED ); state->used = state->modify; /* anything not saved is used*/ tmp = state->used; HW_TurnOff( tmp, StackReg() ); HW_CTurnOff( tmp, HW_BP ); // should be able to call routine which modifies BP if( HW_Ovlap( state->unalterable, tmp ) ) { FEMessage( MSG_BAD_SAVE, aux ); } state->attr = ROUTINE_REMOVES_PARMS; cclass = *(call_class *)FEAuxInfo( aux, CALL_CLASS ); if( cclass & INTERRUPT ) { state->attr |= ROUTINE_INTERRUPT; } else if( cclass & FAR_CALL ) { state->attr |= ROUTINE_LONG; } else if( cclass & FAR16_CALL ) { state->attr |= ROUTINE_FAR16; } if( cclass & CALLER_POPS ) { state->attr &= ~ROUTINE_REMOVES_PARMS; } if( cclass & SUICIDAL ) { state->attr |= ROUTINE_NEVER_RETURNS; } if( cclass & ROUTINE_RETURN ) { state->attr |= ROUTINE_ALLOCS_RETURN; } if( cclass & NO_STRUCT_REG_RETURNS ) { state->attr |= ROUTINE_NO_STRUCT_REG_RETURNS; } if( cclass & NO_FLOAT_REG_RETURNS ) { state->attr |= ROUTINE_NO_FLOAT_REG_RETURNS; state->attr |= ROUTINE_NO_8087_RETURNS; } if( cclass & NO_8087_RETURNS ) { state->attr |= ROUTINE_NO_8087_RETURNS; } if( cclass & MODIFY_EXACT ) { state->attr |= ROUTINE_MODIFY_EXACT; } if( cclass & NO_MEMORY_CHANGED ) { state->attr |= ROUTINE_MODIFIES_NO_MEMORY; } if( cclass & NO_MEMORY_READ ) { state->attr |= ROUTINE_READS_NO_MEMORY; } if( cclass & LOAD_DS_ON_ENTRY ) { state->attr |= ROUTINE_LOADS_DS; } if( cclass & LOAD_DS_ON_CALL ) { state->attr |= ROUTINE_NEEDS_DS_LOADED; } if( cclass & PARMS_STACK_RESERVE ) { state->attr |= ROUTINE_STACK_RESERVE; } if( cclass & PARMS_PREFER_REGS ) { state->attr |= ROUTINE_PREFER_REGS; } if( cclass & FARSS ) { state->attr |= ROUTINE_FARSS; } if( state == &CurrProc->state ) { if( cclass & ( GENERATE_STACK_FRAME | PROLOG_HOOKS | EPILOG_HOOKS ) ) { CurrProc->prolog_state |= GENERATE_FAT_PROLOG; state->attr |= ROUTINE_NEEDS_PROLOG; } if( cclass & PROLOG_HOOKS ) { CurrProc->prolog_state |= GENERATE_PROLOG_HOOKS; } if( cclass & EPILOG_HOOKS ) { CurrProc->prolog_state |= GENERATE_EPILOG_HOOKS; } if( cclass & FAT_WINDOWS_PROLOG ) { CurrProc->prolog_state |= GENERATE_FAT_PROLOG; } if( cclass & EMIT_FUNCTION_NAME ) { CurrProc->prolog_state |= GENERATE_FUNCTION_NAME; } if( cclass & THUNK_PROLOG ) { CurrProc->prolog_state |= GENERATE_THUNK_PROLOG; } if( cclass & GROW_STACK ) { CurrProc->prolog_state |= GENERATE_GROW_STACK; } if( cclass & TOUCH_STACK ) { CurrProc->prolog_state |= GENERATE_TOUCH_STACK; } if( cclass & LOAD_RDOSDEV_ON_ENTRY ) { CurrProc->prolog_state |= GENERATE_RDOSDEV_PROLOG; } } type_class = ReturnClass( tipe, state->attr ); i = 0; parm_dst = &parms[0]; for( parm_src = FEAuxInfo( aux, PARM_REGS ); !HW_CEqual( *parm_src, HW_EMPTY ); ++parm_src ) { *parm_dst = *parm_src; if( HW_Ovlap( *parm_dst, state->unalterable ) ) { FEMessage( MSG_BAD_SAVE, aux ); } HW_CTurnOff( *parm_dst, HW_UNUSED ); parm_dst++; i++; } *parm_dst = *parm_src; i++; state->parm.table = CGAlloc( i*sizeof( hw_reg_set ) ); Copy( parms, state->parm.table, i * sizeof( hw_reg_set ) ); HW_CAsgn( state->parm.used, HW_EMPTY ); state->parm.curr_entry = state->parm.table; state->parm.offset = 0; if( tipe == TypeNone ) { HW_CAsgn( state->return_reg, HW_EMPTY ); } else if( type_class == XX ) { if( cclass & SPECIAL_STRUCT_RETURN ) { pregs = FEAuxInfo( aux, STRETURN_REG ); state->return_reg = *pregs; state->attr |= ROUTINE_HAS_SPECIAL_RETURN; } else { state->return_reg = StructReg(); } if( (state->attr & ROUTINE_ALLOCS_RETURN) == 0 ) { tmp = ReturnReg( WD, false ); HW_TurnOn( state->modify, tmp ); } } else { if( cclass & SPECIAL_RETURN ) { pregs = FEAuxInfo( aux, RETURN_REG ); state->return_reg = *pregs; state->attr |= ROUTINE_HAS_SPECIAL_RETURN; } else { state->return_reg = ReturnReg( type_class, _NPX( state->attr ) ); } } UpdateReturn( state, tipe, type_class, aux ); return( type_class ); }
void FlowSave( hw_reg_set *preg ) /*******************************/ { int score; int i, j; int best; int num_blocks; int num_regs; int curr_reg; hw_reg_set *curr_push; reg_flow_info *reg_info; block *save; block *restore; instruction *ins; type_class_def reg_type; HW_CAsgn( flowedRegs, HW_EMPTY ); if( _IsntModel( FLOW_REG_SAVES ) ) return; if( !HaveDominatorInfo ) return; // we can't do this if we have push's which are 'live' at the end of a block // - this flag is set when we see a push being generated for a call in a different // block #if _TARGET & _TARG_INTEL if( CurrProc->targ.never_sp_frame ) return; #endif num_regs = CountRegs( *preg ); if( num_regs == 0 ) return; reg_info = CGAlloc( num_regs * sizeof( reg_flow_info ) ); num_blocks = CountBlocks(); InitBlockArray(); curr_push = PushRegs; for( curr_reg = 0; curr_reg < num_regs; curr_reg++ ) { while( !HW_Ovlap( *curr_push, *preg ) ) curr_push++; HW_Asgn( reg_info[curr_reg].reg, *curr_push ); reg_info[curr_reg].save = NULL; reg_info[curr_reg].restore = NULL; #if _TARGET & _TARG_INTEL if( HW_COvlap( *curr_push, HW_BP ) ) continue; // don't mess with BP - it's magical #endif GetRegUsage( ®_info[curr_reg] ); best = 0; for( i = 0; i < num_blocks; i++ ) { for( j = 0; j < num_blocks; j++ ) { if( PairOk( blockArray[i], blockArray[j], ®_info[0], curr_reg ) ) { // we use the number of blocks dominated by the save block plus // the number of blocks post-dominated by the restore block as a // rough metric for determining how much we like a given (valid) // pair of blocks - the more blocks dominated, the further 'in' // we have pushed the save, which should be good score = CountDomBits( &blockArray[i]->dom.dominator ); score += CountDomBits( &blockArray[j]->dom.post_dominator ); if( score > best ) { best = score; reg_info[curr_reg].save = blockArray[i]; reg_info[curr_reg].restore = blockArray[j]; } } } } // so now we know where we are going to save and restore the register // emit the instructions to do so, and remove reg from the set to push // in the normal prolog sequence save = reg_info[curr_reg].save; restore = reg_info[curr_reg].restore; if( ( save != NULL && save != HeadBlock ) && ( restore != NULL && !_IsBlkAttr( restore, BLK_RETURN ) ) ) { reg_type = WD; #if _TARGET & _TARG_INTEL if( IsSegReg( reg_info[curr_reg].reg ) ) { reg_type = U2; } #endif ins = MakeUnary( OP_PUSH, AllocRegName( reg_info[curr_reg].reg ), NULL, reg_type ); ResetGenEntry( ins ); PrefixIns( save->ins.hd.next, ins ); ins = MakeUnary( OP_POP, NULL, AllocRegName( reg_info[curr_reg].reg ), reg_type ); ins->num_operands = 0; ResetGenEntry( ins ); SuffixIns( restore->ins.hd.prev, ins ); HW_TurnOff( *preg, reg_info[curr_reg].reg ); HW_TurnOn( flowedRegs, reg_info[curr_reg].reg ); FixStackDepth( save, restore ); } curr_push++; } CGFree( reg_info ); }
extern type_class_def CallState( aux_handle aux, type_def *tipe, call_state *state ) /*******************************************************************/ { type_class_def class; uint i; hw_reg_set parms[20]; hw_reg_set *parm_src; hw_reg_set *parm_dst; hw_reg_set *pregs; call_class cclass; call_class *pcclass; risc_byte_seq *code; bool have_aux_code = FALSE; state->unalterable = FixedRegs(); if( FEAttr( AskForLblSym( CurrProc->label ) ) & FE_VARARGS ) { HW_TurnOn( state->unalterable, VarargsHomePtr() ); } // For code bursts only, query the #pragma aux instead of using // hardcoded calling convention. If it ever turns out that we need // to support more than a single calling convention, this will need // to change to work more like x86 if( !AskIfRTLabel( CurrProc->label ) ) { code = FEAuxInfo( aux, CALL_BYTES ); if( code != NULL ) { have_aux_code = TRUE; } } pregs = FEAuxInfo( aux, SAVE_REGS ); HW_CAsgn( state->modify, HW_FULL ); if( have_aux_code ) { HW_TurnOff( state->modify, *pregs ); } else { HW_TurnOff( state->modify, SavedRegs() ); } HW_CTurnOff( state->modify, HW_UNUSED ); state->used = state->modify; /* anything not saved is used */ state->attr = 0; pcclass = FEAuxInfo( aux, CALL_CLASS ); cclass = *pcclass; if( cclass & SETJMP_KLUGE ) { state->attr |= ROUTINE_IS_SETJMP; } if( cclass & SUICIDAL ) { state->attr |= ROUTINE_NEVER_RETURNS; } if( cclass & NO_MEMORY_CHANGED ) { state->attr |= ROUTINE_MODIFIES_NO_MEMORY; } if( cclass & NO_MEMORY_READ ) { state->attr |= ROUTINE_READS_NO_MEMORY; } i = 0; if( have_aux_code ) { parm_src = FEAuxInfo( aux, PARM_REGS ); } else { parm_src = ParmRegs(); } parm_dst = &parms[0]; for( ;; ) { *parm_dst = *parm_src; if( HW_CEqual( *parm_dst, HW_EMPTY ) ) break; if( HW_Ovlap( *parm_dst, state->unalterable ) ) { FEMessage( MSG_BAD_SAVE, aux ); } HW_CTurnOff( *parm_dst, HW_UNUSED ); parm_dst++; parm_src++; i++; } i++; state->parm.table = CGAlloc( i * sizeof( hw_reg_set ) ); Copy( parms, state->parm.table, i * sizeof( hw_reg_set ) ); HW_CAsgn( state->parm.used, HW_EMPTY ); state->parm.curr_entry = state->parm.table; state->parm.offset = 0; class = ReturnClass( tipe, state->attr ); UpdateReturn( state, tipe, class, aux ); return( class ); }