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/ops_logic.c,v 1.1.1.1 1998/01/17 15:55:49 release Exp $
--------------------------------------------------------------------------
$Log: ops_logic.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/11/09 14:23:43 ma
Now is_signed is used instead of issigned.
Revision 1.1.1.1 1997/10/13 12:42:57 ma
First version.
Revision 1.2 1997/09/25 06:45:26 ma
All general_proc tests passed
Revision 1.1.1.1 1997/03/14 07:50:15 ma
Imported from DRA
* Revision 1.1.1.1 1996/09/20 10:57:00 john
*
* Revision 1.2 1996/07/05 14:24:36 john
* Changes for spec 3.1
*
* Revision 1.1.1.1 1996/03/26 15:45:16 john
*
* Revision 1.6 94/06/29 14:23:47 14:23:47 ra (Robert Andrews)
* Changed a few moves to change_varieties (consequent to changes in
* bitfields).
*
* Revision 1.5 94/02/21 16:01:48 16:01:48 ra (Robert Andrews)
* A couple of arguments should be int, not long.
*
* Revision 1.4 93/11/19 16:22:37 16:22:37 ra (Robert Andrews)
* Minor correction to bitfields of complex operands.
*
* Revision 1.3 93/03/08 15:30:22 15:30:22 ra (Robert Andrews)
* Emulate cc by mapping ( a << b ) to ( a << ( b % 64 ) ) when b is
* a constant.
*
* Revision 1.2 93/03/03 14:49:37 14:49:37 ra (Robert Andrews)
* Started adding support for error treatments.
*
* Revision 1.1 93/02/22 17:16:23 17:16:23 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"
/*
MARKERS FOR AND, OR AND XOR
The logical operations, and, or and xor are handled by a single
routine with a flag to indicate which is meant. The flag can take
the following values.
*/
#define AND 0
#define OR 1
#define XOR 2
/*
AND/OR/XOR BY A CONSTANT
The value a of shape sha and size sz has the logical operator indicated
by logop applied to it and the constant c. The result is stored in
dest. instr is one of m_andl, m_orl, eorl.
*/
static void
andetc_const(int instr, shape sha, long sz, long c, where a, where dest,
int logop)
{
long whd;
/* First check that a is not a constant */
if (whereis(a) == Value) {
long ca = nw(a);
switch (logop) {
case AND:
ca &= c;
break;
case OR:
ca |= c;
break;
case XOR:
ca ^= c;
break;
}
move(sha, mnw(ca), dest);
return;
}
/* Now look for some special values of c */
switch (logop) {
case AND: {
long cc;
if (c == 0) {
move(sha, zero, dest);
return;
}
cc = ~c;
if (sz == 32) {
if (cc == 0) {
change_var(sha, a, dest);
return;
}
if (is_pow2(cc)) {
long p = log2(cc);
if (whereis(dest) == Dreg) {
change_var(sha, a, dest);
ins2n(m_bclr, p, sz, dest, 1);
have_cond = 0;
return;
}
}
}
break;
}
case OR:
if (c == 0) {
change_var(sha, a, dest);
return;
}
if (is_pow2(c)) {
long p = log2(c);
if (whereis(dest) == Dreg) {
change_var(sha, a, dest);
ins2n(m_bset, p, sz, dest, 1);
have_cond = 0;
return;
}
}
break;
case XOR:
if (c == 0) {
change_var(sha, a, dest);
return;
}
break;
}
whd = whereis(dest);
if (whd != Areg && eq_where(a, dest)) {
ins2h(instr, c, sz, dest, 1);
set_cond(dest, sz);
return;
}
if (whd == Dreg) {
change_var(sha, a, dest);
ins2h(instr, c, sz, dest, 1);
set_cond(dest, sz);
return;
}
if (whereis(a) == Dreg && last_use(a)) {
ins2h(instr, c, sz, a, 1);
change_var(sha, a, dest);
set_cond(dest, sz);
return;
}
change_var(sha, a, D0);
ins2h(instr, c, sz, D0, 1);
move(sha, D0, dest);
set_cond(dest, sz);
return;
}
/*
AUXILLARY ROUTINE FOR AND/OR/XOR
The values a1 and a2 of shape sha have the logical operation indicated
by logop applied to them and the result is stored in dest. ( opb,
opw, opl ) is an ordered triple giving the byte, word and long forms of
the appropriate machine instruction.
*/
static void
andetc(int opb, int opw, int opl, shape sha, where a1, where a2, where dest,
int logop)
{
int instr;
long wha, whb, whd;
long sz = shape_size(sha);
if (eq_where(a1, a2)) {
switch (logop) {
case AND:
move(sha, a1, dest);
return;
case OR:
move(sha, a1, dest);
return;
case XOR:
move(sha, zero, dest);
return;
}
}
instr = ins(sz, opb, opw, opl);
wha = whereis(a1);
whb = whereis(a2);
if (wha == Freg) {
move(sha, a1, D0);
andetc(opb, opw, opl, sha, D0, a2, dest, logop);
return;
}
if (whb == Freg) {
move(sha, a2, D0);
andetc(opb, opw, opl, sha, a1, D0, dest, logop);
return;
}
if (wha == Value) {
long c = nw(a1);
andetc_const(instr, sha, sz, c, a2, dest, logop);
return;
}
if (whb == Value) {
long c = nw(a2);
andetc_const(instr, sha, sz, c, a1, dest, logop);
return;
}
whd = whereis(dest);
if (eq_where(a1, dest) && whd != Areg) {
if (whb == Dreg) {
ins2(instr, sz, sz, a2, dest, 1);
return;
}
if (whd == Dreg) {
if (logop == XOR || whb == Areg) {
if (eq_where(dest, D0)) {
regsinproc |= regmsk(REG_D1);
move(sha, a2, D1);
ins2(instr, sz, sz, D1, dest, 1);
set_cond(dest, sz);
return;
} else {
move(sha, a2, D0);
ins2(instr, sz, sz, D0, dest, 1);
set_cond(dest, sz);
return;
}
} else {
ins2(instr, sz, sz, a2, dest, 1);
set_cond(dest, sz);
return;
}
} else {
move(sha, a2, D0);
ins2(instr, sz, sz, D0, dest, 1);
set_cond(dest, sz);
return;
}
}
if (eq_where(a2, dest) && whd != Areg) {
if (wha == Dreg) {
ins2(instr, sz, sz, a1, dest, 1);
set_cond(dest, sz);
return;
}
if (whd == Dreg) {
if (logop == XOR || wha == Areg || wha == Freg) {
if (eq_where(dest, D0)) {
regsinproc |= regmsk(REG_D1);
move(sha, a1, D1);
ins2(instr, sz, sz, D1, dest, 1);
} else {
move(sha, a1, D0);
ins2(instr, sz, sz, D0, dest, 1);
}
} else {
ins2(instr, sz, sz, a1, dest, 1);
}
} else {
move(sha, a1, D0);
ins2(instr, sz, sz, D0, dest, 1);
}
set_cond(dest, sz);
return;
}
if (whd == Dreg) {
if (!interfere(a2, dest)) {
move(sha, a1, dest);
andetc(opb, opw, opl, sha, a2, dest, dest, logop);
return;
}
if (!interfere(a1, dest)) {
move(sha, a2, dest);
andetc(opb, opw, opl, sha, a1, dest, dest, logop);
return;
}
}
move(sha, a1, D0);
andetc(opb, opw, opl, sha, a2, D0, D0, logop);
move(sha, D0, dest);
return;
}
/*
AND INSTRUCTION
The values a1 and a2 of shape sha are anded and the result is stored
in dested.
*/
void
and(shape sha, where a1, where a2, where dest)
{
andetc(ml_and, sha, a1, a2, dest, AND);
return;
}
/*
OR INSTRUCTION
The values a1 and a2 of shape sha are ored and the result is stored
in dested.
*/
void
or(shape sha, where a1, where a2, where dest)
{
andetc(ml_or, sha, a1, a2, dest, OR);
return;
}
/*
XOR INSTRUCTION
The values a1 and a2 of shape sha are xored and the result is stored
in dested.
*/
void
xor(shape sha, where a1, where a2, where dest)
{
andetc(ml_eor, sha, a1, a2, dest, XOR);
return;
}
/*
LOGICAL NEGATION INSTRUCTION
The value a of shape sha is logically negated and the result is stored
in dest.
*/
void
not(shape sha, where a, where dest)
{
int instr;
long sz = shape_size(sha);
long wha = whereis(a);
long whd = whereis(dest);
if (wha == Value) {
long c = nw(a);
move(sha, mnw(~c), dest);
return;
}
if (eq_where(a, dest) && whd != Areg) {
instr = ins(sz, ml_not);
ins1(instr, sz, dest, 1);
set_cond(dest, sz);
return;
}
if (whd == Dreg) {
move(sha, a, dest);
not(sha, dest, dest);
return;
}
if (wha == Dreg && last_use(a)) {
not(sha, a, a);
move(sha, a, dest);
return;
}
move(sha, a, D0);
not(sha, D0, D0);
move(sha, D0, dest);
return;
}
/*
LOW LEVEL SHIFT
This routine outputs a simple shift instruction, taking overflow
into account if necessary (not right yet).
*/
static void
shift_it(shape sha, shape shb, int instr, where by, where to)
{
long sz = shape_size(sha);
ins2(instr, L8, sz, by, to, 1);
have_cond = 0;
test_overflow(ON_OVERFLOW);
return;
}
/*
AUXILIARY SHIFT ROUTINE
The value from of shape sha is shifted, either left if sw is 0, or
right otherwise, by the value by. The result is stored in to.
The dont_use_D1 flag indicates that register D1 should not be used.
It is always false for simple shifts, but may be true for certain
multiplications which are done by shifts.
*/
void
shift_aux(shape sha, where by, where from, where to, int sw, int dont_use_D1)
{
where w;
long whb, wht;
int instr, shift_plus, shift_minus;
shape shb = sh(by.wh_exp);
long sz = shape_size(sha);
bool sig = is_signed(sha);
switch (sz) {
case 8:
shift_plus = (sig ? m_aslb : m_lslb);
shift_minus = (sig ? m_asrb : m_lsrb);
break;
case 16:
shift_plus = (sig ? m_aslw : m_lslw);
shift_minus = (sig ? m_asrw : m_lsrw);
break;
default:
shift_plus = (sig ? m_asll : m_lsll);
shift_minus = (sig ? m_asrl : m_lsrl);
break;
}
if (sw) {
/* Switch shift_plus and shift_minus for right shifts */
instr = shift_plus;
shift_plus = shift_minus;
shift_minus = instr;
}
whb = whereis(by);
wht = whereis(to);
if (whb == Value && !have_overflow()) {
long p = nw(by);
if (p == 0) {
/* A shift by 0 is a move */
move(sha, from, to);
return;
}
/* Reduce mod 64 to emulate instruction */
p &= 0x3f;
instr = shift_plus;
/* Do the shift, at most eight at a time */
if (p <= 8 || D1_is_special || dont_use_D1) {
w = (wht == Dreg ? to : D0);
move(sha, from, w);
while (p) {
long q = (p > 8 ? 7 : p);
ins2n(instr, q, sz, w, 1);
p -= q;
}
have_cond = 0;
move(sha, w, to);
return;
}
/* Fall through otherwise */
shb = slongsh;
}
if (wht == Dreg) {
if (whb == Dreg && !eq_where(by, to)) {
move(sha, from, to);
shift_it(sha, shb, shift_plus, by, to);
return;
}
if (eq_where(D0, to)) {
w = D1;
regsinproc |= regmsk(REG_D1);
} else {
w = D0;
}
move(shb, by, w);
move(sha, from, to);
shift_it(sha, shb, shift_plus, w, to);
return;
}
if (whb == Dreg) {
if (eq_where(D0, by)) {
w = D1;
regsinproc |= regmsk(REG_D1);
} else {
w = D0;
}
move(sha, from, w);
shift_it(sha, shb, shift_plus, by, w);
move(sha, w, to);
return;
}
regsinproc |= regmsk(REG_D1);
move(shb, by, D0);
move(sha, from, D1);
shift_it(sha, shb, shift_plus, D0, D1);
move(sha, D1, to);
return;
}
/*
MAIN LEFT SHIFT ROUTINE
The value from of shape sha is shifted left by the value by. The
result is stored in to.
*/
void
shift(shape sha, where by, where from, where to)
{
shift_aux(sha, by, from, to, 0, 0);
return;
}
/*
MAIN RIGHT SHIFT ROUTINE
The value from of shape sha is shifted right by the value by. The
result is stored in to.
*/
void
rshift(shape sha, where by, where from, where to)
{
shift_aux(sha, by, from, to, 1, 0);
return;
}
/*
ADJUST AN EXPRESSION READY FOR A BITFIELD OPERATION
The value in the no field of e is rounded down to a multiple of 32.
The remainder is the bitfield offset and is returned.
*/
static long
adjust_bitf(exp e)
{
long boff = no(e)% 32;
no(e) -= boff;
return (boff);
}
/*
FIND POSITION OF A CONTENTS BITFIELD
*/
static long
contents_bitf(exp e)
{
char n = name(e);
if (n == name_tag || n == reff_tag) {
return (adjust_bitf(e));
}
if (n == ident_tag) {
exp s = son(e);
exp b = bro(s);
if (name(b) == reff_tag) {
return (adjust_bitf(b));
}
if (name(b) == ident_tag) {
return (contents_bitf(b));
}
if (name(b) == name_tag && son(b) == e &&
name(s) == name_tag) {
return (contents_bitf(son(s)));
}
if (name(s) == name_tag) {
return (adjust_bitf(s));
}
}
error("Illegal bitfield operation");
return (0);
}
/*
FIND POSITION OF A BITFIELD OPERATION
*/
static long
bitf_posn(exp e)
{
char n = name(e);
if (n == name_tag) {
return (adjust_bitf(e));
}
if (n == cont_tag || n == ass_tag) {
return (bitf_posn(son(e)));
}
if (n == ident_tag) {
return (0);
}
error("Illegal bitfield operation");
return (0);
}
/*
EXTRACT A BITFIELD
The bitfield e of shape sha is extracted into dest. The current state
of the stack is also given.
*/
void
bitf_to_int(exp e, shape sha, where dest, ash stack)
{
where bf, d;
exp t = dest.wh_exp;
shape dsha = sh(t);
int extend = (is_signed(sha)? 1 : 0);
int instr = (extend ? m_bfexts : m_bfextu);
long off, sz, bstart;
bitpattern pmask;
long nbits = shape_size(sha);
long boff = bitf_posn(e);
off = 8 *(boff / 8);
sz = 8 *((boff + nbits - 1) / 8) + 8 - off;
if (sz == 24) {
sz = 32;
off -= 8;
}
bstart = boff - off;
pmask = ((hi_bits[nbits]) >> bstart) >> (32 - sz);
switch (name(t)) {
case ident_tag:
dsha = sh(son(t));
break;
case ass_tag:
dsha = sh(bro(son(t)));
break;
}
if (name(dsha) == bitfhd) {
dsha = (extend ? slongsh : ulongsh);
}
if (name(dsha) == tophd) {
warning("Top in bitfield assignment");
}
bf = mw(e, off);
if (bstart == 0 && nbits == sz) {
shape bsha;
switch (sz) {
case 8:
bsha = scharsh;
break;
case 16:
bsha = swordsh;
break;
case 32:
bsha = slongsh;
break;
}
change_var_sh(dsha, bsha, bf, dest);
return;
}
if (whereis(bf) == Dreg) {
bitpattern m = (lo_bits[nbits] << boff);
d = (whereis(dest) == Dreg ? dest : D0);
and(slongsh, bf, mnw(m), d);
if (extend) {
long r = 32 - nbits - boff;
if (r) {
if (r <= 8) {
ins2n(m_lsll, r, L32, d, 1);
ins2n(m_asrl, r, L32, d, 1);
} else {
regsinproc |= regmsk(REG_D1);
ins2n(m_moveq, r, L32, D1, 1);
ins2(m_lsll, L32, L32, D1, d, 1);
ins2(m_asrl, L32, L32, D1, d, 1);
}
}
}
have_cond = 0;
change_var_sh(dsha, slongsh, d, dest);
return;
} else {
mach_op *op1, *op2;
d = (whereis(dest) == Dreg ? dest : D0);
op1 = operand(L32, bf);
op1 = make_bitfield_op(op1,(int)bstart,(int)nbits);
op2 = operand(L32, d);
make_instr(instr, op1, op2, regs_changed(op2, 1));
have_cond = 0;
change_var_sh(dsha, slongsh, d, dest);
return;
}
}
/*
INSERT A BITFIELD
The value e is inserted into the bitfield d. The state of the stack
is also given.
*/
void
int_to_bitf(exp e, exp d, ash stack)
{
shape sha;
where dest, f;
long off, sz, bstart, bend;
bitpattern pmask, nmask, v;
long nbits = shape_size(sh(e));
long boff = bitf_posn(d);
off = 8 *(boff / 8);
sz = 8 *((boff + nbits - 1) / 8) + 8 - off;
if (sz == 24) {
sz = 32;
off -= 8;
}
bstart = boff - off;
bend = sz - nbits - bstart;
pmask = ((hi_bits[nbits]) >> bstart) >> (32 - sz);
nmask = ~pmask;
switch (sz) {
case 8:
nmask &= 0xff;
sha = scharsh;
break;
case 16:
nmask &= 0xffff;
sha = swordsh;
break;
default:
sha = slongsh;
break;
}
if (name(e) == int_to_bitf_tag) {
exp s = son(e);
if (is_o(name(s))) {
e = s;
} else {
regsinproc |= regmsk(REG_D1);
coder(D1, stack, s);
if (shape_size(sh(s)) < 32) {
warning("Think again!");
}
e = D1.wh_exp;
}
}
dest = mw(d, off);
if (bstart == 0 && nbits == sz) {
change_var_sh(sha, sh(e), zw(e), dest);
return;
}
if ((bstart + nbits > 32) || (name(e)!= val_tag)) {
where dd;
bitpattern ch;
mach_op *op1, *op2;
dd = zw(e);
if (whereis(dd)!= Dreg || shape_size(sh(e))!= 32) {
change_var_sh(slongsh, sh(e), dd, D0);
dd = D0;
}
op1 = operand(L32, dd);
op2 = operand(L32, dest);
ch = regs_changed(op2, 1);
op2 = make_bitfield_op(op2,(int)bstart,(int)nbits);
make_instr(m_bfins, op1, op2, ch);
have_cond = 0;
return;
}
v = (bitpattern)no(e);
v = ((v << bend) & pmask);
if (v == 0) {
and(sha, mnw(nmask), dest, dest);
return;
}
if (v == pmask) {
or(sha, mnw(pmask), dest, dest);
return;
}
f = ((whereis(dest) == Dreg)? dest : D0);
and(sha, mnw(nmask), dest, f);
or(sha, mnw(v), f, dest);
return;
}
/*
TEST A NUMBER OF BITS
The value a1 of shape sha is tested to see if the bits indicated by
the value a2 are set. If a2 is a constant power of 2 then a bit
test operation is used. Otherwise a1 is anded with a2 and the
result is stored in an unwanted D-register.
*/
void
bit_test(shape sha, where a1, where a2)
{
long sz = shape_size(sha);
long wh1 = whereis(a1);
long wh2 = whereis(a2);
if (wh2 == Value) {
if (wh1 == External || wh1 == Parameter || wh1 == RegInd) {
long v = nw(a2);
if (is_pow2(v)) {
where w;
long n = log2(v);
long off = sz - 8 *(1 + (n / 8));
w = mw(a1.wh_exp, a1.wh_off + off);
ins2n(m_btstb, n % 8, 8, w, 1);
have_cond = 0;
return;
}
}
if (wh1 == Dreg) {
long v = nw(a2);
if (last_use(a1)) {
and(sha, a2, a1, a1);
return;
}
if (is_pow2(v) && sz == 32) {
long n = log2(v);
ins2n(m_btstl, n, sz, a1, 1);
have_cond = 0;
return;
}
}
}
if (wh1 == Dreg && last_use(a1)) {
and(sha, a2, a1, a1);
return;
}
if (wh2 == Dreg && last_use(a2)) {
and(sha, a1, a2, a2);
return;
}
move(sha, a1, D0);
and(sha, a2, D0, D0);
return;
}