/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  WHEN YOU FIGURE OUT WHAT THIS FILE DOES, PLEASE
*               DESCRIBE IT HERE!
*
****************************************************************************/


#include "standard.h"
#include "coderep.h"
#include "opcodes.h"
#include "sysmacro.h"
#include "pattern.h"
#include "vergen.h"
#include "procdef.h"
#include "score.h"
#include "zerobits.h"

extern  name            *LowPart(name*,type_class_def);
extern  name            *HighPart(name*,type_class_def);
extern  opcode_entry    *FindGenEntry(instruction*,bool*);
extern  instruction     *MakeUnary(opcode_defs,name*,name*,type_class_def);
extern  void            SuffixIns(instruction*,instruction*);
extern  void            FreeIns(instruction*);
extern  instruction     *MakeBinary(opcode_defs,name*,name*,name*,type_class_def);
extern  instruction     *MakeMove(name*,name*,type_class_def);
extern  name            *AllocIntConst(int);
extern  byte            HasZero(score*,name*);
extern  bool            VolatileIns(instruction*);

extern  type_class_def  HalfClass[];



static  bool    CheckIns( instruction **pins ) {
/**********************************************/

    bool                ok;
    opcode_entry        *try;
    instruction         *ins;
    bool                dummy;
    int                 i;

    ins = *pins;
    ok = TRUE;
    if( ins != NULL ) {
        i = ins->num_operands;
        while( --i >= 0 ) {
            if( ins->operands[i]->n.class == N_REGISTER &&
                ins->operands[i]->r.reg_index == -1 ) {
                ok = FALSE;
            }
        }
        if( ok && ins->result != NULL ) {
            if( ins->result->n.class == N_REGISTER &&
                ins->result->r.reg_index == -1 ) {
                ok = FALSE;
            }
        }
        if( ok ) {
            try = FindGenEntry( ins, &dummy );
            if( try == NULL ) {
                ok = FALSE;
            } else if( try->generate >= G_UNKNOWN ) {
                ok = FALSE;
            } else {
                ins->table = try;
                ins->u.gen_table = try;
            }
        }
    }
    if( !ok ) {
        ins->head.next = ins;
        ins->head.prev = ins;
        FreeIns( ins );
        *pins = NULL;
    }
    return( ok );
}


static  name    *NonZeroPart( name *op, zero_bits z, type_class_def class ) {
/******************************************************************/

    if( z & LO_HALF ) {
        return( HighPart( op, HalfClass[  class  ] ) );
    } else if( z & HI_HALF ) {
        return( LowPart( op, HalfClass[  class  ] ) );
    } else {
        return( op );
    }
}


static  zero_bits       AZeroHalf( zero_bits l, zero_bits r ) {
/*************************************************************/

    zero_bits   zhalf;

    zhalf = l & LO_HALF;
    if( zhalf ) return( zhalf );
    zhalf = l & HI_HALF;
    if( zhalf ) return( zhalf );
    zhalf = r & LO_HALF;
    if( zhalf ) return( zhalf );
    zhalf = r & HI_HALF;
    return( zhalf );
}

static  instruction     *MakeClear( name *res, type_class_def class ) {
/*********************************************************************/

    if( res->n.class == N_REGISTER ) { /* since there's a R_MAKEXORRR*/
        return( MakeBinary( OP_XOR, res, res, res, class ) );
    } else {
        return( MakeMove( AllocIntConst( 0 ), res, class ) );
    }
}


extern  bool    ScoreZero( pointer sc, instruction **pins ) {
/***********************************************************/

    instruction         *ins;
    bool                change;
    type_class_def      class;
    type_class_def      hc;
    zero_bits           op1zpart;
    zero_bits           op2zpart;
    zero_bits           zeropart;
    zero_bits           nonzeropart;
    name                *op1;
    name                *op2;
    name                *res;
    instruction         *ins1;
    instruction         *ins2;
    name                *tmp1;
    name                *tmp2;
    name                *tmp3;
    name                *tmp4;


#define NonZeroOP2Part( x )  NonZeroPart( op2, x, hc )
#define NonZeroOP1Part( x )  NonZeroPart( op1, x, hc )
#define NonZeroRESPart( x )  NonZeroPart( res, x, hc )

    ins = *pins;
    if( ins->num_operands != 2 ) return( FALSE );
    if( ins->result == NULL ) return( FALSE );
    if( ( ins->head.opcode != OP_ADD && ins->head.opcode != OP_AND
       && ins->head.opcode != OP_OR && ins->head.opcode != OP_XOR
       && ins->head.opcode != OP_SUB ) ) return( FALSE );
    if( ins->ins_flags & INS_CC_USED ) return( FALSE );
    if( VolatileIns( ins ) ) return( FALSE );
    op1 = ins->operands[ 0 ];
    op2 = ins->operands[ 1 ];
    res = ins->result;
    if( op1->n.size != op2->n.size ) return( FALSE );
    if( res->n.size != op2->n.size ) return( FALSE );
    class = ins->type_class;
    op1zpart = HasZero( sc, op1 );
    op2zpart = HasZero( sc, op2 );
    hc = HalfClass[  class      ];
    ins1 = NULL;
    ins2 = NULL;
    change = FALSE;
    switch( ins->head.opcode ) {
    case OP_ADD:
        if( _IsZero( op1zpart ) && _IsZero( op2zpart ) ) {
            ins1 = MakeClear( res, class );
        } else if( _IsZero( op1zpart ) ) {
            ins1 = MakeMove( op2, res, class );
        } else if( _IsZero( op2zpart ) ) {
            ins1 = MakeMove( op1, res, class );
        } else if( _OpposZero( op1zpart, op2zpart ) ) {
            tmp1 = NonZeroOP1Part( op1zpart );
            tmp2 = NonZeroRESPart( op1zpart );
            tmp3 = NonZeroOP2Part( op2zpart );
            tmp4 = NonZeroRESPart( op2zpart );
            if( tmp1==0 || tmp2==0 || tmp3==0 || tmp4==0 ) return( FALSE );
            ins1 = MakeMove( tmp1, tmp2, hc );
            ins2 = MakeMove( tmp3, tmp4, hc );
        } else {
            zeropart = _ZeroHalf( op1zpart, op2zpart );
            if( _LoZero( zeropart ) ) {
                nonzeropart = _OtherHalf( zeropart );
                tmp1 = NonZeroRESPart( nonzeropart );
                tmp2 = NonZeroOP1Part( zeropart );
                tmp3 = NonZeroOP2Part( zeropart );
                tmp4 = NonZeroRESPart( zeropart );
                if( tmp1==0 || tmp2==0 || tmp3==0 || tmp4==0 ) return( FALSE );
                ins2 = MakeClear( tmp1, hc );
                ins1 = MakeBinary( OP_ADD, tmp2, tmp3, tmp4, hc );
            }
        }
        break;
    case OP_SUB:
        if( _IsZero( op1zpart ) && _IsZero( op2zpart ) ) {
            ins1 = MakeClear( res, class );
        } else if( _IsZero( op2zpart ) ) {
            ins1 = MakeMove( op1, res, class );
        } else if( _IsZero( op1zpart ) ) {
            ins1 = MakeUnary( OP_NEGATE, op2, res, class );
        }
        break;
    case OP_AND:
        if( _IsZero( op1zpart ) || _IsZero( op2zpart ) || _OpposZero( op1zpart,op2zpart ) ) {
            ins1 = MakeClear( res, class );
        } else {
            zeropart = AZeroHalf( op1zpart, op2zpart );
            if( zeropart ) {
                nonzeropart = _OtherHalf( zeropart );
                tmp1 = NonZeroRESPart( nonzeropart );
                tmp2 = NonZeroOP1Part( zeropart );
                tmp3 = NonZeroOP2Part( zeropart );
                tmp4 = NonZeroRESPart( zeropart );
                if( tmp1==0 || tmp2==0 || tmp3==0 || tmp4==0 ) return( FALSE );
                ins1 = MakeClear( tmp1, hc );
                ins2 = MakeBinary( OP_AND, tmp2, tmp3, tmp4, hc );
            }
        }
        break;
    case OP_OR:
    case OP_XOR:
        if( _IsZero( op1zpart ) && _IsZero( op2zpart ) ) {
            if( op1 == res || op2 == res ) { /* result already has zero*/
                *pins = ins->head.next;
                FreeIns( ins );
                change = TRUE;
            } else {
                ins1 = MakeClear( res, class );
            }
        } else if( _IsZero( op1zpart ) ) {
            ins1 = MakeMove( op2, res, class );
        } else if( _IsZero( op2zpart ) ) {
            ins1 = MakeMove( op1, res, class );
        } else if( _OpposZero( op1zpart, op2zpart ) ) {
            tmp1 = NonZeroOP1Part( op1zpart );
            tmp2 = NonZeroRESPart( op1zpart );
            tmp3 = NonZeroOP2Part( op2zpart );
            tmp4 = NonZeroRESPart( op2zpart );
            if( tmp1==0 || tmp2==0 || tmp3==0 || tmp4==0 ) return( FALSE );
            ins1 = MakeMove( tmp1, tmp2, hc );
            ins2 = MakeMove( tmp3, tmp4, hc );
        } else if( _MatchZero( op1zpart, op2zpart ) ) {
            zeropart = _ZeroHalf( op1zpart, op2zpart );
            nonzeropart = _OtherHalf( zeropart );
            tmp1 = NonZeroRESPart( nonzeropart );
            tmp2 = NonZeroOP1Part( zeropart );
            tmp3 = NonZeroOP2Part( zeropart );
            tmp4 = NonZeroRESPart( zeropart );
            if( tmp1==0 || tmp2==0 || tmp3==0 || tmp4==0 ) return( FALSE );
            ins2 = MakeClear( tmp1, hc );
            ins1 = MakeBinary( ins->head.opcode, tmp2, tmp3, tmp4, hc );
        }
        break;
    }
    if( CheckIns( &ins1 ) == FALSE && ins2 != NULL ) {
        ins2->head.next = ins2;
        ins2->head.prev = ins2;
        FreeIns( ins2 );
        ins2 = NULL;
    }
    if( CheckIns( &ins2 ) == FALSE && ins1 != NULL ) {
        ins1->head.next = ins1;
        ins1->head.prev = ins1;
        FreeIns( ins1 );
        ins1 = NULL;
    }
    if( ins1 == NULL && ins2 == NULL ) return( change );
    if( ins2 != NULL ) {
        SuffixIns( ins, ins2 );
    }
    if( ins1 != NULL ) {
        SuffixIns( ins, ins1 );
    }
    FreeIns( ins );
    *pins = ins1;
    return( TRUE );
}
