Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
Crown Copyright (c) 1997
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/ops_misc.c,v 1.1.1.1 1998/01/17 15:55:49 release Exp $
--------------------------------------------------------------------------
$Log: ops_misc.c,v $
* Revision 1.1.1.1 1998/01/17 15:55:49 release
* First version to be checked into rolling release.
*
Revision 1.5 1997/11/13 08:27:16 ma
All avs test passed (except add_to_ptr).
Revision 1.4 1997/11/10 15:38:09 ma
.
Revision 1.3 1997/11/09 14:22:51 ma
Now is_signed is used instead of issigned. Added clear for 64 bit shapes.
Revision 1.2 1997/10/29 10:22:27 ma
Replaced use_alloca with has_alloca.
Revision 1.1.1.1 1997/10/13 12:42:57 ma
First version.
Revision 1.6 1997/10/13 08:49:52 ma
Made all pl_tests for general proc & exception handling pass.
Revision 1.5 1997/09/25 06:45:28 ma
All general_proc tests passed
Revision 1.4 1997/06/24 10:56:07 ma
Added changes for "Plumhall Patch"
Revision 1.3 1997/06/18 10:09:43 ma
Checking in before merging with Input Baseline changes.
Revision 1.2 1997/04/20 11:30:36 ma
Introduced gcproc.c & general_proc.[ch].
Added cases for apply_general_proc next to apply_proc in all files.
Revision 1.1.1.1 1997/03/14 07:50:16 ma
Imported from DRA
* Revision 1.1.1.1 1996/09/20 10:56:57 john
*
* Revision 1.3 1996/07/30 16:31:50 john
* Removed offset conversion
*
* Revision 1.2 1996/07/05 14:24:52 john
* Changes for spec 3.1
*
* Revision 1.1.1.1 1996/03/26 15:45:16 john
*
* Revision 1.4 94/06/29 14:24:51 14:24:51 ra (Robert Andrews)
* Need to be more careful about bitfields in change_variety.
*
* Revision 1.3 94/02/21 16:02:15 16:02:15 ra (Robert Andrews)
* Clear up a couple of int-long confusions.
*
* Revision 1.2 93/03/03 14:49:46 14:49:46 ra (Robert Andrews)
* Added error treatment processing routine, jump_overflow.
*
* Revision 1.1 93/02/22 17:16:26 17:16:26 ra (Robert Andrews)
* Initial revision
*
--------------------------------------------------------------------------
*/
#include "config.h"
#include "common_types.h"
#include "assembler.h"
#include "basicread.h"
#include "check.h"
#include "exp.h"
#include "expmacs.h"
#include "externs.h"
#include "install_fns.h"
#include "shapemacs.h"
#include "tags.h"
#include "mach.h"
#include "mach_ins.h"
#include "where.h"
#include "mach_op.h"
#include "instr.h"
#include "codex.h"
#include "instrs.h"
#include "coder.h"
#include "tests.h"
#include "operations.h"
#include "evaluate.h"
#include "utility.h"
#include "translate.h"
#include "ops_shared.h"
#include "special_exps.h"
/************************************************************************
SET_OVERFLOW
If the expression e has a long_jump error treatment then
the global variable overflow_jump is set the the corresponding label.
If e has the error treatment trap overflow_jump is set to -1 instead.
The previous value of overflow_jump is returned, so it can be restored.
************************************************************************/
int set_overflow
PROTO_N ( ( e ) )
PROTO_T ( exp e )
{
int prev_overflow_jump = overflow_jump ;
if (! optop ( e ) ) {
if ( pt ( e ) ) {
overflow_jump = no(son ( pt ( e ) ) ) ; /* error jump on overflow */
overflow_jump = ptno(pt(son(pt(e)))) ;
overflow_jump = e->ptf.expr->sonf.expr->ptf.expr->ptf.l;
}
else {
overflow_jump = -1 ; /* trap on overflow */
}
}
return prev_overflow_jump ;
}
/************************************************************************
CLEAR_OVERFLOW
Restore the global variable overflow_jump with a previous value.
************************************************************************/
void clear_overflow
PROTO_N ( ( prev_overflow_jump ) )
PROTO_T ( int prev_overflow_jump )
{
overflow_jump = prev_overflow_jump ;
}
/************************************************************************
HAVE_OVERFLOW
Used to test if overflow_jump has been set (we have an error treatment)
************************************************************************/
int have_overflow
PROTO_Z ()
{
return overflow_jump ;
}
/************************************************************************
TRAP_INS
Calls the error handler with ec as argument
************************************************************************/
void trap_ins
PROTO_N ( ( ec ) )
PROTO_T ( int ec )
{
push ( slongsh, L32, mnw( ec ) ) ;
callins( 0, get_error_handler() ) ;
}
/*
OVERFLOW JUMP LABEL
This is 0 to denote that overflows should be ignored. Otherwise
it gives the label to be jumped to.
*/
int overflow_jump = 0 ;
int err_continue = 0;
/************************************************************************
TEST_OVERFLOW2
If an error_treatment is specified and the previous instruction
overflowed then either a trap or a jump is takken.
The test condition is specified by br_ins
************************************************************************/
void test_overflow2
PROTO_N ( ( br_ins ) )
PROTO_T ( int br_ins )
{
if ( overflow_jump == -1 ) {
ins0( bra2trap( br_ins ) ) ;
}
else if ( overflow_jump ) {
make_jump(br_ins, overflow_jump);
}
}
/************************************************************************
TEST_OVERFLOW
If an error_treatment is specified and the previous instruction
overflowed then either a trap or a jump is takken.
This function finds the right test condition based on overflow_type
************************************************************************/
void test_overflow
PROTO_N ( ( typ ) )
PROTO_T ( overflow_type typ )
{
int instr ;
if (! have_overflow() ) return ;
switch ( typ ) {
case UNCONDITIONAL: instr = m_bra ; break ;
case ON_OVERFLOW: instr = m_bvs ; break ;
case ON_CARRY: instr = m_bcs ; break ;
case ON_FP_OVERFLOW:
case ON_FP_CARRY:
ins2 ( m_fmovel, L32, L32, register ( REG_FPSR ), D0, 1 ) ;
ins2h ( m_andl, 0x00001c00, L32, D0, 1 ) ;
instr = m_bne ;
break ;
case ON_FP_OPERAND_ERROR:
ins2 ( m_fmovel, L32, L32, register ( REG_FPSR ), D0, 1 ) ;
ins2h ( m_andl, 0x00002000, L32, D0, 1 ) ;
instr = m_bne ;
break;
default:
error("invalid overflow test");
return ;
}
test_overflow2(instr);
}
/************************************************************************
CHECKALLOC_STACK
Checks if it is possible to allocate sz bytes on the stack.
If it is not possible an exception is generated.
else if do_alloc is TRUE, the allocation is done.
************************************************************************/
void checkalloc_stack
PROTO_N ( ( sz, do_alloc ) )
PROTO_T ( where sz X int do_alloc )
{
int erlab = next_lab ();
int cnlab = next_lab ();
make_comment("check for stack overflow ...") ;
ins2 (m_movl, 32, 32, SP, D0, 1);
ins2 (m_subl, 32, 32, sz, D0, 1);
make_jump (m_bcs, erlab);
ins2 (m_cmpl, 32, 32, mw(get_stack_limit(), 0), D0, 0);
make_jump (m_bcc, cnlab);
make_label (erlab);
trap_ins(f_stack_overflow);
make_label (cnlab);
if ( do_alloc )
ins2 (m_movl, 32, 32, D0, SP, 1);
make_comment("check for stack overflow done") ;
}
/*
MARK D1 AS SPECIAL
This flag is used to indicate that the D1 regsiter is being used
as a special register and should be treated with care.
*/
bool D1_is_special = 0 ;
/*
OUTPUT A CALL INSTRUCTION
The procedure call given by fn is output. A temporary A-register
needs to be used when fn is not a simple procedure name. The
stack is then increased by longs to overwrite the procedure arguments.
*/
void callins
PROTO_N ( ( longs, fn ) )
PROTO_T ( long longs X exp fn )
{
mach_op *op ;
exp s = son ( fn ), call_exp, fn_exp ;
bool simple_proc = 0 ;
fn_exp = fn ;
/* Let's see if we have the procedure at compilation time */
if ( name ( fn ) == name_tag && ! isvar ( s ) && isglob ( s ) ) {
exp def = son ( s ) ; /* Definition of Identify construct */
if ( !def || name ( def ) == proc_tag || name ( def ) == general_proc_tag )
simple_proc = 1;
}
/* If this is not a straight call, put the name into an A register */
if ( ! simple_proc ) {
where w ;
w = zw ( fn ) ;
if ( whereis ( w ) != Areg ) {
int r = next_tmp_reg () ;
regsinproc |= regmsk ( r ) ;
move ( slongsh, w, register ( r ) ) ;
fn_exp = register ( r ).wh_exp ;
}
}
/* Now output the call instruction */
call_exp = getexp ( proksh, nilexp, 0, fn_exp, nilexp, 0, L0, cont_tag ) ;
op = operand ( L32, zw ( call_exp ) ) ;
make_instr ( m_call, op, null, ~save_msk ) ;
no_calls++ ;
retcell ( call_exp ) ;
dec_stack ( -longs ) ;
have_cond = 0 ;
return ;
}
/************************************************************************
OUTPUT A JMP INSTRUCTION
The jump to the procedure given by fn is output. A temporary A-register
needs to be used when fn is not a simple procedure name.
************************************************************************/
void jmpins
PROTO_N ( ( fn ) )
PROTO_T ( exp fn )
{
mach_op *op ;
exp s = son ( fn ), jmp_exp, fn_exp ;
fn_exp = fn ;
/* If this is not a straight jmp, put the name into an A register */
if ( name ( fn ) != name_tag || isvar ( s ) || !isglob ( s ) ) {
where w ;
w = zw ( fn ) ;
if ( whereis ( w ) != Areg ) {
int r = next_tmp_reg () ;
regsinproc |= regmsk ( r ) ;
move ( slongsh, w, register ( r ) ) ;
fn_exp = register ( r ).wh_exp ;
}
}
/* Now output the jmp instruction */
jmp_exp = getexp ( proksh, nilexp, 0, fn_exp, nilexp, 0, L0, cont_tag ) ;
op = operand ( L32, zw ( jmp_exp ) ) ;
make_instr ( m_jmp, op, null, ~save_msk ) ;
retcell ( jmp_exp ) ;
have_cond = 0 ;
return ;
}
/*
CONDITION CODES STATUS
Many comparison instructions are unnecessary because the previous
instruction has set the appropriate condition flags. The flag
have_cond deals with this. A value of 0 indicates that we have
no information on the flag values. A value of 1 indicates that
the last instruction set the flags appropriate to the where
last_cond of size last_cond_sz. A value of 2 is used immediately
after a cmp instruction, the two arguments of the cmp being
last_cond and last_cond2. Finally a value of 3 is used immediately
after certain move instructions to indicate that the flags are
appropriate to either of the arguments, last_cond or last_cond_alt.
*/
bool have_cond = 0 ;
where last_cond ;
where last_cond2 ;
where last_cond_alt ;
long last_cond_sz ;
/*
COMPARE WITH ZERO
The value a (of shape sha and size sz) is compared with 0. The
cases when have_cond is 1 or 3 are dealt with by this routine.
*/
void cmp_zero
PROTO_N ( ( sha, sz, a ) )
PROTO_T ( shape sha X long sz X where a )
{
long w ;
/* Check existing condition codes */
if ( have_cond == 1 && last_cond_sz == sz ) {
if ( eq_where ( last_cond, a ) ) return ;
}
if ( have_cond == 3 && last_cond_sz == sz ) {
if ( eq_where ( last_cond, a ) ) return ;
if ( eq_where ( last_cond_alt, a ) ) return ;
}
w = whereis ( a ) ;
if ( w == Areg ) {
/* This does work, despite the manual */
int instr = ins ( sz, ml_tst ) ;
ins1 ( instr, sz, a, 0 ) ;
} else if ( w == Freg || ( w == External && name ( sha ) == prokhd ) ) {
/* Moving to D0 sets the flags */
move ( sha, a, D0 ) ;
} else {
if ( sz == 64 ) {
where w ;
w = a ;
ins1 ( m_tstl, 32, w, 0 ) ;
w.wh_off += 32 ;
ins1 ( m_tstl, 32, w, 0 ) ;
}
else {
int instr = ins ( sz, ml_tst ) ;
ins1 ( instr, sz, a, 0 ) ;
}
}
/* Set new condition codes */
set_cond ( a, sz ) ;
return ;
}
/*
AUXILIARY COMPARISON ROUTINE
The values a and b of size sz are compared.
*/
static bool cmp_aux
PROTO_N ( ( sz, a, b ) )
PROTO_T ( long sz X where a X where b )
{
where d ;
if ( whereis ( a ) == Freg ) {
if ( whereis ( b ) == Freg ) {
move ( slongsh, a, D0 ) ;
move ( slongsh, b, D1 ) ;
regsinproc |= regmsk ( REG_D1 ) ;
return ( cmp_aux ( sz, D1, D0 ) ) ;
}
if ( eq_where ( b, D0 ) ) {
d = D1 ;
regsinproc |= regmsk ( REG_D1 ) ;
} else {
d = D0 ;
}
move ( slongsh, a, d ) ;
return ( cmp_aux ( sz, b, d ) ) ;
}
if ( whereis ( b ) == Freg ) {
if ( eq_where ( a, D0 ) ) {
d = D1 ;
regsinproc |= regmsk ( REG_D1 ) ;
} else {
d = D0 ;
}
move ( slongsh, b, d ) ;
return ( cmp_aux ( sz, a, d ) ) ;
}
ins2_cmp ( ins ( sz, ml_cmp ), sz, sz, a, b, 0 ) ;
have_cond = 2 ;
last_cond = a ;
last_cond2 = b ;
last_cond_sz = sz ;
return ( 1 ) ;
}
/*
COMPARE WITH A CONSTANT
The value a is compared with the constant value c, the type of the
comparison being given by ntst. The value returned by this routine
has the same meaning as that returned by cmp.
*/
static bool cmp_const
PROTO_N ( ( sha, sz, c, a, ntst ) )
PROTO_T ( shape sha X long sz X where c X where a X long ntst )
{
bool sw ;
long v = nw ( c ) ;
if ( is_offset ( c.wh_exp ) ) v /= 8 ;
if ( v == 0 ) {
if ( !is_signed ( sha ) && ntst != tst_neq && ntst != tst_eq ) {
/* Force an actual comparison in these cases */
have_cond = 0 ;
}
cmp_zero ( sha, sz, a ) ;
return ( 1 ) ;
}
if ( v < -128 || v > 127 ) {
sw = cmp_aux ( sz, c, a ) ;
return ( sw ) ;
}
if ( interfere ( a, D0 ) ) {
sw = cmp_aux ( sz, c, a ) ;
return ( sw ) ;
}
#ifdef REJECT
if ( !output_immediately ) {
mach_ins *p = current_ins ;
if ( p && p->ins_no == m_moveq && p->op1->def.num == v ) {
sw = cmp_aux ( sz, a, register ( p->op2->def.num ) ) ;
last_cond2 = c ;
return ( !sw ) ;
}
}
#endif
move ( slongsh, c, D0 ) ;
sw = cmp_aux ( sz, a, D0 ) ;
last_cond2 = c ;
return ( !sw ) ;
}
/*
MAIN COMPARISON ROUTINE
The values var and limit of shape sha are compared for the test
indicated by ntst. Depending on the addressing modes of var and
limit we may do "cmp var,limit" or "cmp limit,var". In the first
case we return 1 and in the second 0. The case when have_cond is
2 is dealt with by this routine.
*/
bool cmp
PROTO_N ( ( sha, var, limit, ntst ) )
PROTO_T ( shape sha X where var X where limit X long ntst )
{
bool sw ;
long sz = shape_size ( sha ) ;
long rt = shtype ( sha ) ;
long whv = whereis ( var ) ;
long whl = whereis ( limit ) ;
#if 0
if (name(sha) == ptrhd) {
make_comment("HACK shape size");
shape_size(sha) = 32 ;
sz = 32 ;
}
#endif
if ( rt == Freg ) {
/* Floating point comparisons are never swapped */
where rv, rl ;
have_cond = 0 ;
if ( whv == Freg && last_use ( var ) ) {
rv = var ;
} else {
if ( eq_where ( limit, FP0 ) ) {
rv = FP1 ;
regsinproc |= regmsk ( REG_FP1 ) ;
} else {
rv = FP0 ;
}
}
if ( whl == Freg && last_use ( limit ) ) {
rl = limit ;
} else {
if ( eq_where ( rv, FP0 ) ) {
rl = FP1 ;
regsinproc |= regmsk ( REG_FP1 ) ;
} else {
rl = FP0 ;
}
}
if ( whv == Freg ) {
push_float ( sz, var ) ;
pop_float ( sz, rv ) ;
} else {
move ( sha, var, rv ) ;
}
if ( whl == Freg ) {
push_float ( sz, limit ) ;
pop_float ( sz, rl ) ;
} else {
move ( sha, limit, rl ) ;
}
ins2_cmp ( m_fcmpx, sz, sz, rl, rv, 0 ) ;
return ( 1 ) ;
}
/* Check existing condition codes */
if ( have_cond == 2 && last_cond_sz == sz ) {
if ( eq_where ( last_cond, var ) &&
eq_where ( last_cond2, limit ) ) return ( 0 ) ;
if ( eq_where ( last_cond, limit ) &&
eq_where ( last_cond2, var ) ) return ( 1 ) ;
}
if ( whl == Value ) {
sw = cmp_const ( sha, sz, limit, var, ntst ) ;
return ( sw ) ;
}
if ( whv == Value ) {
sw = cmp_const ( sha, sz, var, limit, ntst ) ;
return ( !sw ) ;
}
if ( whl == Dreg || whl == Areg ) {
sw = cmp_aux ( sz, var, limit ) ;
return ( !sw ) ;
}
if ( whv == Dreg || whv == Areg ) {
sw = cmp_aux ( sz, limit, var ) ;
return ( sw ) ;
}
#if 0
if(name (var.wh_exp) == name_tag && name(sha) == prokhd &&
((son(son(var.wh_exp))==nilexp) ||
(name(son(son(var.wh_exp))) == proc_tag))) {
exp proc_cont = getexp(sha,nilexp,0,var.wh_exp,nilexp,0,0,cont_tag);
var.wh_exp = proc_cont;
}
#endif
if ( !interfere ( var, D0 ) ) {
move ( sha, limit, D0 ) ;
sw = cmp_aux ( sz, var, D0 ) ;
last_cond2 = limit ;
return ( !sw ) ;
}
if ( !interfere ( limit, D0 ) ) {
move ( sha, var, D0 ) ;
sw = cmp_aux ( sz, limit, D0 ) ;
last_cond2 = var ;
return ( sw ) ;
}
move ( sha, limit, D1 ) ;
sw = cmp_aux ( sz, var, D1 ) ;
regsinproc |= regmsk ( REG_D1 ) ;
last_cond2 = limit ;
return ( !sw ) ;
}
/*
OUTPUT A PUSH INSTRUCTION
The value wh of shape sha and size sz is pushed onto the stack.
*/
void push
PROTO_N ( ( sha, sz, wh ) )
PROTO_T ( shape sha X long sz X where wh )
{
long s ;
mach_op *op1, *op2 ;
bool real_push = 1 ;
if ( sz != 32 ) {
if ( is_signed ( sha ) && ( whereis ( wh ) == Dreg ) ) {
change_var_sh ( slongsh, sha, wh, wh ) ;
push ( slongsh, L32, wh ) ;
} else {
change_var_sh ( slongsh, sha, wh, D0 ) ;
push ( slongsh, L32, D0 ) ;
}
have_cond = 0 ;
return ;
}
if ( stack_change ) {
stack_change -= 32 ;
real_push = 0 ;
if ( stack_direction ) update_stack () ;
s = stack_change ;
stack_change = 0 ;
}
op1 = operand ( sz, wh ) ;
if ( real_push ) {
op2 = make_dec_sp () ;
} else {
op2 = make_indirect ( REG_SP, s / 8 ) ;
}
make_instr ( m_movl, op1, op2, 0 ) ;
have_cond = 0 ;
if ( real_push ) {
stack_size -= 32 ;
} else {
stack_change = s ;
}
return ;
}
/*
PUSH A FLOATING POINT REGISTER
The floating-point register wh of size sz is pushed onto the stack.
*/
void push_float
PROTO_N ( ( sz, wh ) )
PROTO_T ( long sz X where wh )
{
mach_op *op1 = operand ( sz, wh ) ;
mach_op *op2 = make_dec_sp () ;
int instr = insf ( sz, ml_fmove ) ;
make_instr ( instr, op1, op2, 0 ) ;
stack_size -= sz ;
have_cond = 0 ;
return ;
}
/*
OUTPUT A POP OPERATION
A value of shape sha and size sz is popped from the stack into wh.
*/
void pop
PROTO_N ( ( sha, sz, wh ) )
PROTO_T ( shape sha X long sz X where wh )
{
mach_op *op1, *op2 ;
if ( sz != 32 ) {
if ( whereis ( wh ) == Dreg ) {
pop ( slongsh, L32, wh ) ;
change_var_sh ( sha, slongsh, wh, wh ) ;
} else {
pop ( slongsh, L32, D0 ) ;
change_var_sh ( sha, slongsh, D0, wh ) ;
}
have_cond = 0 ;
return ;
}
op1 = make_inc_sp () ;
op2 = operand ( sz, wh ) ;
make_instr ( m_movl, op1, op2, 0 ) ;
have_cond = 0 ;
stack_size += sz ;
return ;
}
/*
POP A FLOATING POINT REGISTER
A value of size sz is popped from the stack into the floating-point
register wh.
*/
void pop_float
PROTO_N ( ( sz, wh ) )
PROTO_T ( long sz X where wh )
{
mach_op *op1 = make_inc_sp () ;
mach_op *op2 = operand ( sz, wh ) ;
int instr = insf ( sz, ml_fmove ) ;
make_instr ( instr, op1, op2, 0 ) ;
have_cond = 0 ;
stack_size += sz ;
return ;
}
/*
MOVE AN ADDRESS INTO A TEMPORARY REGISTER
The effective address of wh is loaded into a temporary register and
the register number is returned. By default, register r is used,
but if try is true we see if we can do better.
*/
static int tmp_mova
PROTO_N ( ( wh, r, try ) )
PROTO_T ( where wh X int r X bool try )
{
tmp_reg_prefer = r ;
mova ( wh, register ( r ) ) ;
if ( try && !output_immediately && current_ins ) {
int i = current_ins->ins_no ;
if ( i == m_lea || i == m_movl ) {
mach_op *op1 = current_ins->op1 ;
mach_op *op2 = current_ins->op2 ;
if ( op2->type == MACH_REG && op2->def.num == r ) {
int t = r ;
if ( i == m_lea ) {
if ( op1->type == MACH_CONT ) {
op1 = op1->of ;
if ( op1->type == MACH_REG && op1->plus == null ) {
t = op1->def.num ;
}
}
} else {
if ( op1->type == MACH_REG ) t = op1->def.num ;
}
if ( t != r ) {
current_ins->ins_no = m_ignore_ins ;
op2->def.num = t ;
r = t ;
}
}
}
}
regsinproc |= regmsk ( r ) ;
return ( r ) ;
}
/*
MOVE A CONSTANT VALUE
The constant value c is assigned to the where to (of shape sha and
size sz).
*/
void move_const
PROTO_N ( ( sha, sz, c, to ) )
PROTO_T ( shape sha X long sz X long c X where to )
{
int instr ;
int whto = whereis ( to ) ;
if ( c == 0 ) {
/* Clearing is a special case */
if ( whto == Dreg ) {
ins2n ( m_moveq, 0, L32, to, 1 ) ;
set_cond ( to, sz ) ;
return ;
}
if ( whto == Areg ) {
ins2 ( m_subl, L32, L32, to, to, 1 ) ;
have_cond = 0 ;
return ;
}
if ( sz == 64 ) {
where w ;
w = to ;
ins1 ( m_clrl, 32, w, 0 ) ;
w.wh_off += 32 ;
ins1 ( m_clrl, 32, w, 0 ) ;
}
else {
instr = ins ( sz, ml_clr ) ;
ins1 ( instr, sz, to, 1 ) ;
set_cond ( to, sz ) ;
}
return ;
}
instr = ins ( sz, ml_mov ) ;
if ( sz == 8 ) c &= 0xff ;
if ( sz == 16 ) c &= 0xffff ;
if ( c >= -128 && c <= 127 ) {
/* Look for quick moves */
if ( whto == Dreg ) {
ins2n ( m_moveq, c, L32, to, 1 ) ;
set_cond ( to, sz ) ;
return ;
} else {
ins2n ( m_moveq, c, L32, D0, 1 ) ;
if ( whto == Areg ) instr = m_movl ;
ins2 ( instr, sz, sz, D0, to, 1 ) ;
if ( whto == Areg ) {
have_cond = 0 ;
} else {
set_cond ( to, sz ) ;
}
return ;
}
}
if ( whto == Areg && sz == 8 ) {
ins2n ( instr, c, sz, D0, 1 ) ;
ins2 ( m_movl, L32, L32, D0, to, 1 ) ;
} else {
ins2n ( instr, c, sz, to, 1 ) ;
}
if ( whto == Areg ) {
have_cond = 0 ;
} else {
set_cond ( to, sz ) ;
}
return ;
}
/*
MOVE FROM A FLOATING-POINT REGISTER
The value in the floating-point register from (of size sz) is moved
into to.
*/
static void move_from_freg
PROTO_N ( ( sz, from, to ) )
PROTO_T ( long sz X where from X where to )
{
int instr = insf ( sz, ml_fmove ) ;
switch ( whereis ( to ) ) {
case Dreg : {
ins2 ( m_fmoves, sz, sz, from, to, 1 ) ;
have_cond = 0 ;
return ;
}
case Freg : {
ins2 ( m_fmovex, sz, sz, from, to, 1 ) ;
have_cond = 0 ;
return ;
}
case RegPair : {
exp te = to.wh_exp ;
if ( sz != 64 ) error ( "Wrong floating variety" ) ;
push_float ( sz, from ) ;
pop ( slongsh, L32, zw ( son ( te ) ) ) ;
pop ( slongsh, L32, zw ( bro ( te ) ) ) ;
have_cond = 0 ;
return ;
}
default : {
ins2 ( instr, sz, sz, from, to, 1 ) ;
have_cond = 0 ;
return ;
}
}
}
/*
MOVE TO A FLOATING-POINT REGISTER
The value in from (of size sz) is moved into the floating-point
register to.
*/
static void move_to_freg
PROTO_N ( ( sz, from, to ) )
PROTO_T ( long sz X where from X where to )
{
int instr = insf ( sz, ml_fmove ) ;
switch ( whereis ( from ) ) {
case Dreg : {
ins2 ( m_fmoves, sz, sz, from, to, 1 ) ;
have_cond = 0 ;
return ;
}
case Areg : {
move ( slongsh, from, D0 ) ;
ins2 ( m_fmoves, sz, sz, D0, to, 1 ) ;
have_cond = 0 ;
return ;
}
case Freg : {
ins2 ( m_fmovex, sz, sz, from, to, 1 ) ;
have_cond = 0 ;
return ;
}
case RegPair : {
exp fe = from.wh_exp ;
if ( sz != 64 ) error ( "Wrong floating variety" ) ;
push ( slongsh, L32, zw ( bro ( fe ) ) ) ;
push ( slongsh, L32, zw ( son ( fe ) ) ) ;
pop_float ( sz, to ) ;
have_cond = 0 ;
return ;
}
default : {
ins2 ( instr, sz, sz, from, to, 1 ) ;
have_cond = 0 ;
return ;
}
}
}
/*
TEST AN EXTERNAL FOR SIMPLE CONTENTS/ASSIGN
The expression e of external storage type is checked for simple
operand type.
*/
static bool ca_extern
PROTO_N ( ( e ) )
PROTO_T ( exp e )
{
char n = name ( e ) ;
if ( n != cont_tag && n != ass_tag ) return ( 0 ) ;
return ( name ( son ( e ) ) == name_tag ? 1 : 0 ) ;
}
/*
MOVE LARGE OBJECTS
sz bits are copied from from to to. down can be 0 (start at the
top), 1 (start at the bottom) or 2 (don't care).
*/
void move_bytes
PROTO_N ( ( sz, from, to, down ) )
PROTO_T ( long sz X where from X where to X int down )
{
long off ;
int instr ;
exp fe = from.wh_exp ;
exp te = to.wh_exp ;
long fof = from.wh_off ;
long tof = to.wh_off ;
long whfrom = whereis ( from ) ;
long whto = whereis ( to ) ;
/* Set up move types */
int r1 = REG_A0 ;
int r2 = REG_A1 ;
int s1 = 0 ;
int s2 = 0 ;
if ( whfrom == External && ca_extern ( fe ) ) s1 = 3 ;
if ( name ( te ) == apply_tag || name ( te ) == apply_general_tag
|| name ( te ) == tail_call_tag ) s2 = 1 ;
if ( whto == External && ca_extern ( te ) ) s2 = 3 ;
if ( whfrom == Variable || whfrom == Parameter || whfrom == RegInd ) {
s1 = 3 ;
}
if ( whto == Variable || whto == Parameter || whto == RegInd ) {
s2 = 3 ;
}
if ( whfrom == RegPair ) s1 = 4 ;
if ( whto == RegPair ) s2 = 4 ;
if ( sz > 12 * 32 && s2 != 1 && down != 1 ) {
mach_op *op1, *op2 ;
long lab = next_lab () ;
long longs = ( sz / 32 ) ;
sz -= 32 * longs ;
r1 = REG_A0 ;
r2 = REG_A1 ;
s1 = 0 ;
s2 = 0 ;
tmp_mova ( from, r1, 0 ) ;
tmp_mova ( to, r2, 0 ) ;
move ( slongsh, mnw ( longs - 1 ), D0 ) ;
make_label ( lab ) ;
op1 = make_postinc ( r1 ) ;
op2 = make_postinc ( r2 ) ;
make_instr ( m_movl, op1, op2, regmsk ( r1 ) | regmsk ( r2 ) ) ;
op1 = make_register ( REG_D0 ) ;
op2 = make_lab_data ( lab, 0 ) ;
make_instr ( m_dbf, op1, op2, regmsk ( REG_D0 ) ) ;
} else {
if ( s1 == 0 ) {
int r = tmp_mova ( from, r1, 1 ) ;
if ( r != r1 ) {
if ( s2 == 0 ) r2 = tmp_mova ( to, r1, 1 ) ;
r1 = r ;
} else {
if ( s2 == 0 ) r2 = tmp_mova ( to, r2, 1 ) ;
}
} else {
if ( s2 == 0 ) r2 = tmp_mova ( to, REG_A1, 1 ) ;
}
}
off = 0 ;
while ( sz ) {
mach_op *op1, *op2 ;
long b = ( ( sz >= 32 ) ? 32 : ( ( sz >= 16 ) ? 16 : 8 ) ) ;
sz -= b ;
if ( down != 0 ) off = sz ;
instr = ins ( b, ml_mov ) ;
switch ( s1 ) {
case 0 : op1 = make_indirect ( r1, off / 8 ) ; break ;
case 2 : op1 = make_lab_ind ( r1, off / 8 ) ; break ;
case 3 : op1 = operand ( L32, mw ( fe, fof + off ) ) ; break ;
case 4 : {
op1 = operand ( L32, zw ( sz ? bro ( fe ) : son ( fe ) ) ) ;
break ;
}
}
switch ( s2 ) {
case 0 : op2 = make_indirect ( r2, off / 8 ) ; break ;
case 1 : op2 = make_dec_sp () ; break ;
case 3 : op2 = operand ( L32, mw ( te, tof + off ) ) ; break ;
case 4 : {
op2 = operand ( L32, zw ( sz ? bro ( te ) : son ( te ) ) ) ;
break ;
}
}
make_instr ( instr, op1, op2, 0 ) ;
if ( s2 == 1 ) stack_size -= b ;
off += b ;
}
have_cond = 0 ;
return ;
}
/*
MAIN MOVE ROUTINE
A value of shape sha is moved from from into to. There are several
main subcases : floating-point values, values of sizes 8, 16 and 32,
and all other cases.
*/
void move
PROTO_N ( ( sha, from, to ) )
PROTO_T ( shape sha X where from X where to )
{
int instr ;
long sz = shape_size ( sha ) ;
long rt = shtype ( sha ) ;
where from1, from2 ;
exp fe = from.wh_exp ;
exp te = to.wh_exp ;
long fof = from.wh_off ;
long tof = to.wh_off ;
long whfrom = whereis ( from ) ;
long whto = whereis ( to ) ;
if ( sz == 0 || eq_where ( from, to ) || eq_where(to,zero)) {
return ;
}
sz = round ( sz, shape_align ( sha ) ) ;
if ( name ( sha ) == bitfhd && sz != 8 && sz != 16 ) sz = 32 ;
if ( rt == Freg || whfrom == Freg || whto == Freg ) {
if ( name ( fe ) == real_tag ) whfrom = Value ;
if ( name ( te ) == apply_tag || name ( te ) == apply_general_tag
|| name ( te ) == tail_call_tag ) {
switch ( whfrom ) {
case Dreg :
case Areg : {
from1 = from ;
break ;
}
case Freg : {
push_float ( sz, from ) ;
return ;
}
case Value : {
long *p = realrep ( fe ) ;
if ( p ) {
from1 = mnw ( p [0] ) ;
if ( sz > 32 ) from2 = mnw ( p [1] ) ;
} else {
long lb = next_lab () ;
exp t = simple_exp ( internal_tag ) ;
make_constant ( lb, fe ) ;
no ( t ) = lb ;
from1 = mw ( t, fof ) ;
from2 = mw ( t, fof + 32 ) ;
}
break ;
}
case RegPair : {
from1 = zw ( son ( fe ) ) ;
from2 = zw ( bro ( fe ) ) ;
break ;
}
case Variable : {
from1 = mw ( fe, fof ) ;
if ( sz > 32 ) from2 = mw ( fe, fof + 32 ) ;
break ;
}
case External : {
if ( ca_extern ( fe ) ) {
from1 = mw ( fe, fof ) ;
if ( sz > 32 ) from2 = mw ( fe, fof + 32 ) ;
} else {
tmp_mova ( from, REG_A0, 0 ) ;
from1 = A0_p ;
if ( sz > 32 ) from2 = mw ( A0_p.wh_exp, 32 ) ;
}
break ;
}
default : {
tmp_mova ( from, REG_A0, 0 ) ;
from1 = A0_p ;
if ( sz > 32 ) from2 = mw ( A0_p.wh_exp, 32 ) ;
break ;
}
}
if ( sz > 32 ) move ( slongsh, from2, to ) ;
move ( slongsh, from1, to ) ;
have_cond = 0 ;
return ;
}
if ( whfrom == Freg ) {
move_from_freg ( sz, from, to ) ;
return ;
}
if ( whto == Freg ) {
move_to_freg ( sz, from, to ) ;
return ;
}
if ( whfrom == Value ) {
if ( sz == 32 ) {
long *p = realrep ( fe ) ;
if ( p ) {
from1 = mnw ( p [0] ) ;
ins2 ( m_movl, L32, L32, from1, to, 1 ) ;
} else {
ins2 ( m_movl, L32, L32, from, to, 1 ) ;
}
have_cond = 0 ;
return ;
} else {
long *p = realrep ( fe ) ;
if ( p ) {
from1 = mnw ( p [0] ) ;
from2 = mnw ( p [1] ) ;
} else {
long lb = next_lab () ;
exp t = simple_exp ( internal_tag ) ;
make_constant ( lb, fe ) ;
no ( t ) = lb ;
from1 = mw ( t, fof ) ;
from2 = mw ( t, fof + 32 ) ;
}
if ( whto == RegPair ) {
ins2 ( m_movl, L32, L32, from1, zw ( son ( te ) ), 1 ) ;
ins2 ( m_movl, L32, L32, from2, zw ( bro ( te ) ), 1 ) ;
have_cond = 0 ;
return ;
}
ins2 ( m_movl, L32, L32, from2, mw ( te, tof + 32 ), 1 ) ;
ins2 ( m_movl, L32, L32, from1, to, 1 ) ;
have_cond = 0 ;
return ;
}
}
if ( whfrom == RegPair ) {
if ( sz != 64 ) error ( "Wrong floating variety" ) ;
ins2 ( m_movl, L32, L32, zw ( bro ( fe ) ),
mw ( te, tof + 32 ), 1 ) ;
ins2 ( m_movl, L32, L32, zw ( son ( fe ) ), to, 1 ) ;
have_cond = 0 ;
return ;
}
if ( whto == RegPair ) {
if ( sz != 64 ) error ( "Wrong floating variety" ) ;
ins2 ( m_movl, L32, L32, from, zw ( son ( te ) ), 1 ) ;
ins2 ( m_movl, L32, L32, mw ( fe, fof + 32 ),
zw ( bro ( te ) ), 1 ) ;
have_cond = 0 ;
return ;
}
/* Fall through otherwise */
}
/* Move things of size 8, 16 or 32 */
if ( sz <= 32 && sz != 24 ) {
if ( name ( te ) == apply_tag || name ( te ) == apply_general_tag
|| name ( te ) == tail_call_tag ) {
if ( whfrom == Value ) {
mach_op *op1, *op2 ;
long v = nw ( from ) ;
if ( is_offset ( from.wh_exp ) ) v /= 8 ;
if ( v == 0 && stack_change == 0 ) {
op1 = make_dec_sp () ;
make_instr ( m_clrl, op1, null, 0 ) ;
have_cond = 0 ;
stack_size -= 32 ;
return ;
}
if ( v >= -128 && v <= 127 ) {
long s = stack_change ;
stack_change = 0 ;
op1 = make_value ( v ) ;
op2 = make_register ( REG_D0 ) ;
make_instr ( m_moveq, op1, op2, regmsk ( REG_D0 ) ) ;
stack_change = s ;
push ( sha, L32, D0 ) ;
return ;
}
if ( stack_change ) {
push ( sha, L32, from ) ;
return ;
}
op1 = make_int_data ( v ) ;
make_instr ( m_pea, op1, null, 0 ) ;
have_cond = 0 ;
stack_size -= 32 ;
return ;
}
push ( sha, sz, from ) ;
return ;
}
if ( name ( fe ) == null_tag ) {
move_const ( sha, sz, L0, to ) ;
return ;
}
if ( whfrom == Value ) {
long v = nw ( from ) ;
if ( is_offset ( from.wh_exp ) ) v /= 8 ;
move_const ( sha, sz, v, to ) ;
return ;
}
if ( sz == 8 ) {
if ( whfrom == Areg ) {
move ( slongsh, from, D0 ) ;
move ( sha, D0, to ) ;
return ;
}
if ( whto == Areg ) {
move ( sha, from, D0 ) ;
move ( slongsh, D0, to ) ;
return ;
}
}
if ( whfrom == Other && whto == Other ) {
move ( sha, from, D0 ) ;
move ( sha, D0, to ) ;
return ;
}
# if 0
if ((name(sha) == prokhd) && (whfrom == External) && (whto == Dreg)){
/* We need the contents of this address */
move(sha,from,A0);
move(sha,A0_p,D0);
move(sha,D0,to);
return;
}
#endif
instr = ins ( sz, ml_mov ) ;
ins2 ( instr, sz, sz, from, to, 1 ) ;
if ( whto == Areg ) {
have_cond = 0 ;
} else {
set_cond ( to, sz ) ;
if ( whfrom == Dreg || whfrom == Areg ) set_cond_alt ( from ) ;
}
return ;
}
if ( name ( fe ) == null_tag ) {
move_const ( sha, sz, L0, to ) ;
return ;
}
/* Other cases are dealt with by move_bytes */
move_bytes ( sz, from, to, 2 ) ;
return ;
}
/*
MOVE ADDRESS ROUTINE
The effective address of from is loaded into to.
*/
void mova
PROTO_N ( ( from, to ) )
PROTO_T ( where from X where to )
{
int r ;
exp fe = from.wh_exp ;
char nf = name ( fe ) ;
char nt = name ( to.wh_exp ) ;
if ( nf == reff_tag ) {
exp s = son ( from.wh_exp ) ;
mova ( mw ( s, nw ( from ) ), to ) ;
return ;
}
if ( nt == apply_tag || nt == apply_general_tag || nt == tail_call_tag ) {
exp s = son ( from.wh_exp ) ;
if ( nf == cont_tag ) {
ins1 ( m_pea, L32, zw ( s ), 0 ) ;
} else {
ins1 ( m_pea, L32, from, 0 ) ;
}
stack_size -= 32 ;
have_cond = 0 ;
return ;
}
switch ( nf ) {
case val_tag : {
move ( slongsh, from, to ) ;
return ;
}
case cont_tag :
case ass_tag : {
exp s = son ( from.wh_exp ) ;
if ( from.wh_off == 0 && name ( s ) == name_tag ) {
exp ss = son ( s ) ;
if ( !isvar ( ss ) && !isglob ( ss ) ) {
move ( slongsh, zw ( s ), to ) ;
return ;
}
}
break ;
}
}
if ( whereis ( to ) == Areg ) {
/*
if (nf == name_tag && isvar (son (fe))) {
move (slongsh, from, to);
return;
}
*/
if (nf == name_tag && !isvar(son(fe)) && ptno(son(fe)) == reg_pl)
add(slongsh, mw(fe, 0), mw(zeroe, from.wh_off/8), to);
else {
ins2 ( m_lea, L32, L32, from, to, 1 ) ;
have_cond = 0 ;
}
return ;
}
r = next_tmp_reg () ;
regsinproc |= regmsk ( r ) ;
ins2 ( m_lea, L32, L32, from, register ( r ), 1 ) ;
have_cond = 0 ;
tmp_reg_status = 1 ;
move ( slongsh, register ( r ), to ) ;
return ;
}
long range_max
PROTO_N ( (shp) )
PROTO_T ( shape shp )
{
switch (name(shp)) {
case scharhd : return 0x7f;
case swordhd : return 0x7fff;
case slonghd : return 0x7fffffff;
case ucharhd : return 0xff;
case uwordhd : return 0xffff;
case ulonghd : return 0xffffffff;
default : fprintf(stderr,"Illegal shape in comparison");
}
return 0 ;
}
long range_min
PROTO_N ( (shp) )
PROTO_T ( shape shp )
{
switch (name(shp)) {
case scharhd : return -0x80;
case swordhd : return -0x8000;
case slonghd : return -0x80000000;
case ucharhd : case uwordhd : case ulonghd : return 0;
default : fprintf(stderr,"Illegal shape in comparison");
}
return 0 ;
}
/*
AUXILIARY CHANGE VARIETY ROUTINE
The value from of shape shf is converted to a value of shape sht and
moved into to.
*/
void change_var_sh
PROTO_N ( ( sht, shf, from, to ) )
PROTO_T ( shape sht X shape shf X where from X where to )
{
int instr ;
long szf = shape_size ( shf ) ;
long szt = shape_size ( sht ) ;
bool sgf = is_signed ( shf ) ;
bool sgt = is_signed ( sht ) ;
long whf = whereis ( from ) ;
long wht = whereis ( to ) ;
if(have_overflow()) {
if (whf == Value) {
if(((nw(from) < 0) && !is_signed(sht)) ||
((nw(from)) < 0 && (is_signed(sht) && name(shf)==ulonghd))) {
test_overflow( UNCONDITIONAL ) ;
}
if(is_signed(sht)) {
if((nw(from) < range_min(sht)) || (nw(from) > range_max(sht))) {
test_overflow( UNCONDITIONAL ) ;
}
}
else {
if((nw(from) < (unsigned)range_min(sht)) ||
(nw(from) > (unsigned)range_max(sht))) {
test_overflow( UNCONDITIONAL ) ;
}
}
}
}
if ( whf == Value ) {
long v = dochvar ( nw ( from ), sht ) ;
move ( sht, mnw ( v ), to ) ;
return ;
}
if ( name ( sht ) == bitfhd ) {
sgt = is_signed ( sht ) ;
switch ( szt ) {
case 8 : {
sht = ( sgt ? scharsh : ucharsh ) ;
break ;
}
case 16 : {
sht = ( sgt ? swordsh : uwordsh ) ;
break ;
}
default : {
szt = L32 ;
sht = ( sgt ? slongsh : ulongsh ) ;
break ;
}
}
}
if ( name ( shf ) == bitfhd ) {
sgf = is_signed ( shf ) ;
switch ( szf ) {
case 8 : {
shf = ( sgf ? scharsh : ucharsh ) ;
break ;
}
case 16 : {
shf = ( sgf ? swordsh : uwordsh ) ;
break ;
}
default : {
szf = L32 ;
shf = ( sgf ? slongsh : ulongsh ) ;
break ;
}
}
}
if(have_overflow()) {
bool sw;
int br_ins ;
/*move(shf,from,D0);*/
if(is_signed(shf) && !is_signed(sht)) {
/* if signed -> unsigned, test lt 0. */
exp zero_exp = getexp(shf,nilexp,0,nilexp,nilexp,0,0,val_tag);
sw = cmp(shf,from,zw(zero_exp),tst_ls);
br_ins = branch_ins(tst_ls,sw,1,is_floating(name(shf)));
test_overflow2( br_ins ) ;
kill_exp(zero_exp,zero_exp);
}
if(is_signed(sht) && (name(shf) == ulonghd)) {
/* treat the unsigned value as signed and check .lt. zero */
int br_ins ;
exp zero_exp = getexp(slongsh,nilexp,0,nilexp,nilexp,0,0,val_tag);
sw = cmp(slongsh,from,zw(zero_exp),tst_ls);
br_ins = branch_ins(tst_ls,sw,1,is_floating(name(shf)));
test_overflow2( br_ins ) ;
kill_exp(zero_exp,zero_exp);
}
if(name(sht) <= name(shf)) { /* shortening variety */
exp max_val = getexp(sht,nilexp,0,nilexp,nilexp,0,range_max(sht),
val_tag);
exp min_val = getexp(sht,nilexp,0,nilexp,nilexp,0,range_min(sht),
val_tag);
int br_ins ;
if ( whf != Dreg ) {
move ( shf, from, D0 ) ;
from = D0 ;
whf = Dreg ;
}
/* if value is a char or word we must sign-extend it, as
the checks are done using long arithmetic */
if( is_signed(shf) && (szf < 32)) {
ins1((szf == 16)?m_extl : m_extbl,32,from,1);
}
sw = cmp(is_signed(sht)?slongsh:ulongsh,from,zw(max_val),tst_gr);
br_ins = branch_ins(tst_gr,sw,is_signed(sht), is_floating(name(sht)));
test_overflow2( br_ins ) ;
sw = cmp(is_signed(sht)?slongsh:ulongsh,from,zw(min_val),tst_ls);
br_ins = branch_ins(tst_ls,sw,is_signed(sht), is_floating(name(sht)));
test_overflow2( br_ins ) ;
kill_exp(max_val,max_val);
kill_exp(min_val,min_val);
}
}
if(szt<=szf) {
if ( whf == Parameter ) {
where adj ;
adj = mw ( from.wh_exp, from.wh_off + szf - szt ) ;
move ( sht, adj, to ) ;
return ;
}
if ( szt == szf || whf == Dreg ) {
move ( sht, from, to ) ;
return ;
}
if ( wht == Dreg ) {
move ( shf, from, to ) ;
return ;
}
move ( shf, from, D0 ) ;
move ( sht, D0, to ) ;
return ;
}
if ( sgf && sgt && szf == 16 && szt == 32 ) {
/* The instruction "mov.w <ea>, %an" automatically sign extends */
if ( wht == Areg ) {
move ( shf, from, to ) ;
return ;
}
if ( wht != Dreg ) {
int r = next_tmp_reg () ;
move ( shf, from, register ( r ) ) ;
tmp_reg_status = 1 ;
move ( sht, register ( r ), to ) ;
regsinproc |= regmsk ( r ) ;
return ;
}
}
if ( sgf ) {
bool d ;
where dest ;
if ( wht == Dreg ) {
dest = to ;
move ( shf, from, dest ) ;
d = 0 ;
} else {
if ( whf == Dreg ) {
/* Extension is non-intrusive */
dest = from ;
} else {
dest = D0 ;
move ( shf, from, dest ) ;
}
d = 1 ;
}
if ( szf == 8 ) {
instr = ( szt == 16 ? m_extw : m_extbl ) ;
} else {
instr = m_extl ;
}
ins1 ( instr, szt, dest, 1 ) ;
set_cond ( dest, szt ) ;
if ( d ) move ( sht, dest, to ) ;
} else {
if ( wht == Dreg ) {
if ( eq_where ( to, from ) ) {
long v = ( szf == 8 ? 0xff : 0xffff ) ;
if ( !eq_where ( to, D0 ) ) and ( slongsh, mnw ( v ), to, to ) ;
return ;
}
}
move ( slongsh, zero, D0 ) ;
move ( shf, from, D0 ) ;
move ( sht, D0, to ) ;
return ;
}
return ;
}
/*
MAIN CHANGE VARIETY ROUTINE
The value from is converted to a value of shape sha and moved into to.
*/
void change_var
PROTO_N ( ( sha, from, to ) )
PROTO_T ( shape sha X where from X where to )
{
shape shf = sh ( from.wh_exp ) ;
change_var_sh ( sha, shf, from, to ) ;
return ;
}
/*
FIND APPROPRIATE BRANCH INSTRUCTION TYPE
This routine returns the appropriate branch instruction for test number
test_no, which should be switched if sw is 0. sf indicates whether
a floating-point instruction should be used. If not, sg indicates
whether a signed or unsigned instruction should be used.
*/
int branch_ins
PROTO_N ( ( test_no, sw, sg, sf ) )
PROTO_T ( long test_no X int sw X int sg X int sf )
{
int r = test_no ;
if ( !sw ) {
switch ( r ) {
case tst_le : r = tst_ge ; break ;
case tst_ls : r = tst_gr ; break ;
case tst_ge : r = tst_le ; break ;
case tst_gr : r = tst_ls ; break ;
case tst_ngr : r = tst_nls ; break ;
case tst_nge : r = tst_nle ; break ;
case tst_nls : r = tst_ngr ; break ;
case tst_nle : r = tst_nge ; break ;
}
}
switch ( r ) {
case tst_eq : {
/* Equal */
return ( sf ? m_fbeq : m_beq ) ;
}
case tst_neq : {
/* Not equal */
return ( sf ? m_fbne : m_bne ) ;
}
case tst_le : {
/* Less than or equals */
if ( sf ) return ( m_fble ) ;
return ( sg ? m_ble : m_bls ) ;
}
case tst_ls : {
/* Less than */
if ( sf ) return ( m_fblt ) ;
return ( sg ? m_blt : m_bcs ) ;
}
case tst_ge : {
/* Greater than or equals */
if ( sf ) return ( m_fbge ) ;
return ( sg ? m_bge : m_bcc ) ;
}
case tst_gr : {
/* Greater than */
if ( sf ) return ( m_fbgt ) ;
return ( sg ? m_bgt : m_bhi ) ;
}
case tst_ngr : {
/* Not greater than */
if ( sf ) return ( m_fbngt ) ;
return ( sg ? m_ble : m_bls ) ;
}
case tst_nge : {
/* Not greater than or equals */
if ( sf ) return ( m_fbnge ) ;
return ( sg ? m_blt : m_bcs ) ;
}
case tst_nls : {
/* Not less than */
if ( sf ) return ( m_fbnlt ) ;
return ( sg ? m_bge : m_bcc ) ;
}
case tst_nle : {
/* Not less than or equals */
if ( sf ) return ( m_fbnle ) ;
return ( sg ? m_bgt : m_bhi ) ;
}
}
error ( "Illegal test" ) ;
return ( m_dont_know ) ;
}
/*
OUTPUT CONDITIONAL JUMP
A jump to the label indicated by jr is output. test_no, sw, sg and sf
have the same meanings as in branch_ins.
*/
void branch
PROTO_N ( ( test_no, jr, sg, sw, sf ) )
PROTO_T ( long test_no X exp jr X int sg X int sw X int sf )
{
make_jump ( branch_ins ( test_no, sw, sg, sf ), ptno ( jr ) ) ;
return ;
}