static void CopyAuxInfo( void ) /*****************************/ { hw_reg_set default_flt_n_seg; hw_reg_set flt_n_seg; if( CurrEntry == NULL ) { // Redefining a built-in calling convention } else { CurrInfo = (struct aux_info *)CMemAlloc( sizeof( struct aux_info ) ); *CurrInfo = *CurrAlias; } if( AuxInfo.code != NULL ) { CurrInfo->code = AuxInfo.code; } if( AuxInfoFlg.f_near ) { CurrInfo->cclass &= ~FAR; } if( AuxInfoFlg.f_routine_pops ) { CurrInfo->cclass &= ~CALLER_POPS; } if( AuxInfoFlg.f_caller_return ) { CurrInfo->cclass &= ~ROUTINE_RETURN; } if( AuxInfoFlg.f_8087_returns ) { CurrInfo->cclass &= ~NO_8087_RETURNS; } CurrInfo->cclass |= AuxInfo.cclass; CurrInfo->flags |= AuxInfo.flags; if( AuxInfo.objname != NULL ) CurrInfo->objname = AuxInfo.objname; if( AuxInfo.cclass & SPECIAL_RETURN ) CurrInfo->returns = AuxInfo.returns; if( AuxInfo.cclass & SPECIAL_STRUCT_RETURN ) CurrInfo->streturn = AuxInfo.streturn; if( AuxInfo.parms != NULL ) CurrInfo->parms = AuxInfo.parms; if( !HW_CEqual( AuxInfo.save, HW_EMPTY ) ) { HW_CTurnOn( CurrInfo->save, HW_FULL ); if( !( AuxInfo.cclass & MODIFY_EXACT ) && !CompFlags.save_restore_segregs ) { HW_Asgn( default_flt_n_seg, WatcallInfo.save ); HW_CAsgn( flt_n_seg, HW_FLTS ); HW_CTurnOn( flt_n_seg, HW_SEGS ); HW_TurnOff( CurrInfo->save, flt_n_seg ); HW_OnlyOn( default_flt_n_seg, flt_n_seg ); HW_TurnOn( CurrInfo->save, default_flt_n_seg ); } HW_TurnOff( CurrInfo->save, AuxInfo.save ); } }
hw_reg_set MustSaveRegs( void ) /**********************************/ { hw_reg_set save; hw_reg_set tmp; HW_CAsgn( save, HW_FULL ); HW_TurnOff( save, CurrProc->state.modify ); HW_CTurnOff( save, HW_UNUSED ); if( CurrProc->state.attr & ROUTINE_MODIFY_EXACT ) { HW_TurnOff( save, CurrProc->state.return_reg ); } else { tmp = CurrProc->state.parm.used; HW_TurnOn( tmp, CurrProc->state.return_reg ); tmp = FullReg( tmp ); HW_TurnOff( save, tmp ); } tmp = StackReg(); HW_TurnOff( save, tmp ); if( HW_CEqual( CurrProc->state.return_reg, HW_EMPTY ) ) { tmp = ReturnReg( WD, _NPX( CurrProc->state.attr ) ); HW_TurnOff( save, tmp ); } tmp = CurrProc->state.unalterable; HW_TurnOff( tmp, DisplayReg() ); HW_TurnOff( tmp, StackReg() ); HW_TurnOff( save, tmp ); return( save ); }
static bool Check( hw_reg_set *name, hw_reg_set test ) { /*********************************************************************/ if( !HW_Subset( *name, test ) ) return( false ); HW_TurnOff( *name, test ); return( true ); }
static bool Check( hw_reg_set *name, hw_reg_set test ) { /*********************************************************************/ if( !HW_Subset( *name, test ) ) return( FALSE ); HW_TurnOff( *name, test ); return( TRUE ); }
static void GetSaveInfo( void ) { hw_reg_set modlist; hw_reg_set default_flt_n_seg; hw_reg_set flt_n_seg; struct { unsigned f_exact : 1; unsigned f_nomemory : 1; unsigned f_list : 1; } have; have.f_exact = 0; have.f_nomemory = 0; have.f_list = 0; for( ;; ) { if( !have.f_exact && PragRecog( "exact" ) ) { CurrInfo->cclass |= MODIFY_EXACT; have.f_exact = 1; } else if( !have.f_nomemory && PragRecog( "nomemory" ) ) { CurrInfo->cclass |= NO_MEMORY_CHANGED; have.f_nomemory = 1; } else if( !have.f_list && IS_REGSET( CurToken ) ) { modlist = PragRegList(); have.f_list = 1; } else { break; } } if( have.f_list ) { HW_Asgn( default_flt_n_seg, DefaultInfo.save ); HW_CTurnOn( CurrInfo->save, HW_FULL ); if( !have.f_exact && !CompFlags.save_restore_segregs ) { HW_CAsgn( flt_n_seg, HW_FLTS ); HW_CTurnOn( flt_n_seg, HW_SEGS ); HW_TurnOff( CurrInfo->save, flt_n_seg ); HW_OnlyOn( default_flt_n_seg, flt_n_seg ); HW_TurnOn( CurrInfo->save, default_flt_n_seg ); } HW_TurnOff( CurrInfo->save, modlist ); } }
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 FlowRestore( hw_reg_set *preg ) /**********************************/ { HW_TurnOff( *preg, flowedRegs ); }
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 ); }
instruction *rMAKECALL( instruction *ins ) /********************************************* Using the table RTInfo[], do all the necessary stuff to turn instruction "ins" into a call to a runtime support routine. Move the parms into registers, and move the return register of the runtime routine into the result. Used for 386 and 370 versions */ { rtn_info *info; label_handle lbl; instruction *left_ins; instruction *new_ins; instruction *last_ins; name *reg_name; hw_reg_set regs; hw_reg_set all_regs; hw_reg_set tmp; rt_class rtindex; if( !_IsConvert( ins ) ) { rtindex = LookupRoutine( ins ); } else { /* look it up again in case we ran out of memory during expansion*/ rtindex = LookupConvertRoutine( ins ); } info = &RTInfo[rtindex]; regs = _ParmReg( info->left ); all_regs = regs; left_ins = MakeMove( ins->operands[0], AllocRegName( regs ), info->operand_class ); ins->operands[0] = left_ins->result; MoveSegOp( ins, left_ins, 0 ); PrefixIns( ins, left_ins ); regs = _ParmReg( info->right ); if( !HW_CEqual( regs, HW_EMPTY ) ) { new_ins = MakeMove( ins->operands[1], AllocRegName( regs ), info->operand_class ); ins->operands[1] = new_ins->result; MoveSegOp( ins, new_ins, 0 ); HW_TurnOn( all_regs, regs ); PrefixIns( ins, new_ins ); } #if _TARGET & _TARG_370 tmp = RAReg(); HW_TurnOn( all_regs, tmp ); tmp = LNReg(); HW_TurnOn( all_regs, tmp ); #elif _TARGET & _TARG_80386 { tmp = ReturnReg( WD, false ); HW_TurnOn( all_regs, tmp ); } #endif reg_name = AllocRegName( all_regs ); lbl = RTLabel( rtindex ); new_ins = NewIns( 3 ); new_ins->head.opcode = OP_CALL; new_ins->type_class = ins->type_class; new_ins->operands[CALL_OP_USED] = reg_name; new_ins->operands[CALL_OP_USED2] = reg_name; new_ins->operands[CALL_OP_ADDR] = AllocMemory( lbl, 0, CG_LBL, ins->type_class ); new_ins->result = NULL; new_ins->num_operands = 2; /* special case for OP_CALL*/ #if _TARGET & _TARG_AXP { HW_CTurnOn( all_regs, HW_FULL ); HW_TurnOff( all_regs, SavedRegs() ); HW_CTurnOff( all_regs, HW_UNUSED ); HW_TurnOn( all_regs, ReturnAddrReg() ); } #endif new_ins->zap = (register_name *)AllocRegName( all_regs ); /* all parm regs could be zapped*/ last_ins = new_ins; if( ins->result == NULL || _OpIsCondition( ins->head.opcode ) ) { /* comparison, still need conditional jumps*/ ins->operands[0] = AllocIntConst( 0 ); ins->operands[1] = AllocIntConst( 1 ); DelSeg( ins ); DoNothing( ins ); /* just conditional jumps for ins*/ PrefixIns( ins, new_ins ); new_ins->ins_flags |= INS_CC_USED; last_ins = ins; } else { regs = _ParmReg( info->result ); tmp = regs; HW_TurnOn( tmp, new_ins->zap->reg ); new_ins->zap = (register_name *)AllocRegName( tmp ); reg_name = AllocRegName( regs ); new_ins->result = reg_name; last_ins = MakeMove( reg_name, ins->result, ins->type_class ); ins->result = last_ins->operands[0]; MoveSegRes( ins, last_ins ); SuffixIns( ins, last_ins ); ReplIns( ins, new_ins ); } FixCallIns( new_ins ); UpdateLive( left_ins, last_ins ); return( left_ins ); }