Rev 5 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2002-2005 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$
*/
/*
Copyright (c) 1993 Open Software Foundation, Inc.
All Rights Reserved
Permission to use, copy, modify, and distribute this software
and its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appears in all
copies and that both the copyright notice and this permission
notice appear in supporting documentation.
OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE.
IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
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.
*/
/**********************************************************************
$Author: release $
$Date: 1998/02/04 15:48:50 $
$Revision: 1.2 $
$Log: inst_fmt.c,v $
* Revision 1.2 1998/02/04 15:48:50 release
* Added OSF copyright message.
*
* Revision 1.1.1.1 1998/01/17 15:55:57 release
* First version to be checked into rolling release.
*
* Revision 1.2 1996/10/04 16:01:25 pwe
* add banners and mod for PWE ownership
*
**********************************************************************/
/******************************************************************
inst_formats.c
Procs for outputting various POWER instruction formats to the
external file - as_file. Each procedure produces assembler for a family
of POWER operations, the actual member is passed as the string
understood by the assembler.
******************************************************************/
#include "config.h"
#include "geninst.h"
#include "proc.h"
#include "myassert.h"
#include "flags.h"
#include "comment.h"
#include "translat.h"
#include "maxminmacs.h" /* for absval() */
#include "stack.h"
#include "inst_fmt.h"
#include "macro.h"
#include "mask.h"
#define IS_POW2(c) ((c)!= 0 && ((c) & ((c) -1)) == 0)
#define CHECKREG(r) ASSERT(IS_FIXREG(r) && (!IS_SREG(r) || (r) >= p_sreg_first_save || (r == R_FP)));
#define CHECKFREG(r) ASSERT((!IS_FLT_SREG(r) || (r) >= p_sfreg_first_save));
extern FILE *as_file;
char * get_instruction(Instruction_P);
char *ext_name(long id)
{
if (id < 0)
{
char *ext = main_globals[(-id) - 1] ->dec_u.dec_val.dec_id;
return ext;
}
else
{
static char sp[16];
ASSERT(id > R_LAST); /* not a confused register */
sprintf(sp, "L.D%ld", id);
return sp;
}
}
/* +++ do better for offset to R_SP too big, trace reg or load extra base reg */
void ld_ro_ins(Instruction_P ins, baseoff a, int dest)
{
CHECKREG(dest); CHECKREG(a.base);
clear_reg(dest);
if (a.base == R_0)
{
/* base reg of R_0 is not normally allowed, special case 0 offset */
if (a.offset != 0)
fail("ld_ro_ins: non zero offset to R_0");
/* with XXXx (indexed instructions) RA of R_0 is taken as constant 0 */
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s",get_instruction(ins),reg_macro(dest),reg_macro(R_0),reg_macro(R_0));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d", get_instruction(ins), dest, R_0, R_0);
#endif
}
else if (IMM_SIZE(a.offset))
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%d(%s)", get_instruction(ins), reg_macro(dest), (int)a.offset, reg_macro(a.base));
#else
fprintf(as_file, "\t%s\t%d,%d(%d)", get_instruction(ins), dest,(int)a.offset, a.base);
#endif
}
else
{
/* offset too big, put in temp reg and used ld_rr_ins */
/* +++ arrange stack variable to minimise this */
ASSERT(a.base!=R_TMP0);
ld_const_ins(a.offset, R_TMP0);
ld_rr_ins(ins, a.base, R_TMP0, dest);
}
}
void ld_rr_ins(Instruction_P ins, int reg1, int reg2, int dest)
{
CHECKREG(dest); CHECKREG(reg1); CHECKREG(reg2);
ASSERT(reg1!=R_0);
clear_reg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s\n", get_instruction(ins), reg_macro(dest), reg_macro(reg1), reg_macro(reg2));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d\n", get_instruction(ins), dest, reg1, reg2);
#endif
}
void set_ins(baseoff a, int dest)
{
char *extname = ext_name(a.base);
CHECKREG(dest);
clear_reg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,T.%s(%s)\n", get_instruction(i_l), reg_macro(dest), extname, reg_macro(R_TOC));
#else
fprintf(as_file, "\t%s\t%d,T.%s(%d)\n", get_instruction(i_l), dest, extname, R_TOC);
#endif
/* +++ offsets in TOC */
if (a.offset != 0)
rir_ins(i_a, dest, a.offset, dest);
}
void ld_ins(Instruction_P ins, baseoff a, int dest)
{
/*
* Not a single instruction. Load from baseoff, which may be a global
* requiring a temporary reg.
*/
CHECKREG(dest);
if (!IS_FIXREG(a.base))
{
/* global */
baseoff tmp_off;
FULLCOMMENT2("ld_ins ext: off=%#x -> r%d", a.offset, dest);
ASSERT(a.offset==0 || dest!=R_TMP0);
/* load base into dest reg, then let ld_ro_ins do offset (which may need R_TMP0) */
tmp_off.base = a.base;
tmp_off.offset = 0;
set_ins(tmp_off, dest);
tmp_off.base = dest;
tmp_off.offset = a.offset;
ld_ro_ins(ins, tmp_off, dest);comment(NIL);
}
else
{
ld_ro_ins(ins, a, dest);comment(NIL);
}
}
void st_ro_ins(Instruction_P ins, int src, baseoff a)
{
CHECKREG(src); CHECKREG(a.base);
/* in general we cannot cope with store using temp reg, catch it always */
if ((src == R_TMP0 || a.base == R_TMP0) && absval(a.offset) > (16 + 1 + 6) * 4) /* leeway for mem_temp() */
fail("st_ro_ins: store of temp reg to offset not allowed"); /* should not happen */
if (a.base == R_0)
{
if (a.offset != 0)
fail("st_ro_ins: non zero offset to R_0");
/* with XXXx (indexed instructions) RA of R_0 is taken as constant 0 */
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s", get_instruction(ins), reg_macro(src), reg_macro(R_0), reg_macro(R_0));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d", get_instruction(ins), src, R_0, R_0);
#endif
}
else if (IMM_SIZE(a.offset))
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%d(%s)", get_instruction(ins), reg_macro(src), (int)a.offset, reg_macro(a.base));
#else
fprintf(as_file, "\t%s\t%d,%d(%d)", get_instruction(ins), src,(int)a.offset, a.base);
#endif
}
else
{
/* offset too big, put in temp reg and used st_rr_ins */
/* +++ arrange stack variable to minimise this */
COMMENT("st_ro_ins: big offset, use temp reg and st_rr_ins");
ASSERT(a.base!=R_TMP0); /* otherwise we corrupt it */
ld_const_ins(a.offset, R_TMP0);
st_rr_ins(ins, src, a.base, R_TMP0);
}
}
void st_rr_ins(Instruction_P ins, int src, int reg1, int reg2)
{
CHECKREG(src); CHECKREG(reg1); CHECKREG(reg2);
ASSERT(reg1!=R_0);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s\n", get_instruction(ins), reg_macro(src), reg_macro(reg1), reg_macro(reg2));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d\n", get_instruction(ins), src, reg1, reg2);
#endif
}
void st_ins(Instruction_P ins, int src, baseoff a)
{
/*
* Not a single instruction. Store into baseoff, which may be a global
* requiring a temporary reg.
*/
CHECKREG(src);
if (!IS_FIXREG(a.base))
{
/* global */
baseoff tmp_off;
if (src == R_TMP0)
fail("st_ins: store of temp reg to global not allowed"); /* should not happen */
tmp_off.base = R_TMP0;
tmp_off.offset = 0;
set_ins(a, R_TMP0);
st_ro_ins(ins, src, tmp_off);comment(NIL);
}
else
{
st_ro_ins(ins, src, a);comment(NIL);
}
}
/* 3 register operand instructions, source1, source2, destination */
void rrr_ins(Instruction_P ins, int src1, int src2, int dest)
{
CHECKREG(dest); CHECKREG(src1); CHECKREG(src2);
clear_reg(dest);
/* i_s is a pseudo instruction, use i_sf with reversed ops */
if (ins == i_s)
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%s\n", get_instruction(i_sf), reg_macro(dest), reg_macro(src2), reg_macro(src1));
#else
fprintf(as_file, "\t%s\t%d,%d,%d\n", get_instruction(i_sf), dest, src2, src1);
#endif
}
else
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%s\n", get_instruction(ins), reg_macro(dest), reg_macro(src1), reg_macro(src2));
#else
fprintf(as_file, "\t%s\t%d,%d,%d\n", get_instruction(ins), dest, src1, src2);
#endif
}
}
/* source register, immediate, destination register instructions */
void rir_ins(Instruction_P ins, int src, long imm, int dest)
{
bool logical = ins == i_and || ins == i_or || ins == i_xor||
ins ==i_and_cr|| ins==i_or_cr || ins==i_xor_cr;
CHECKREG(dest); CHECKREG(src);
ASSERT(ins!=i_divs&&ins!=i_div); /* no divi, so we should not be called for div */
/*
* Check agaonst IBM assembler bug which we should avoid at higher levels:
* IX25505 bosadt : SRI 30,29,0 DOES NOT ASSEMBLE CORRECTLY
*/
ASSERT(!(ins==i_sr && imm==0));
clear_reg(dest);
if (!logical && IMM_SIZE(imm))
{
Instruction_P imins;
if (ins==i_a) imins=i_ai;
else if (ins==i_a_cr) imins=i_ai_cr;
else if (ins==i_sf) imins=i_sfi;
else if (ins==i_sl) imins=i_sli;
else if (ins==i_sl_cr) imins=i_sli_cr;
else if (ins==i_sr) imins=i_sri;
else if (ins==i_sr_cr) imins=i_sri_cr;
else if (ins==i_sra) imins=i_srai;
else if (ins==i_sra_cr) imins=i_srai_cr;
else if (ins==i_muls) imins=i_muli;
else
{
printf("Unknown immediate instruction for %s\n",get_instruction(ins));
imins=ins;
}
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file,"\t%s\t%s,%s,%ld\n",get_instruction(imins), reg_macro(dest), reg_macro(src), imm);
#else
fprintf(as_file,"\t%s\t%d,%d,%ld\n",get_instruction(imins), dest, src, imm);
#endif
return;
}
if (ins == i_a && IMMLOGU_SIZE(imm))
{
unsigned long uimm = imm;
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%ld\n", get_instruction(i_cau), reg_macro(dest), reg_macro(src), uimm >> 16);
#else
fprintf(as_file, "\t%s\t%d,%d,%ld\n", get_instruction(i_cau), dest, src, uimm >> 16);
#endif
return;
}
if ((ins == i_a || ins == i_s) && IMM_SIZE((imm/2) +1) && dest != R_SP)
{
COMMENT1("rir_ins: special casing add/sub of constant %ld", imm);
if (ins == i_s && imm == 0x8000)
{
/* use -0x8000 as imm, which is immediate */
rir_ins(i_a, src, -imm, dest);
}
else
{
/* use 2 adds or subs */
long half = imm / 2;
rir_ins(ins, src, half, dest);
rir_ins(ins, dest, imm - half, dest);
}
return;
}
if (ins == i_and)
{
/*
* See if we can use rlinm instruction, ie a single series of one bits.
* This is prefered to generating an andiX. below, which may cause
* a CR delay.
*/
unsigned long x = imm;
ASSERT(x != 0); /* should be handled above */
if (is_a_mask(x) || is_a_mask(~x))
{
COMMENT1("rir_ins: special casing and of constant %#lx", imm);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,0,0x%lx\n", get_instruction(i_rlinm), reg_macro(dest), reg_macro(src), imm);
#else
fprintf(as_file, "\t%s\t%d,%d,0,0x%lx\n", get_instruction(i_rlinm), dest, src, imm);
#endif
return;
}
}
if (ins == i_and_cr)
{
/*
* See if we can use rlinm instruction, ie a single series of one bits.
* This is prefered to generating an andiX. below, which may cause
* a CR delay.
*/
unsigned long x = imm;
ASSERT(x != 0); /* should be handled above */
if (is_a_mask(x) || is_a_mask(~x))
{
COMMENT1("rir_ins: special casing and of constant %#lx", imm);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,0,0x%lx\n", get_instruction(i_rlinm_cr), reg_macro(dest), reg_macro(src), imm);
#else
fprintf(as_file, "\t%s\t%d,%d,0,0x%lx\n", get_instruction(i_rlinm_cr), dest, src, imm);
#endif
return;
}
}
if (logical && IMMLOGL_SIZE(imm)) /* Lower 16 bit load */
{
Instruction_P ilins;
if (ins==i_and) ilins = i_andil_cr;
else if (ins==i_or) ilins = i_oril;
else if (ins==i_xor) ilins = i_xoril;
else if (ins==i_and_cr)ilins = i_andil_cr;
else fail("Should never reach here");
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%ld\n", get_instruction(ilins), reg_macro(dest), reg_macro(src), imm);
#else
fprintf(as_file, "\t%s\t%d,%d,%ld\n", get_instruction(ilins), dest, src, imm);
#endif
return;
}
if (logical && IMMLOGU_SIZE(imm)) /* Upper 16 bit load */
{
unsigned long uimm = imm;
Instruction_P iuins;
if (ins==i_and) iuins = i_andiu_cr;
else if (ins==i_or) iuins = i_oriu;
else if (ins==i_xor) iuins = i_xoriu;
else if (ins==i_and_cr)iuins = i_andiu_cr;
else fail("Should never reach here");
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%ld\n",get_instruction(iuins), reg_macro(dest), reg_macro(src), uimm >> 16);
#else
fprintf(as_file, "\t%s\t%d,%d,%ld\n",get_instruction(iuins), dest, src, uimm >> 16);
#endif
return;
}
if (ins == i_or)
{
/* or lower and then upper end */
unsigned long uimm = imm;
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,0x%lx\n", get_instruction(i_oril), reg_macro(dest), reg_macro(src), uimm & 0xffff);
fprintf(as_file, "\t%s\t%s,%s,0x%lx\n", get_instruction(i_oriu), reg_macro(dest), reg_macro(dest), uimm >> 16);
#else
fprintf(as_file, "\t%s\t%d,%d,0x%lx\n", get_instruction(i_oril), dest, src, uimm & 0xffff);
fprintf(as_file, "\t%s\t%d,%d,0x%lx\n", get_instruction(i_oriu), dest, dest, uimm >> 16);
#endif
return;
}
/* default: use temp reg for large constant */
COMMENT("rir_ins: large constant in R_TMP0");
if (src == R_TMP0)
fail("rir_ins: temp reg in use when needed for large constant"); /* should not happen */
ld_const_ins(imm, R_TMP0);
rrr_ins(ins, src, R_TMP0, dest);
}
/* register to register pseudo instruction */
void rr_ins(Instruction_P ins, int src, int dest)
{
CHECKREG(dest); CHECKREG(src);
clear_reg(dest);
if (ins == i_not)
rir_ins(i_sf, src, -1, dest); /* implements monadic not */
else
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s\n", get_instruction(ins), reg_macro(dest), reg_macro(src));
#else
fprintf(as_file, "\t%s\t%d,%d\n", get_instruction(ins), dest, src);
#endif
}
/* mov fixed point reg to another */
void mov_rr_ins(int src, int dest)
{
CHECKREG(dest); CHECKREG(src);
if (src != dest)
{
clear_reg(dest);
/* move by i_oril of src with 0 to dest */
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%d", get_instruction(i_oril), reg_macro(dest), reg_macro(src), 0);
#else
fprintf(as_file, "\t%s\t%d,%d,%d", get_instruction(i_oril), dest, src, 0);
#endif
}
}
/* load const into fixed point reg */
void ld_const_ins(long imm, int dest)
{
CHECKREG(dest);
clear_reg(dest);
if (IMM_SIZE(imm))
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%ld\n", get_instruction(i_lil), reg_macro(dest), imm);
#else
fprintf(as_file, "\t%s\t%d,%ld\n", get_instruction(i_lil), dest, imm);
#endif
}
else
{
unsigned long uimm = imm;
unsigned long uimml = uimm & 0xffff;
/* load upper 16 bits */
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,0x%lx\n",get_instruction(i_liu), reg_macro(dest), uimm >> 16);
#else
fprintf(as_file, "\t%s\t%d,0x%lx\n",get_instruction(i_liu), dest, uimm >> 16);
#endif
/* or in lower 16 bits if needed */
if (uimml != 0)
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,0x%lx\n", get_instruction(i_oril), reg_macro(dest), reg_macro(dest), uimml);
#else
fprintf(as_file, "\t%s\t%d,%d,0x%lx\n", get_instruction(i_oril), dest, dest, uimml);
#endif
}
}
}
/* move from branch unit to fixed point reg */
void mf_ins(Instruction_P ins, int dest)
{
if (ins !=i_mffs)
{
CHECKREG(dest);
clear_reg(dest);
}
else
{
CHECKFREG(dest);
clear_freg(dest);
}
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s\n", get_instruction(ins), reg_macro(dest));
#else
fprintf(as_file, "\t%s\t%d\n", get_instruction(ins), dest);
#endif
}
/* move to branch unit from fixed point reg */
void mt_ins(Instruction_P ins, int src)
{
CHECKREG(src);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s\n", get_instruction(ins), reg_macro(src));
#else
fprintf(as_file, "\t%s\t%d\n", get_instruction(ins), src);
#endif
}
/* zeroadic pseudo instruction */
void z_ins(Instruction_P ins)
{
fprintf(as_file, "\t%s\n", get_instruction(ins));
}
/******************************************************************************
Branch instructions. These have labels as destination.
******************************************************************************/
/* unconditional branch */
void uncond_ins(Instruction_P ins, int lab)
{
fprintf(as_file, "\t%s\tL.%d\n", get_instruction(ins), lab);
}
/*
* Call, and external jump instructions.
*/
/* jump/call to external identifier */
void extj_ins(Instruction_P ins, baseoff b)
{
char *ext;
FULLCOMMENT1("extj_ins: global proc no=%d",(-b.base) - 1);
ASSERT(((-b.base) -1) >=0);
ext = main_globals[(-b.base) - 1] ->dec_u.dec_val.dec_id;
fprintf(as_file, "\t%s\t.%s\n", get_instruction(ins), ext);
/*
* By convention a special no-op is generated after a call,
* which the linker changes to reload our TOC reg (2) if the
* call is inter-module.
*
* We optimise by omitting the no-op where we know the call is intra-module.
*/
if (diagnose || !main_globals[(-b.base) -1] ->dec_u.dec_val.have_def)
{
fprintf(as_file, "\t%s\t%d,%d,%d\n", get_instruction(i_cror), 15, 15, 15); /* conventional nop */
}
}
/* jump/call to compiler generated external identifier, eg .mul */
void extj_special_ins(Instruction_P ins, char *nm)
{
fprintf(as_file, "\t%s\t%s\n", get_instruction(ins), nm);
fprintf(as_file, "\t%s\t%d,%d,%d\n", get_instruction(i_cror), 15, 15, 15); /* conventional nop */
}
/*
* Conditional instructions.
*/
/* branch conditional instruction */
void bc_ins(Instruction_P ins, int creg, int lab, int prediction)
{
if (architecture == POWERPC_CODE)
{
int BI;
int BO;
BI=creg*4;
if (ins==i_ble)
{
BO = 4;
BI += 1;
}
else if (ins ==i_blt)
{
BO = 12;
BI += 0;
}
else if (ins == i_bge)
{
BO = 4;
BI += 0;
}
else if (ins == i_bgt)
{
BO = 12;
BI += 1;
}
else if (ins == i_bne)
{
BO = 4;
BI += 2;
}
else if (ins == i_beq)
{
BO = 12;
BI += 2;
}
else
{
fprintf(as_file,"\t%s\t%d,L.%d\n",get_instruction(ins),creg,lab);
return;
}
if (prediction)
{
BO+=1;
}
fprintf(as_file,"\t%s\t%d,%d,L.%d\n",get_instruction(i_bc),BO,BI,lab);
}
else
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file,"\t%s\t%s,L.%d\n",get_instruction(ins),cr_macro(creg),lab);
#else
fprintf(as_file,"\t%s\t%d,L.%d\n",get_instruction(ins),creg,lab);
#endif
}
}
/* branch conditional instruction */
void long_bc_ins(Instruction_P ins, int creg, int lab, int prediction)
{
/* same as bc_ins only the test is reversed so that the lab is called directly so that there is no chance of the branch being out of range */
long new_lab = lab;
lab = new_label();
if (ins==i_blt) {ins = i_bge;}
else if (ins==i_ble) {ins = i_bgt;}
else if (ins==i_bne) {ins = i_beq;}
else if (ins==i_beq) {ins = i_bne;}
else if (ins==i_bgt) {ins = i_ble;}
else if (ins==i_bge) {ins = i_blt;}
else if (ins==i_bso) {ins = i_bns;}
else if (ins==i_bns) {ins = i_bso;}
else
{
fail("Don't know how to reverse this test");
}
if (architecture == POWERPC_CODE)
{
int BI;
int BO;
BI=creg*4;
if (ins==i_ble)
{
BO = 4;
BI += 1;
}
else if (ins ==i_blt)
{
BO = 12;
BI += 0;
}
else if (ins == i_bge)
{
BO = 4;
BI += 0;
}
else if (ins == i_bgt)
{
BO = 12;
BI += 1;
}
else if (ins == i_bne)
{
BO = 4;
BI += 2;
}
else if (ins == i_beq)
{
BO = 12;
BI += 2;
}
else
{
fprintf(as_file,"\t%s\t%d,L.%d\n",get_instruction(ins),creg,lab);
uncond_ins(i_b,new_lab);
set_label(lab);
return;
}
if (prediction)
{
BO+=1;
}
fprintf(as_file,"\t%s\t%d,%d,L.%d\n",get_instruction(i_bc),BO,BI,lab);
uncond_ins(i_b,new_lab);
set_label(lab);
return;
}
else
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file,"\t%s\t%s,L.%d\n",get_instruction(ins),cr_macro(creg),lab);
#else
fprintf(as_file,"\t%s\t%d,L.%d\n",get_instruction(ins),creg,lab);
#endif
uncond_ins(i_b,new_lab);
set_label(lab);
return;
}
}
/* cmp or cmpl instruction */
void cmp_rr_ins(Instruction_P ins, int reg1, int reg2, int cr_dest)
{
CHECKREG(reg1); CHECKREG(reg2);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%s\n", get_instruction(ins), cr_macro(cr_dest), reg_macro(reg1), reg_macro(reg2));
#else
fprintf(as_file, "\t%s\t%d,%d,%d\n", get_instruction(ins), cr_dest, reg1, reg2);
#endif
}
/* for cmpi or cmpli instruction */
void cmp_ri_ins(Instruction_P ins, int reg, long imm, int cr_dest)
{
CHECKREG(reg);
/* +++ for equality can use cmpi or cmpli for larger constant range */
if (ins == i_cmp && IMM_SIZE(imm))
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%si\t%s,%s,%ld\n", get_instruction(ins), cr_macro(cr_dest), reg_macro(reg), imm);
#else
fprintf(as_file, "\t%si\t%d,%d,%ld\n", get_instruction(ins), cr_dest, reg, imm);
#endif
}
else if (ins == i_cmpl && IMMLOGL_SIZE(imm))
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%si\t%s,%s,%ld\n", get_instruction(ins), cr_macro(cr_dest), reg_macro(reg), imm);
#else
fprintf(as_file, "\t%si\t%d,%d,%ld\n", get_instruction(ins), cr_dest, reg, imm);
#endif
}
else
{
/* use temp reg for large constant */
COMMENT("condri_ins: large constant in R_TMP0");
if (reg == R_TMP0)
fail("cmp_ri_ins: temp reg in use when needed for large constant"); /* should not happen */
ld_const_ins(imm, R_TMP0);
cmp_rr_ins(ins, reg, R_TMP0, cr_dest);
}
}
/*
* Floating point instructions.
*/
void ldf_ro_ins(Instruction_P ins, baseoff a, int dest)
{
CHECKREG(a.base); CHECKFREG(dest);
clear_freg(dest);
if (a.base == R_0)
{
if (a.offset != 0)
fail("ldf_ro_ins: non zero offset to R_0");
/* with XXXx (indexed instructions) RA of R_0 is taken as constant 0 */
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s\n", get_instruction(ins), freg_macro(dest), reg_macro(R_0), reg_macro(R_0));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d\n", get_instruction(ins), dest, R_0, R_0);
#endif
}
else
if (IMM_SIZE(a.offset))
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%d(%s)\n", get_instruction(ins), freg_macro(dest), (int)a.offset, reg_macro(a.base));
#else
fprintf(as_file, "\t%s\t%d,%d(%d)\n", get_instruction(ins), dest,(int)a.offset, a.base);
#endif
}
else
{
/* offset too big, put in temp reg and used ld_rr_ins */
/* +++ arrange stack variable to minimise this */
COMMENT("ldf_ro_ins: big offset, use R_TMP0 and ldf_rr_ins");
ASSERT(a.base!=R_TMP0); /* otherwise we corrupt it */
ld_const_ins(a.offset, R_TMP0);
ldf_rr_ins(ins, a.base, R_TMP0, dest);
}
}
void ldf_rr_ins(Instruction_P ins, int reg1, int reg2, int dest)
{
CHECKREG(reg1); CHECKREG(reg2); CHECKFREG(dest);
clear_freg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s\n", get_instruction(ins), freg_macro(dest), reg_macro(reg1), reg_macro(reg2));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d\n", get_instruction(ins), dest, reg1, reg2);
#endif
}
void ldf_ins(Instruction_P ins, baseoff a, int dest)
{
/*
* Not a single instruction. Load from baseoff, which may be a global
* requiring a temporary reg.
*/
CHECKFREG(dest);
if (!IS_FIXREG(a.base))
{
/* global */
baseoff tmp_off;
tmp_off.base = R_TMP0;
tmp_off.offset = 0;
set_ins(a, R_TMP0);
ldf_ro_ins(ins, tmp_off, dest);
}
else
{
ldf_ro_ins(ins, a, dest);
}
}
void stf_ro_ins(Instruction_P ins, int src, baseoff a)
{
CHECKREG(a.base); CHECKFREG(src);
/* in general we cannot cope with store using temp reg, catch it always */
if (a.base == R_TMP0 && absval(a.offset) > (16 + 1 + 6) * 4) /* leeway for mem_temp() */
fail("stf_ro_ins: store of temp reg to offset not allowed"); /* should not happen */
if (a.base == R_0)
{
if (a.offset != 0)
fail("stf_ro_ins: non zero offset to R_0");
/* with XXXx (indexed instructions) RA of R_0 is taken as constant 0 */
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s\n", get_instruction(ins), freg_macro(src), reg_macro(R_0), reg_macro(R_0));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d\n", get_instruction(ins), src, R_0, R_0);
#endif
}
else
if (IMM_SIZE(a.offset))
{
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%d(%s)\n", get_instruction(ins), freg_macro(src), (int)a.offset, reg_macro(a.base));
#else
fprintf(as_file, "\t%s\t%d,%d(%d)\n", get_instruction(ins), src,(int)a.offset, a.base);
#endif
}
else
{
/* offset too big, put in temp reg and used stf_rr_ins */
/* +++ arrange stack variable to minimise this */
COMMENT("stf_ro_ins: big offset, use temp reg and stf_rr_ins");
ASSERT(a.base!=R_TMP0); /* otherwise we corrupt it */
ld_const_ins(a.offset, R_TMP0);
stf_rr_ins(ins, src, a.base, R_TMP0);
}
}
void stf_rr_ins(Instruction_P ins, int src, int reg1, int reg2)
{
CHECKREG(reg1); CHECKREG(reg2);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%sx\t%s,%s,%s\n", get_instruction(ins), freg_macro(src), reg_macro(reg1), reg_macro(reg2));
#else
fprintf(as_file, "\t%sx\t%d,%d,%d\n", get_instruction(ins), src, reg1, reg2);
#endif
}
void stf_ins(Instruction_P ins, int src, baseoff a)
{
/*
* Not a single instruction. Store into baseoff, which may be a global
* requiring a temporary reg.
*/
CHECKFREG(src);
if (!IS_FIXREG(a.base))
{
/* global */
baseoff tmp_off;
tmp_off.base = R_TMP0;
tmp_off.offset = 0;
set_ins(a, R_TMP0);
stf_ro_ins(ins, src, tmp_off);
}
else
{
stf_ro_ins(ins, src, a);
}
}
void rrf_cmp_ins(Instruction_P ins, int reg1, int reg2, int cr_dest)
{
CHECKFREG(reg1); CHECKFREG(reg2);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%s\n", get_instruction(ins), cr_macro(cr_dest), freg_macro(reg1), freg_macro(reg2));
#else
fprintf(as_file, "\t%s\t%d,%d,%d\n", get_instruction(ins), cr_dest, reg1, reg2);
#endif
}
void rrf_ins(Instruction_P ins, int src, int dest)
{
CHECKFREG(dest); CHECKFREG(src);
clear_freg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s\n", get_instruction(ins), freg_macro(dest), freg_macro(src));
#else
fprintf(as_file, "\t%s\t%d,%d\n", get_instruction(ins), dest, src);
#endif
}
void rrrf_ins(Instruction_P ins, int src1, int src2, int dest)
{
CHECKFREG(dest); CHECKFREG(src1); CHECKFREG(src2);
clear_freg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file, "\t%s\t%s,%s,%s\n", get_instruction(ins), freg_macro(dest), freg_macro(src1), freg_macro(src2));
#else
fprintf(as_file, "\t%s\t%d,%d,%d\n", get_instruction(ins), dest, src1, src2);
#endif
}
void rrrrf_ins(Instruction_P ins, int src1, int src2, int src3, int dest)
{
CHECKFREG(dest); CHECKFREG(src1); CHECKFREG(src2); CHECKFREG(src3);
clear_freg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file,"\t%s\t%s,%s,%s,%s\n",get_instruction(ins),freg_macro(dest),freg_macro(src1),freg_macro(src2),freg_macro(src3));
#else
fprintf(as_file,"\t%s\t%d,%d,%d,%d\n",get_instruction(ins),dest,src1,src2,src3);
#endif
}
void rlinm_ins(Instruction_P ins, int src1, int sl, unsigned int mask, int dest)
{
CHECKREG(dest);CHECKREG(src1);
ASSERT(ins==i_rlinm||ins==i_rlinm_cr);
clear_reg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file,"\t%s\t%s,%s,%d,0x%x\n",get_instruction(ins),reg_macro(dest),reg_macro(src1),sl,mask);
#else
fprintf(as_file,"\t%s\t%d,%d,%d,0x%x\n",get_instruction(ins),dest,src1,sl,mask);
#endif
}
void mfspr_ins(int spr, int dest)
{
CHECKREG(dest);
clear_reg(dest);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file,"\t%s\t%s,%s\n",get_instruction(i_mfspr),reg_macro(dest),spr_macro(spr));
#else
fprintf(as_file,"\t%s\t%d,%d\n",get_instruction(i_mfspr),dest,spr);
#endif
}
void mtfsfi_ins(int fld, int imm)
{
fprintf(as_file,"\t%s\t%d,%d\n",get_instruction(i_mtfsfi),fld,imm);
}
void mtfsb0_ins(int bit)
{
ASSERT(bit>=0 && bit<=31);
fprintf(as_file,"\t%s\t%d\n",get_instruction(i_mtfsb0),bit);
}
void mtfsb1_ins(int bit)
{
ASSERT(bit>=0 && bit<=31);
fprintf(as_file,"\t%s\t%d\n",get_instruction(i_mtfsb1),bit);
}
void mcrfs_ins(int a, int b)
{
ASSERT(a>=0 && a<=7);
ASSERT(b>=0 && b<=7);
#ifdef DO_ASSEMBLER_MACROS
fprintf(as_file,"\t%s\t%s,%d\n",get_instruction(i_mcrfs),cr_macro(a),b);
#else
fprintf(as_file,"\t%s\t%d,%d\n",get_instruction(i_mcrfs),a,b);
#endif
}
void lsi_ins(int src, int dest, int nb)
{
fprintf(as_file,"\t%s\t%d,%d,%d\n",get_instruction(i_lsi),dest,src,nb);
}
void stsi_ins(int src, int dest, int nb)
{
fprintf(as_file,"\t%s\t%d,%d,%d\n",get_instruction(i_stsi),src,dest,nb);
}
void comment(char *p)
{
#ifdef DEBUG_POWERTRANS
if (p==NIL)
{
fprintf(as_file,"\n");
}
else
{
fprintf(as_file," # %s\n",p);
}
#else
fprintf(as_file,"\n");
#endif
return;
}
char * get_instruction(Instruction_P ins)
{
char *w;
switch (architecture)
{
case COMMON_CODE:
w=ins->com;
break;
case RS6000_CODE:
w=ins->pwr;
break;
case POWERPC_CODE:
w=ins->ppc;
break;
};
return w;
}