cse.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 1,632 行 · 第 1/4 页

C
1,632
字号
                if( next_bit == EMPTY ) break;
                _BLKBITS( blk ) = _BLKBITS( daddy ) | next_bit;
                change = TRUE;
            }
            blk = blk->u.partition;
        }
        if( next_bit == EMPTY ) break;
        if( change == FALSE ) break;
    }

    /* rip off any blocks in the partition without bits*/
    /* and propagate the bits through the instructions*/

    owner = &root->u.partition;
    for( ;; ) {
        blk = *owner;
        ins = blk->ins.hd.next;
        while( ins->head.opcode != OP_BLOCK ) {
            _INSBITS( ins ) = _BLKBITS( blk );
            ins = ins->head.next;
        }
        if( _BLKBITS( blk ) == EMPTY ) {
            *owner = blk->u.partition;
        } else {
            owner = &blk->u.partition;
        }
        if( blk == root ) break;
    }
}


extern  void    SetCSEBits( instruction *ins, instruction *new_ins )
/*******************************************************************
    set the ancestor bits of "new_ins" to be the same as "ins".
*/
{
    a_bit_set   bits;

    if( ins->head.opcode == OP_BLOCK ) {
        bits = _BLKBITS( _BLOCK( ins ) );
    } else {
        bits = _INSBITS( ins );
    }
    _INSBITS( new_ins ) = bits;
}


static  instruction *WhichIsAncestor( instruction *ins1, instruction *ins2 )
/***************************************************************************
    Return which of instructions "ins1" or "ins2" is always executed
    first, or a pointer the last instruction in a block which is a
    common ancestor to "ins1" and "ins2".
*/
{
    instruction *first;
    instruction *ins;
    a_bit_set   bits1;
    a_bit_set   bits2;
    block       *blk;

    bits1 = _INSBITS( ins1 );
    bits2 = _INSBITS( ins2 );
    if( bits1 == bits2 ) {  /* same_block*/
        first = NULL;
        if( ins1 != ins2 ) {
            ins = ins1;
            for( ;; ) {
                ins = ins->head.next;
                if( ins == ins2 ) {
                    first = ins1;
                } else if( ins->head.opcode == OP_BLOCK ) {
                    first = ins2;
                }
                if( first != NULL ) break;
            }
        }
    } else if( ( bits1 & bits2 ) == bits1 ) {
        first = ins1;
    } else if( ( bits1 & bits2 ) == bits2 ) {
        first = ins2;
    } else { /* find a hoist point*/
        bits1 &= bits2;
        ins = ins1;
        while( ins->head.opcode != OP_BLOCK ) {
            ins = ins->head.next;
        }
        blk = _BLOCK( ins );
        while( _BLKBITS( blk ) != bits1 ) {
            blk = blk->u.partition;
        }
        first = blk->ins.hd.prev;
        while( first->head.opcode == OP_NOP ) {
            if( first->flags.nop_flags & NOP_ZAP_INFO ) break;
            first = first->head.prev;
        }
        for( ;; ) { /* scan back over all the conditional branches at the end of block*/
            if( ( first->head.opcode != OP_SELECT )
                && !_OpIsCondition( first->head.opcode ) ) break;
            first = first->head.prev;
        }
    }
    return( first );
}


static  void    CleanPartition( void )
/*************************************
    Turn of all the bits we turned on in FindPartition.
*/
{
    block       *blk;

    blk = HeadBlock;
    while( blk != NULL ) {
        blk->class &= ~PARTITION_ROOT;
        blk->u.partition = NULL;
        blk = blk->next_block;
    }
}


static  bool    CanCrossBlocks( instruction *ins1,
                                instruction *ins2, name *op )
/************************************************************
    If we're generating a partial routine, we cannot cause an
    existing N_TEMP to cross blocks, due to an assumption
    in ForceTempsMemory.
*/
{
    if( !BlockByBlock ) return( TRUE );
    if( op->n.class != N_TEMP ) return( TRUE );
    if( _INSBITS( ins1 ) == _INSBITS( ins2 ) ) return( TRUE );
    if( op->t.temp_flags & CROSSES_BLOCKS ) return( TRUE );
    return( FALSE );
}


static  void    UseInOther( instruction *ins1, instruction *ins2, name *op )
/***************************************************************************
    Indicate that we have caused operand "op" to be used in both "ins1"
    and "ins2" where it was previously only used in one of those
    instructions.  This may change it's USE_IN_ANOTHER_BLOCK status.
*/
{
    if( _INSBITS( ins1 ) != _INSBITS( ins2 ) ) {
        if( op->n.class == N_MEMORY ) {
            op->v.usage |= USE_IN_ANOTHER_BLOCK;
        } else if ( op->n.class == N_TEMP ) {
            op->t.temp_flags |= CROSSES_BLOCKS;
        }
    }
}


static  bool    UnOpsLiveFrom( instruction *first, instruction *last )
/*********************************************************************
    Do the operand and result of "first" live until instruction "last"?
*/
{
    instruction *ins;

    ins = last->head.prev;
    for( ;; ) {
        while( ins->head.opcode == OP_BLOCK ) {
            ins = _BLOCK( ins )->input_edges->source->ins.hd.prev;
        }
        if( ins == first ) break;
        if( ReDefinedBy( ins, first->operands[ 0 ] ) ) return( FALSE );
        if( ReDefinedBy( ins, first->result ) ) return( FALSE );
        ins = ins->head.prev;
    }
    return( TRUE );
}


static  who_dies BinOpsLiveFrom( instruction *first,
                                instruction *last,
                                name *op1, name *op2, name *result )
/*******************************************************************
    Determine which of "op1", "op2", and "result" live from instruction
    "first" to instruction "last".
*/
{
    instruction *ins;
    bool        result_dies;

    ins = last->head.prev;
    if( result == NULL ) {
        for( ;; ) {
            while( ins->head.opcode == OP_BLOCK ) {
                ins = _BLOCK( ins )->input_edges->source->ins.hd.prev;
            }
            if( ins == first ) break;
            if( ReDefinedBy( ins, op1 ) ) return( OP_DIES );
            if( ReDefinedBy( ins, op2 ) ) return( OP_DIES );
            ins = ins->head.prev;
        }
        return( ALL_LIVE );
    } else {
        result_dies = FALSE;
        for( ;; ) {
            while( ins->head.opcode == OP_BLOCK ) { /* 89-09-05 */
                ins = _BLOCK( ins )->input_edges->source->ins.hd.prev;
            }
            if( ins == first ) break;
            if( ReDefinedBy( ins, op1 ) ) return( OP_DIES );
            if( ReDefinedBy( ins, op2 ) ) return( OP_DIES );
            if( ReDefinedBy( ins, result ) ) {
                result_dies = TRUE;
            }
            ins = ins->head.prev;
        }
        if( result_dies ) return( RESULT_DIES );
        return( ALL_LIVE );
    }
}

static  bool            HoistLooksGood( instruction *target, instruction *orig )
/*******************************************************************************
    Does it look like a good idea to hoist a busy expression from the original
    instruction to the hoist point. Currently, this is not a good idea if we
    are not overly concerned about code size and we would be hoisting the expr
    out of a switch statement.
*/
{
    instruction         *ins;
    block               *target_blk;
    block               *blk;

    if( OptForSize > 50 ) return( TRUE );
    for( ins = target; ins->head.opcode != OP_BLOCK; ins = ins->head.next );
    target_blk = _BLOCK( ins );
    for( ins = orig; ins->head.opcode != OP_BLOCK; ins = ins->head.next );
    blk = _BLOCK( ins );
    if( blk == target_blk ) return( TRUE );
    while( blk != NULL ) {
        if( blk->inputs != 1 ) _Zoiks( ZOIKS_103 ); // because we're in a partition
        blk = blk->input_edges->source;
        if( ( blk->class & SELECT ) != EMPTY ) return( FALSE );
        if( blk == target_blk ) return( TRUE );
    }
    _Zoiks( ZOIKS_104 );
    return( TRUE );
}


#define i1( ins )      _OpIsBinary( ins->head.opcode ) /* unary=0, binary=1 */


static  instruction     *ProcessExpr( instruction *ins1,
                                      instruction *ins2, bool signed_matters )
/*****************************************************************************
    Given two instruction "ins1" and "ins2" in the same partition, first
    find out if they have the same operands, and if they do, determine
    "WhichIsAncestor", which gives us the common ancestor of "ins1" and
    "ins2".  If this is one of ins1 or ins2, we have a common
    subexpressions as long as the operands of the ancestor aren't
    redefined between the two (BinOpsLiveFrom).  If we have a hoist
    point, (different common ancestor) we have a very busy expression as
    long as the operands of ins1 and ins2 aren't redefined between the
    hoist point and either of ins1 and ins2 (BinOpsLiveFrom).
*/
{
    instruction         *first;
    instruction         *new_ins;
    instruction         *dead_or_new_ins;
    name                *temp;
    type_class_def      class;
    who_dies            killed;
    int                 i;

    dead_or_new_ins = NULL;
    i = i1( ins1 );
    if( ins1->head.opcode == OP_CONVERT ) {
        if( ins1->base_type_class != ins2->base_type_class ) {
            return( FALSE );
        }
    }
    if( signed_matters ) {
        if( ins1->type_class != ins2->type_class ) {
            return( NULL );
        }
    } else {
        if( Unsigned[ ins1->type_class ] != Unsigned[ ins2->type_class ] ) {
            return( NULL );
        }
    }
    if( ins1->operands[ 0 ] != ins2->operands[ 0 ] ||
        ins1->operands[ i ] != ins2->operands[ i ] ) {
          if( !_OpCommutes( ins1->head.opcode ) ) return( NULL );
          if( ins1->operands[ 0 ] != ins2->operands[ i ] ||
              ins1->operands[ i ] != ins2->operands[ 0 ] ) return( NULL );
    }
    first = WhichIsAncestor( ins1, ins2 );
    if( first == ins2 ) {
        ins2 = ins1;
        ins1 = first;
    }
    if( first == ins1 ) { /* or used to be ins2*/
        if( ( ins1->ins_flags & INS_DEFINES_OWN_OPERAND ) == EMPTY ) {
            killed = BinOpsLiveFrom( ins1, ins2, ins1->operands[ 0 ],
                                      ins1->operands[ i ], ins1->result );
            if( killed != OP_DIES ) {
                class = ins1->result->n.name_class;
                if( killed == RESULT_DIES
                 || !CanCrossBlocks( ins1, ins2, ins1->result ) ) {
                    temp = AllocTemp( class );
                    new_ins = MakeMove( temp, ins1->result, class );
                    ins1->result = temp;
                    SuffixIns( ins1, new_ins );
                    SetCSEBits( ins1, new_ins );
                    FPNotStack( temp );
                }
                new_ins = MakeMove( ins1->result, ins2->result, class );
                UseInOther( ins1, ins2, ins1->result );
                dead_or_new_ins = ins2;
                SetCSEBits( ins2, new_ins );
                SuffixIns( ins2, new_ins );
                FPNotStack( ins1->result );
            }
        }
    } else if( first != NULL ) { /* we calculated a hoist point*/
        if( ins1->operands[ 0 ]->n.class != N_INDEXED
         && ins1->operands[ i ]->n.class != N_INDEXED
         && Hoistable( ins1, NULL )
         && BinOpsLiveFrom( first->head.next, ins1, ins1->operands[ 0 ],
                            ins1->operands[ i ], NULL ) == ALL_LIVE
         && BinOpsLiveFrom( first->head.next, ins2, ins2->operands[ 0 ],
                            ins2->operands[ i ], NULL ) == ALL_LIVE
         && HoistLooksGood( first, ins1 )
         && HoistLooksGood( first, ins2 ) ) {
            class = ins1->type_class;
            temp = AllocTemp( class );
            temp->t.temp_flags |= CROSSES_BLOCKS;
            if( _OpIsBinary( ins1->head.opcode ) ) {
                new_ins = MakeBinary( ins1->head.opcode,
                                       ins1->operands[ 0 ],
                                       ins1->operands[ 1 ],
                                       temp, class );
            } else {
                new_ins = MakeUnary( ins1->head.opcode,
                                       ins1->operands[ 0 ],
                                       temp, class );
            }
            new_ins->base_type_class = ins1->base_type_class;
            dead_or_new_ins = new_ins;
            SetCSEBits( first, new_ins );
            SuffixIns( first, new_ins );
            new_ins = MakeMove( temp, ins1->result, class );
            SetCSEBits( ins1, new_ins );
            SuffixIns( ins1, new_ins );
            new_ins = MakeMove( temp, ins2->result, class );
            SetCSEBits( ins2, new_ins );
            SuffixIns( ins2, new_ins );
        }
    }
    return( dead_or_new_ins );
}


static  bool    OkToInvert( name *div )
/**************************************
   Is it OK to compute the reciprical of 'div'? Either it must
   be exactly representable (a integer power of two) or the user
   must have said that it was OK.
*/
{
    if( _IsModel( FP_UNSTABLE_OPTIMIZATION ) ) return( TRUE );
    if( (div->n.class == N_TEMP) && (div->t.temp_flags & CONST_TEMP) ) {
        div = div->v.symbol;
    }
    if( div->n.class != N_CONSTANT ) return( FALSE );
    if( div->c.const_type != CONS_ABSOLUTE ) return( FALSE );
    if( !CFIs32( div->c.value ) ) return( FALSE );
    if( GetLog2( div->c.int_value ) == -1 ) return( FALSE );
    return( TRUE );
}


static  bool    ProcessDivide( instruction *ins1, instruction *ins2 )
/********************************************************************
    Given two instruction "ins1" and "ins2" in the same partition, first
    find out if they have the same divisor, and if they do, determine
    "WhichIsAncestor", which gives us the common ancestor of "ins1" and
    "ins2".  If this is one of ins1 or ins2, we have a common
    subexpressions as long as the divisor of the ancestor aren't
    redefined between the two (BinOpsLiveFrom).
*/
{
    instruction         *first;
    instruction         *new_ins;
    name                *temp;
    who_dies            killed;
    name                *divisor;

    if( ins1->type_class != ins2->type_class ) return( FALSE );
    if( !DivIsADog( ins1->type_class ) ) return( FALSE );
    if( ins1->operands[ 1 ] != ins2->operands[ 1 ] ) return( FALSE );
    first = WhichIsAncestor( ins1, ins2 );
    if( first == ins2 ) {
        ins2 = ins1;
        ins1 = first;
    }
    if( first == ins1 ) {
        divisor = ins1->operands[ 1 ];
        if( !OkToInvert( divisor ) ) return( FALSE );
        if( !ReDefinedBy( ins1, divisor ) ) {
            killed = BinOpsLiveFrom( ins1, ins2, divisor, divisor, NULL );

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?