Subversion Repositories tendra.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.org/>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of The TenDRA Project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $Id$
 */
/*
                 Crown Copyright (c) 1996

    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-

        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;

        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;

        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;

        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/
/*
                            VERSION INFORMATION
                            ===================

--------------------------------------------------------------------------
$Header: /u/g/release/CVSROOT/Source/src/installers/680x0/common/mach_op.c,v 1.1.1.1 1998/01/17 15:55:49 release Exp $
--------------------------------------------------------------------------
$Log: mach_op.c,v $
 * Revision 1.1.1.1  1998/01/17  15:55:49  release
 * First version to be checked into rolling release.
 *
Revision 1.2  1997/10/29 10:22:21  ma
Replaced use_alloca with has_alloca.

Revision 1.1.1.1  1997/10/13 12:42:55  ma
First version.

Revision 1.3  1997/09/25 06:45:15  ma
All general_proc tests passed

Revision 1.2  1997/06/18 10:09:37  ma
Checking in before merging with Input Baseline changes.

Revision 1.1.1.1  1997/03/14 07:50:15  ma
Imported from DRA

 * Revision 1.2  1996/09/20  13:51:39  john
 * *** empty log message ***
 *
 * Revision 1.1.1.1  1996/09/20  10:56:55  john
 *
 * Revision 1.1.1.1  1996/03/26  15:45:14  john
 *
 * Revision 1.1  93/02/22  17:16:07  17:16:07  ra (Robert Andrews)
 * Initial revision
 *
--------------------------------------------------------------------------
*/


#include "config.h"
#include "common_types.h"
#include "instrs.h"
#include "mach.h"
#include "mach_ins.h"
#include "mach_op.h"
#include "codex.h"
#include "output.h"
#include "utility.h"

#ifndef tdf3
#define  par2_pl        4       /* procedure argument accessed by use of A5 */
#define  par3_pl        5       /* procedure argument accessed by use of SP */
#endif


/*
    LIST OF FREE OPERANDS

    A list of free mach_op's, linked by the plus field.
*/

static mach_op *mach_op_list = null;


/*
    ALLOCATE A NEW OPERAND

    This routine returns a pointer to a mach_op.  This is taken from the
    list of free mach_op's.
*/
#ifndef tdf3
#ifdef EBUG
static int next_id = 0;
#endif
#endif

mach_op *
new_mach_op(void)
{
        mach_op *p;
        if (mach_op_list == null) {
                int i, n = 1000;
                p = alloc_nof(mach_op, n);
                for (i = 0; i < n - 1; i++) {
                        (p + i)->plus = p + (i + 1);
                        (p + i)->of = null;
                }
                (p + (n - 1))->plus = null;
                (p + (n - 1))->of = null;
                mach_op_list = p;
        }
        p = mach_op_list;
        if (p->of) {
                mach_op *q = p->of;
                mach_op_list = q;
                while (q->plus) {
                        q = q->plus;
                }
                q->plus = p->plus;
        } else {
                mach_op_list = p->plus;
        }
        p->def.num = 0;
        p->plus = null;
        p->of = null;
#ifndef tdf3
#ifdef EBUG
        if (next_id == 70) {
                int dummy = next_id;
        }

        p->id = next_id++;
#endif
#endif
        return (p);
}


/*
    FREE AN OPERAND

    A mach_op is freed by adding it to the list of free mach_op's.
*/

void
free_mach_op(mach_op *ptr)
{
        mach_op *p = ptr;
        if (p == null) {
                return;
        }
        while (p->plus) {
                p = p->plus;
        }
        p->plus = mach_op_list;
        mach_op_list = ptr;
        return;
}


/*
    SPECIAL LABELS INFORMATION

    A special label consists of the label prefix, "L", followed by the
    special label identifier, followed by the value of special_no for
    the current procedure.  A particular special label is that with
    identifier special_str.
*/

long special_no = 0;
char *special_str = "S";


/*
    TEMPORARY REGISTER STATUS

    This records the number of temporary registers which have been allocated
    at any given moment, any temporary register preferences and the last
    temporary register used.
*/

int tmp_reg_status = 0;
int tmp_reg_prefer = 0;
static int last_reg = 0;


/*
    FIND THE NUMBER OF THE NEXT TEMPORARY REGISTER

    This is a look-ahead routine to find what the next temporary register
    allocated will be.  Let X denote the prefered temporary register
    (if specified) and Y denote any A-register used in the procedure
    but not currently active.

    If X is specified, it will always be the first temporary register
    returned.  The second will be Y, if that exists, or A1, unless
    this equals X, if which case A0 is used.

    If X is not specified, the first temporary register will be Y,
    if that exists, or A1.  The second will be A1 if Y exists, or
    A0 otherwise.

    Under very rare conditions a third temporary register is required.
    In these cases D0 always suffices.
*/

int
next_tmp_reg(void)
{
        int r;
        int t = tmp_reg_status;
        if (t > 1) {
                debug_warning("Temporary D-register used");
                r = REG_D0;
        } else if (tmp_reg_prefer) {
                if (t == 0) {
                        r = tmp_reg_prefer;
                        last_reg = r;
                } else {
                        bitpattern na = (regsinuse | reuseables | regsindec);
                        bitpattern a = regsinproc & ~na & 0x3c00;
                        r = (a ? reg(a): REG_A1);
                        if (r == last_reg) {
                                r = REG_A0;
                        }
                }
        } else {
                bitpattern na = (regsinuse | reuseables | regsindec);
                bitpattern a = regsinproc & ~na & 0x3c00;
                if (t == 0) {
                        r = (a ? reg(a): REG_A1);
                        last_reg = r;
                } else {
                        r = (a ? REG_A1 : REG_A0);
                        if (r == last_reg) {
                                r = (r == REG_A0 ? REG_A1 : REG_A0);
                        }
                }
        }
        return (r);
}


/*
    AVOID A GIVEN TEMPORARY REGISTER

    This marks the given register number as to be avoided by pretending
    that it was the previous temporary register.
*/

void
avoid_tmp_reg(int r)
{
        last_reg = r;
        tmp_reg_status++;
        return;
}


/*
    MOVE AN OPERAND INTO A TEMPORARY REGISTER

    It is sometimes necessary to move an operand into a temporary address
    register.  A move instruction (given by instr) is output, and the
    number of the temporary register is returned.
*/

int
tmp_reg(int instr, mach_op *ptr)
{
        int t = tmp_reg_status;
        int r = next_tmp_reg();
        mach_op *p = new_mach_op();
        p->type = MACH_REG;
        p->def.num = (long)r;
        make_instr_aux(instr, ptr, p, regmsk(r), 1);
        regsinproc |= regmsk(r);
        tmp_reg_status = t + 1;
        return (r);
}


/*
    TEST IF A REGISTER IS USED IN AN OPERAND

    This routine returns 1 if register r is used in the operand op.
*/

bool
check_op(mach_op *op, int r)
{
        if (op == null) {
                return (0);
        }
        switch (op->type) {
        case MACH_CONT:
                return ((op->def.num) & regmsk(r)? 1 : 0);
        case MACH_REG:
        case MACH_DEC:
        case MACH_INC:
                return (op->def.num == r ? 1 : 0);
        case MACH_BF:
                return (check_op(op->of, r));
        case MACH_RPAIR:
                if (op->def.num == r) {
                        return (1);
                }
                return (op->plus->def.num == r ? 1 : 0);
        }
        return (0);
}


/*
    TEST IF TWO OPERANDS ARE EQUAL

    This returns 1 if the two operands have equal effect.  Note that,
    for example, consecutive uses of the same pre-decremented register,
    although having the same representation, are not equal in this
    context.
*/

bool
equal_op(mach_op *op1, mach_op *op2)
{
        mach_op *p1 = op1, *p2 = op2;
        while (p1 && p2) {
                if (p1->type != p2->type) {
                        return (0);
                }
                if (p1->type == MACH_DEC || p1->type == MACH_INC) {
                        return (0);
                }
                if (p1->def.num != p2->def.num) {
                        return (0);
                }
                if (p1->plus) {
                        if (p2->plus == null) {
                                return (0);
                        }
                        if (!equal_op(p1->plus, p2->plus)) {
                                return (0);
                        }
                } else {
                        if (p2->plus) {
                                return (0);
                        }
                }
                p1 = p1->of;
                p2 = p2->of;
        }
        return (p1 == p2 ? 1 : 0);
}


/*
    MAKE AN INTEGER CONSTANT OPERAND

    This and the subsequent routines are used to allocate machine operands.
    The constructions are simple applications of the descriptions given
    in mach.h.  They need very little other comment.
*/

mach_op *
make_value(long n)
{
        mach_op *p = new_mach_op();
        p->type = MACH_VAL;
        p->def.num = n;
        return (p);
}


/*
    MAKE AN INTEGER DATA OPERAND
*/

mach_op *
make_int_data(long n)
{
        mach_op *p = new_mach_op();
        p->type = MACH_VALQ;
        p->def.num = n;
        return (p);
}


/*
    MAKE A HEXADECIMAL INTEGER CONSTANT OPERAND
*/

mach_op *
make_hex_value(long n)
{
        mach_op *p = new_mach_op();
        p->type = MACH_HEX;
        p->def.num = n;
        return (p);
}


/*
    MAKE A HEXADECIMAL INTEGER CONSTANT DATA OPERAND
*/

mach_op *
make_hex_data(long n)
{
        mach_op *p = new_mach_op();
        p->type = MACH_HEXQ;
        p->def.num = n;
        return (p);
}


/*
    MAKE A FLOATING POINT DATA OPERAND
*/

mach_op *
make_float_data(flt *f)
{
        mach_op *p = new_mach_op();
        p->type = MACH_FLOATQ;
        p->def.fp = f;
        return (p);
}


/*
    MAKE A LABEL OPERAND
*/

mach_op *
make_lab(long n, long d)
{
        mach_op *p1 = new_mach_op();
        p1->type = MACH_LAB;
        p1->def.num = n;
        if (d) {
                mach_op *p2 = new_mach_op();
                p2->type = MACH_VAL;
                p2->def.num = d;
                p1->plus = p2;
        }
        return (p1);
}


/*
    MAKE A LABEL DATA OPERAND
*/

mach_op *
make_lab_data(long n, long d)
{
        mach_op *p1 = new_mach_op();
        p1->type = MACH_LABQ;
        p1->def.num = n;
        if (d) {
                mach_op *p2 = new_mach_op();
                p2->type = MACH_VAL;
                p2->def.num = d;
                p1->plus = p2;
        }
        return (p1);
}


/*
    MAKE AN OPERAND CORRESPONDING TO THE DIFFERENCE OF TWO LABELS
*/

mach_op *
make_lab_diff(long a, long b)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        mach_op *p3 = new_mach_op();
        p1->type = MACH_LABQ;
        p1->def.num = a;
        p1->plus = p2;
        p2->type = MACH_NEG;
        p2->plus = p3;
        p3->type = MACH_LABQ;
        p3->def.num = b;
        return (p1);
}


/*
    MAKE AN EXTERNAL OPERAND
*/

mach_op *
make_extern(char *nm, long d)
{
        mach_op *p1 = new_mach_op();
        p1->type = MACH_EXT;
        p1->def.str = nm;
        if (d) {
                mach_op *p2 = new_mach_op();
                p2->type = MACH_VAL;
                p2->def.num = d;
                p1->plus = p2;
        }
        return (p1);
}


/*
    MAKE AN EXTERNAL DATA OPERAND
*/

mach_op *
make_extern_data(char *nm, long d)
{
        mach_op *p1 = new_mach_op();
        p1->type = MACH_EXTQ;
        p1->def.str = nm;
        if (d) {
                mach_op *p2 = new_mach_op();
                p2->type = MACH_VAL;
                p2->def.num = d;
                p1->plus = p2;
        }
        return (p1);
}


/*
    MAKE A SPECIAL LABEL OPERAND
*/

mach_op *
make_special(char *nm)
{
        mach_op *p = new_mach_op();
        p->type = MACH_SPEC;
        p->def.str = nm;
        return (p);
}


/*
    MAKE A SPECIAL LABEL DATA OPERAND
*/

mach_op *
make_special_data(char *nm)
{
        mach_op *p = new_mach_op();
        p->type = MACH_SPECQ;
        p->def.str = nm;
        return (p);
}


/*
    MAKE A LABEL INDIRECT OPERAND
*/

mach_op *
make_lab_ind(long n, long d)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        p1->type = MACH_CONT;
        p1->def.num = 0;
        p1->of = p2;
        p2->type = MACH_LAB;
        p2->def.num = n;
        if (d) {
                mach_op *p3 = new_mach_op();
                p3->type = MACH_VAL;
                p3->def.num = d;
                p2->plus = p3;
        }
        return (p1);
}


/*
    MAKE AN EXTERNAL INDIRECT OPERAND
*/

mach_op *
make_extern_ind(char *nm, long d)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        p1->type = MACH_CONT;
        p1->def.num = 0;
        p1->of = p2;
        p2->type = MACH_EXT;
        p2->def.str = nm;
        if (d) {
                mach_op *p3 = new_mach_op();
                p3->type = MACH_VAL;
                p3->def.num = d;
                p2->plus = p3;
        }
        return (p1);
}


/*
    MAKE A REGISTER DIRECT OPERAND
*/

mach_op *
make_register(int r)
{
        mach_op *p = new_mach_op();
        p->type = MACH_REG;
        p->def.num = (long)r;
        return (p);
}

/*
    MAKE PSEUDO OPERAND REPRESENTING LDISP
    (the space between sp and the parameters at procedure entry)
*/

mach_op *
make_ldisp(long offset)
{
        mach_op *p1 = new_mach_op();
        p1->type = MACH_SPEC;
        p1->def.str = special_str;
        if (offset) {
                p1->plus = new_mach_op();
                p1->plus->type = MACH_VAL;
                p1->plus->def.num = offset;
        }
        return (p1);
}


/*
    MAKE A REGISTER INDIRECT WITH DISPLACEMENT OPERAND

    This is the first example where a temporary register may be required.
    Under very rare circumstances, we may be trying to address relative
    to a D-register, if which case we need to use a temporary A-register
    instead.
*/

mach_op *
make_indirect(int r, long d)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        p1->type = MACH_CONT;
        p1->def.num = regmsk(r);
        p2->type = MACH_REG;
        p2->def.num = (long)r;
        if (is_dreg(r)) {
                int t = tmp_reg(m_movl, p2);
                p2 = new_mach_op();
                p2->type = MACH_REG;
                p2->def.num = (long)t;
                p1->def.num = regmsk(t);
        }
        p1->of = p2;
        if (d) {
                mach_op *p3 = new_mach_op();
                p3->type = MACH_VAL;
                p3->def.num = d;
                p2->plus = p3;
        }
        return (p1);
}


/*
    MAKE A APPLICATION POINTER INDIRECT WITH DISPLACEMENT OPERAND

    Since we don't want to use an applications pointer unless absolutely
    necessary, this is often changed into a stack pointer indirect
    with displacement operand.
*/

mach_op *
make_rel_ap(long d)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        p1->type = MACH_CONT;
        p1->def.num = 0;
        p2->type = MACH_REG;
        p1->of = p2;
        if (d > 0) {
                if (!used_stack) {
                        /* Use stack pointer instead of application pointer */
                        long s = stack_size + stack_change;
                        mach_op *p3 = new_mach_op();
                        mach_op *p4 = new_mach_op();
                        p2->def.num = (long)REG_SP;
                        p3->type = MACH_SPEC;
                        p3->def.str = special_str;
                        p2->plus = p3;
                        p4->type = MACH_VAL;
                        p4->def.num = d - s / 8;
                        p3->plus = p4;
                        used_ldisp = 1;
                        return (p1);
                }
                d += 4;
        }
        p2->def.num = (long)REG_AP;
        if (d) {
                mach_op *p3 = new_mach_op();
                p3->type = MACH_VAL;
                p3->def.num = d;
                p2->plus = p3;
        }
        used_stack = 1;
        return (p1);
}

#ifndef tdf3
/*
    MAKE A 2. APPLICATION POINTER INDIRECT WITH DISPLACEMENT OPERAND

    This application pointer A5 is used by general proc. to access
    the caller parameters, when there are a dynamic number of callees.
*/

mach_op *
make_rel_ap2(long d)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        mach_op *p3 = new_mach_op();

        p1->type = MACH_CONT;
        p1->def.num = 0;
        p2->type = MACH_REG;
        p1->of = p2;
        p2->def.num = (long)REG_A5;

        p3->type = MACH_VAL;
        p3->def.num = d;
        p2->plus = p3;

        used_stack = 1;
        return (p1);
}
/*
   Used to access caller parrameters in the postlude.
 */

mach_op *
make_rel_sp(long d)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        mach_op *p3 = new_mach_op();
        long s = stack_size + stack_change;

        p1->type = MACH_CONT;
        p1->def.num = 0;
        p1->of = p2;

        p2->type = MACH_REG;
        p2->def.num = (long)REG_SP;
        p2->plus = p3;

        p3->type = MACH_VAL;
        p3->def.num = d - s / 8;

        return (p1);
}

#endif

/*
    MAKE A REGISTER INDIRECT WITH INDEX OPERAND

    Again we have to be careful, in case r1 is a D-register.
*/

mach_op *
make_reg_index(int r1, int r2, long d, int sf)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        mach_op *p3 = new_mach_op();
        mach_op *p4 = new_mach_op();
        p1->type = MACH_CONT;
        p2->type = MACH_REG;
        p2->def.num = (long)r1;
        if (is_dreg(r1)) {
                int t = tmp_reg(m_movl, p2);
                p2 = new_mach_op();
                p2->type = MACH_REG;
                p2->def.num = (long)t;
                p1->def.num = (regmsk(t) | regmsk(r2));
        } else {
                p1->def.num = (regmsk(r1) | regmsk(r2));
        }
        p1->of = p2;
        p2->plus = p3;
        p3->type = MACH_SCALE;
        p3->def.num = (long)sf;
        p3->of = p4;
        p4->type = MACH_REG;
        p4->def.num = (long)r2;
        if (d) {
                mach_op *p5 = new_mach_op();
                p5->type = MACH_VAL;
                p5->def.num = d;
                p3->plus = p5;
        }
        return (p1);
}


/*
    MAKE A APPLICATION POINTER INDEXED WITH DISPLACEMENT OPERAND

    It is always quicker to do this using a temporary register rather
    than using the complex addressing mode.  However we do use the
    latter course when temporary registers are short.

    Typ determines the type of the application pointer.
*/

mach_op *
_make_ind_rel_ap(long d, long e, int typ)
{
        mach_op *p1, *p2;

        switch (typ) {
        case par2_pl:
                p2 = make_rel_ap2(d);
                break;
        case par3_pl:
                p2 = make_rel_sp(d);
                break;
        default:
                p2 = make_rel_ap(d);
        }

        if (tmp_reg_status < 2) {
                int t = tmp_reg(m_movl, p2);
                return (make_indirect(t, e));
        }

        debug_warning("Complex operand");

        p1 = new_mach_op();
        p1->type = MACH_CONT;
        p1->def.num = 0;
        p1->of = p2;
        if (e) {
                mach_op *p3 = new_mach_op();
                p3->type = MACH_VAL;
                p3->def.num = e;
                p2->plus = p3;
        }
        return (p1);
}

mach_op *
make_ind_rel_ap(long d, long e)
{
        return _make_ind_rel_ap(d, e, 0);
}

mach_op *
make_ind_rel_ap2(long d, long e)
{
        return _make_ind_rel_ap(d, e, par2_pl);
}

mach_op *
make_ind_rel_ap3(long d, long e)
{
        return _make_ind_rel_ap(d, e, par3_pl);
}



/*
    MAKE A PRE-DECREMENT STACK POINTER OPERAND
*/

mach_op *
make_dec_sp(void)
{
        mach_op *p = new_mach_op();
        p->type = MACH_DEC;
        p->def.num = (long)REG_SP;
        return (p);
}


/*
    MAKE A POST-INCREMENT STACK POINTER OPERAND
*/

mach_op *
make_inc_sp(void)
{
        mach_op *p = new_mach_op();
        p->type = MACH_INC;
        p->def.num = (long)REG_SP;
        return (p);
}

#ifndef tdf3
/*
    MAKE A PRE-DECREMENT REGISTER OPERAND
*/

mach_op *
make_predec(int r)
{
        mach_op *p = new_mach_op();
        p->type = MACH_DEC;
        p->def.num = (long)r;
        return (p);
}
#endif

/*
    MAKE A POSTINCREMENT REGISTER OPERAND
*/

mach_op *
make_postinc(int r)
{
        mach_op *p = new_mach_op();
        p->type = MACH_INC;
        p->def.num = (long)r;
        return (p);
}


/*
    MAKE A REGISTER PAIR
*/

mach_op *
make_reg_pair(int r1, int r2)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        p1->type = MACH_RPAIR;
        p1->def.num = (long)r1;
        p1->plus = p2;
        p2->type = MACH_REG;
        p2->def.num = (long)r2;
        return (p1);
}


/*
    MAKE AN INDEX OPERAND

    The machine operands op1 and op2 are turned into an index operand
    with scale factor sf.  Unless op1 is very simple or temporary
    registers are short, it is always quicker to move the contents of
    op1 into a temporary register.  A temporary register may also be
    required for op2, but hopefully not too often.
*/

mach_op *
make_index_op(mach_op *op1, mach_op *op2, int sf)
{
        bitpattern u;
        bool use_tmp = 1;
        mach_op *p1, *p2 = new_mach_op();

        if (op1->type != MACH_CONT) {
                error("Illegal indexing operand");
                return (null);
        }

        p1 = op1->of;
        u = op1->def.num;
        if (p1->type == MACH_REG) {
                use_tmp = is_dreg(p1->def.num);
        } else if (tmp_reg_status && op2->type != MACH_REG) {
                if (p1->type == MACH_EXT && p1->plus == null) {
                        p1->type = MACH_EXTQ;
                        use_tmp = 0;
                } else if (p1->type == MACH_CONT) {
                        mach_op *q = p1->of;
                        if (q->type == MACH_REG) {
                                q = q->plus;
                                if (q == null || q->type != MACH_SCALE) {
                                        use_tmp = 0;
                                }
                        }
                }
        }

        if (use_tmp) {
                int t = tmp_reg(m_movl, p1);
                p1 = new_mach_op();
                p1->type = MACH_REG;
                p1->def.num = (long)t;
                u = regmsk(t);
        }
        op1->of = p1;
        p2->type = MACH_SCALE;
        p2->def.num = (long)sf;
        p2->plus = p1->plus;
        p1->plus = p2;
        if (op2->type == MACH_REG) {
                u |= regmsk(op2->def.num);
        } else {
                int t = tmp_reg(m_movl, op2);
                op2 = new_mach_op();
                op2->type = MACH_REG;
                op2->def.num = (long)t;
                u |= regmsk(t);
        }
        p2->of = op2;
        op1->def.num = u;
        return (op1);
}


/*
    MAKE A BITFIELD OPERAND

    The machine operand op is turned into the corresponding bitfield
    operand.
*/

mach_op *
make_bitfield_op(mach_op *op, int bf_off, int bf_bits)
{
        mach_op *p1 = new_mach_op();
        mach_op *p2 = new_mach_op();
        p1->type = MACH_BF;
        p1->def.num = (long)bf_off;
        p1->plus = p2;
        p1->of = op;
        p2->type = MACH_VAL;
        p2->def.num = (long)bf_bits;
        return (p1);
}