Files
Run-Sun3-SunOS-4.1.1/tme-0.8_up/ic/sparc/sparc-rc-insns.c
Amberelle Mason ac30ff9032 Initial import
Initial import of SunOS 4.1.1 and TME 0.8
2023-05-01 12:16:40 -04:00

2223 lines
85 KiB
C

/* $Id: sparc-rc-insns.c,v 1.8 2010/06/05 17:00:47 fredette Exp $ */
/* ic/sparc/sparc-rc-insns.c - SPARC recode instruction support: */
/*
* Copyright (c) 2008 Matt Fredette
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Matt Fredette.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
#if TME_SPARC_RECODE_SIZE(ic) == TME_RECODE_SIZE_32
_TME_RCSID("$Id: sparc-rc-insns.c,v 1.8 2010/06/05 17:00:47 fredette Exp $");
/* macros: */
/* the sparc instruction recoder is mostly driven by the 32-bit
control value kept in the sparc_recode variable. this value
includes various fields and flags, and is initially taken from the
sparc instruction's entry in _tme_sparc_recode_insn_opmap[]. this
entry provides only the least significant 16 bits; other
more-significant bits may be added as recoding proceeds: */
/* the opcode for the recode instruction: */
#define TME_SPARC_RECODE_INSN_OPCODE(x) ((tme_uint8_t) (x))
#define TME_SPARC_RECODE_INSN_OPCODE_MASK (0xff)
/* if set, the PC and PC_next registers must be valid for this sparc
instruction, either because the sparc instruction itself needs them
to be valid, or because the situation calls for them to be valid
(for example, when TME_SPARC_RECODE_INSN_LAST is also set): */
#define TME_SPARC_RECODE_INSN_UPDATE_PCS (1 << 8)
/* if set, this sparc instruction will be the last one recoded in this
instructions thunk: */
#define TME_SPARC_RECODE_INSN_LAST (1 << 9)
/* when TME_SPARC_RECODE_INSN_UPDATE_INSN is set, these flags are set
if this sparc instruction doesn't take rs2/simm13 and/or rs1 source
operands: */
#define TME_SPARC_RECODE_INSN_NO_RS2 (1 << 10)
#define TME_SPARC_RECODE_INSN_NO_RS1 (1 << 11)
/* when the recode opcode is TME_RECODE_OPCODE_GUEST, this flag is set
if this sparc instruction doesn't write any sparc register visible
to recode: */
#define TME_SPARC_RECODE_INSN_NO_RD (1 << 12)
/* if set, the TME_SPARC_IREG_INSN register must be valid for this
sparc instruction, usually because it uses a generic assist
function that needs the instruction word: */
#define TME_SPARC_RECODE_INSN_UPDATE_INSN (1 << 13)
/* if set, this is a shift instruction: */
#define TME_SPARC_RECODE_INSN_SHIFT (1 << 14)
/* when TME_SPARC_RECODE_INSN_UPDATE_INSN is clear, this is set if
this sparc instruction needs TME_RECODE_FLAG_CARRY set with the
%icc.C flag: */
#define TME_SPARC_RECODE_INSN_DEFC (TME_SPARC_RECODE_INSN_NO_RS2)
/* when TME_SPARC_RECODE_INSN_UPDATE_INSN is clear, this is set if
this sparc instruction updates the sparc condition codes: */
#define TME_SPARC_RECODE_INSN_CC (TME_SPARC_RECODE_INSN_NO_RS1)
/* NB: the following sparc_recode flags cannot be used in the 16-bit
_tme_sparc_recode_insn_opmap[]: */
/* if set, this suppresses the normal recoding of the sparc
instruction itself (although any TME_SPARC_RECODE_INSN_UPDATE_PCS
will still be done): */
#define TME_SPARC_RECODE_INSN_NONE (1 << 16)
/* if set, this is a v9 32-bit right shift: */
#define TME_SPARC_RECODE_INSN_SHIFT_RIGHT (1 << 17)
/* if set, after the normal recoding of the sparc instruction, a
recode endif will be emitted: */
#define TME_SPARC_RECODE_INSN_NEED_ENDIF (1 << 18)
/* when TME_SPARC_RECODE_INSN_LAST is set, this is any recode chain
information for the instructions thunk: */
#define TME_SPARC_RECODE_CHAIN_INFO(x) ((x) << 19)
#if (TME_RECODE_CHAIN_INFO_UNCONDITIONAL | TME_RECODE_CHAIN_INFO_CONDITIONAL | TME_RECODE_CHAIN_INFO_NEAR | TME_RECODE_CHAIN_INFO_FAR | TME_RECODE_CHAIN_INFO_JUMP | TME_RECODE_CHAIN_INFO_RETURN | TME_RECODE_CHAIN_INFO_CALL) > 0x1f
#error "TME_RECODE_CHAIN_INFO_ values changed"
#endif
/* these are composite values used in
_tme_sparc_recode_insn_opmap[]: */
/* this sparc instruction needs an assist: */
#define TME_SPARC_RECODE_INSN_ASSIST \
(TME_RECODE_OPCODE_GUEST \
| TME_SPARC_RECODE_INSN_UPDATE_INSN)
/* this sparc instruction needs an assist, and can trap: */
#define TME_SPARC_RECODE_INSN_ASSIST_CANTRAP \
(TME_SPARC_RECODE_INSN_ASSIST \
| TME_SPARC_RECODE_INSN_UPDATE_PCS)
/* this sparc instruction needs an assist, can trap, and the assist
function does not need source operands from the rs1 and rs2/simm13
fields: */
#define TME_SPARC_RECODE_INSN_ASSIST_FULL \
(TME_SPARC_RECODE_INSN_ASSIST_CANTRAP \
| TME_SPARC_RECODE_INSN_NO_RS2 \
| TME_SPARC_RECODE_INSN_NO_RS1)
/* this sparc instruction is undefined: */
#define TME_SPARC_RECODE_INSN_UNDEF TME_SPARC_RECODE_INSN_ASSIST_FULL
/* this sparc instruction is a load: */
#define TME_SPARC_RECODE_INSN_LD \
(TME_SPARC_RECODE_INSN_UPDATE_PCS \
| TME_RECODE_OPCODE_RW)
/* this sparc instruction is a store: */
#define TME_SPARC_RECODE_INSN_ST \
(TME_SPARC_RECODE_INSN_UPDATE_PCS \
| TME_RECODE_OPCODE_RW)
/* this sparc instruction is a load alternate: */
#define TME_SPARC_RECODE_INSN_LDA \
(TME_SPARC_VERSION(ic) < 9 \
? TME_SPARC_RECODE_INSN_ASSIST_CANTRAP \
: TME_SPARC_RECODE_INSN_LD)
/* rename various things by the architecture size: */
#define tme_sparc_idle_type_pc _TME_SPARC_RECODE_SIZE(tme_sparc_idle_type_pc,/**/)
#define _tme_sparc_recode_insn_opmap _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_insn_opmap)
#define _tme_sparc_execute_opmap _TME_SPARC_RECODE_SIZE(_tme_sparc,_execute_opmap)
#define tme_sparc_recode_insn_current _TME_SPARC_RECODE_SIZE(tme_sparc,_recode_insn_current)
#define tme_sparc_recode_insn_assist_redispatch _TME_SPARC_RECODE_SIZE(tme_sparc,_recode_insn_assist_redispatch)
#define _tme_sparc_recode_insn_assist _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_insn_assist)
#define _tme_sparc_recode_insn_assist_full _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_insn_assist_full)
#define _tme_sparc_recode_insn_assist_jmpl _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_insn_assist_jmpl)
#define _tme_sparc_recode_insn_assist_unimpl _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_insn_assist_unimpl)
#define _tme_sparc_recode_insns_total _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_insns_total)
#define _tme_sparc_recode_insn_branch_delay_bad _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_insn_branch_delay_bad)
#define _tme_sparc_recode_recode _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_recode)
#endif /* TME_SPARC_RECODE_SIZE(ic) == TME_RECODE_SIZE_32 */
/* this maps format three opcodes into sparc recode controls: */
static const tme_uint16_t _tme_sparc_recode_insn_opmap[128] = {
/* op=2: arithmetic, logical, shift, and remaining: */
/* 000000 */ TME_RECODE_OPCODE_ADD,
/* 000001 */ TME_RECODE_OPCODE_AND,
/* 000010 */ TME_RECODE_OPCODE_OR,
/* 000011 */ TME_RECODE_OPCODE_XOR,
/* 000100 */ TME_RECODE_OPCODE_SUB,
/* 000101 */ TME_RECODE_OPCODE_ANDN,
/* 000110 */ TME_RECODE_OPCODE_ORN,
/* 000111 */ TME_RECODE_OPCODE_XORN,
/* 001000 */ TME_RECODE_OPCODE_ADDC | TME_SPARC_RECODE_INSN_DEFC,
#if TME_SPARC_VERSION(ic) < 9
/* 001001 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 101001 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: mulx */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 001010 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* umul */
/* 001011 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* smul */
/* 001100 */ TME_RECODE_OPCODE_SUBC | TME_SPARC_RECODE_INSN_DEFC,
#if TME_SPARC_VERSION(ic) < 9
/* 001101 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 001101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: udivx */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 001110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* udiv */
/* 001111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* sdiv */
/* 010000 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_ADD,
/* 010001 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_AND,
/* 010010 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_OR,
/* 010011 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_XOR,
/* 010100 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_SUB,
/* 010101 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_ANDN,
/* 010110 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_ORN,
/* 010111 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_XORN,
/* 011000 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_ADDC | TME_SPARC_RECODE_INSN_DEFC,
/* 011001 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 011010 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* umulcc */
/* 011011 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* smulcc */
/* 011100 */ TME_SPARC_RECODE_INSN_CC | TME_RECODE_OPCODE_SUBC | TME_SPARC_RECODE_INSN_DEFC,
/* 011101 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 011110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* udivcc */
/* 011111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* sdivcc */
/* 100000 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* taddcc */
/* 100001 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* tsubcc */
/* 100010 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* taddcctv */
/* 100011 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* tsubcctv */
/* 100100 */ TME_SPARC_RECODE_INSN_ASSIST, /* mulscc */
/* 100101 */ TME_RECODE_OPCODE_SHLL | TME_SPARC_RECODE_INSN_SHIFT,
/* 100110 */ TME_RECODE_OPCODE_SHRL | TME_SPARC_RECODE_INSN_SHIFT,
/* 100111 */ TME_RECODE_OPCODE_SHRA | TME_SPARC_RECODE_INSN_SHIFT,
/* 101000 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* rdasr */
#if TME_SPARC_VERSION(ic) < 9
/* 101001 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* rdpsr */
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 101001 */ TME_SPARC_RECODE_INSN_UNDEF,
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 101010 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* rdwim (v9: rdpr) */
/* 101011 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* rdtbr (v9: flushw) */
#if TME_SPARC_VERSION(ic) < 9
/* 101100 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101101 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101110 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101111 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 101100 */ 0, /* v9: movcc (handled specially) */
/* 101101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: sdivx */
/* 101110 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP, /* v9: popc */
/* 101111 */ 0, /* v9: movr (handled specially) */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 110000 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP | TME_SPARC_RECODE_INSN_NO_RD, /* wrasr */
#if TME_SPARC_VERSION(ic) < 9
/* 110001 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP | TME_SPARC_RECODE_INSN_NO_RD, /* wrpsr */
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 110001 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: saved/restored */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 110010 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP | TME_SPARC_RECODE_INSN_NO_RD, /* wrwim (v9: wrpr) */
#if TME_SPARC_VERSION(ic) < 9
/* 110011 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP | TME_SPARC_RECODE_INSN_NO_RD, /* wrtbr */
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 110011 */ TME_SPARC_RECODE_INSN_UNDEF,
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 110100 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* fpop1 */
/* 110101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* fpop2 */
/* 110110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* cpop1 (v9: impdep1) */
/* 110111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* cpop2 (v9: impdep2) */
/* 111000 */ 0, /* jmpl (handled specially) */
/* 111001 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_LAST, /* rett (v9: return) */
/* 111010 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP | TME_SPARC_RECODE_INSN_NO_RD, /* ticc */
/* 111011 */ TME_SPARC_RECODE_INSN_ASSIST | TME_SPARC_RECODE_INSN_NO_RD, /* flush */
/* 111100 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* save */
/* 111101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* restore */
#if TME_SPARC_VERSION(ic) < 9
/* 111110 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 111110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_LAST, /* v9: done/retry */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 111111 */ TME_SPARC_RECODE_INSN_UNDEF,
/* op=3: memory instructions: */
/* 000000 */ TME_SPARC_RECODE_INSN_LD, /* ld (v9: lduw) */
/* 000001 */ TME_SPARC_RECODE_INSN_LD, /* ldub */
/* 000010 */ TME_SPARC_RECODE_INSN_LD, /* lduh */
/* 000011 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldd */
/* 000100 */ TME_SPARC_RECODE_INSN_ST, /* st (v9: stw) */
/* 000101 */ TME_SPARC_RECODE_INSN_ST, /* stb */
/* 000110 */ TME_SPARC_RECODE_INSN_ST, /* sth */
/* 000111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_NO_RD, /* std */
#if TME_SPARC_VERSION(ic) < 9
/* 001000 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 001000 */ TME_SPARC_RECODE_INSN_LD, /* v9: ldsw */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 001001 */ TME_SPARC_RECODE_INSN_LD, /* ldsb */
/* 001010 */ TME_SPARC_RECODE_INSN_LD, /* ldsh */
#if TME_SPARC_VERSION(ic) < 9
/* 001011 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 001011 */ TME_SPARC_RECODE_INSN_LD, /* v9: ldx */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 001100 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 001101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldstub */
#if TME_SPARC_VERSION(ic) < 9
/* 001110 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 001110 */ TME_SPARC_RECODE_INSN_ST, /* v9: stx */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 001111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* swap */
/* 010000 */ TME_SPARC_RECODE_INSN_LDA, /* lda (v9: lduwa) */
/* 010001 */ TME_SPARC_RECODE_INSN_LDA, /* lduba */
/* 010010 */ TME_SPARC_RECODE_INSN_LDA, /* lduha */
/* 010011 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldda */
/* 010100 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_NO_RD, /* sta (v9: stwa) */
/* 010101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_NO_RD, /* stba */
/* 010110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_NO_RD, /* stha */
/* 010111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_NO_RD, /* stda */
#if TME_SPARC_VERSION(ic) < 9
/* 011000 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 011000 */ TME_SPARC_RECODE_INSN_LDA, /* v9: ldswa*/
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 011001 */ TME_SPARC_RECODE_INSN_LDA, /* ldsba */
/* 011010 */ TME_SPARC_RECODE_INSN_LDA, /* ldsha */
#if TME_SPARC_VERSION(ic) < 9
/* 011011 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 001011 */ TME_SPARC_RECODE_INSN_LDA, /* v9: ldxa*/
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 011100 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 011101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldstuba */
#if TME_SPARC_VERSION(ic) < 9
/* 011110 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 001110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_NO_RD, /* v9: stxa */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 011111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* swapa */
/* 100000 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldf */
/* 100001 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldfsr */
#if TME_SPARC_VERSION(ic) < 9
/* 100010 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 100010 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: ldqf */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 100011 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* lddf */
/* 100100 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stf */
/* 100101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stfsr */
/* 100110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stdfq */
/* 100111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stdf */
/* 101000 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101001 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101010 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101011 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101100 */ TME_SPARC_RECODE_INSN_UNDEF,
#if TME_SPARC_VERSION(ic) < 9
/* 101101 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 101101 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP | TME_SPARC_RECODE_INSN_NO_RD, /* v9: prefetch */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 101110 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 101111 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 110000 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldc (v9: ldfa) */
#if TME_SPARC_VERSION(ic) < 9
/* 110001 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* ldcsr */
/* 110010 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 110001 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 110010 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: ldqfa */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 110011 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* lddc (v9: lddfa) */
/* 110100 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stc (v9: stfa) */
#if TME_SPARC_VERSION(ic) < 9
/* 110101 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stcsr */
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 110101 */ TME_SPARC_RECODE_INSN_UNDEF,
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 110110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stdcq (v9: stqfa) */
/* 110111 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* stdc (v9: stdfa) */
/* 111000 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 111001 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 111010 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 111011 */ TME_SPARC_RECODE_INSN_UNDEF,
#if TME_SPARC_VERSION(ic) < 9
/* 111100 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 111101 */ TME_SPARC_RECODE_INSN_UNDEF,
/* 111110 */ TME_SPARC_RECODE_INSN_UNDEF,
#else /* TME_SPARC_VERSION(ic) >= 9 */
/* 111100 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: casa */
/* 111101 */ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP | TME_SPARC_RECODE_INSN_NO_RD, /* v9: prefetch */
/* 111110 */ TME_SPARC_RECODE_INSN_ASSIST_FULL, /* v9: casxa */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* 111111 */ TME_SPARC_RECODE_INSN_UNDEF,
};
/* this returns the current instruction: */
tme_uint32_t
tme_sparc_recode_insn_current(const struct tme_sparc *ic)
{
const struct tme_token *itlb_current_token;
const struct _TME_SPARC_RECODE_SIZE(tme_recode_tlb_c16_a,/**/) *recode_itlb_current;
const struct tme_sparc_tlb *itlb_current;
tme_uint32_t page_offset;
const tme_shared tme_uint32_t *_sparc_insn;
tme_uint32_t sparc_insn;
/* in a cooperative threading system, the current ITLB entry must be
valid. in a noncooperative threading system, some master may be
in the process of invalidating the current ITLB entry: */
itlb_current_token = ic->_tme_sparc_itlb_current_token;
assert (!TME_THREADS_COOPERATIVE || tme_token_is_valid(itlb_current_token));
/* get the current recode ITLB entry: */
recode_itlb_current
= ((struct _TME_SPARC_RECODE_SIZE(tme_recode_tlb_c16_a,/**/) *)
(((const char *) itlb_current_token)
- (((char *) &(((struct _TME_SPARC_RECODE_SIZE(tme_recode_tlb_c16_a,/**/) *) 0)->_TME_SPARC_RECODE_SIZE(tme_recode_tlb_c16_a,_token)))
- (char *) 0)));
/* the current ITLB entry must cover the current context,
instruction ASI and PC, and allow fast reading: */
itlb_current = &ic->tme_sparc_tlbs[(recode_itlb_current - &ic->_TME_SPARC_RECODE_SIZE(tme_sparc_recode_tlb,s)[0])];
assert ((itlb_current->tme_sparc_tlb_context > ic->tme_sparc_memory_context_max
|| itlb_current->tme_sparc_tlb_context == ic->tme_sparc_memory_context_default)
&& TME_SPARC_TLB_ASI_MASK_OK(itlb_current, ic->tme_sparc_asi_mask_insn)
&& itlb_current->tme_sparc_tlb_addr_first <= ic->tme_sparc_ireg(TME_SPARC_IREG_PC)
&& ic->tme_sparc_ireg(TME_SPARC_IREG_PC) <= itlb_current->tme_sparc_tlb_addr_last
&& itlb_current->tme_sparc_tlb_emulator_off_read != TME_EMULATOR_OFF_UNDEF);
/* fetch the instruction: */
page_offset = recode_itlb_current->_TME_SPARC_RECODE_SIZE(tme_recode_tlb_c16_a,_page);
page_offset ^= (tme_uint32_t) ic->tme_sparc_ireg(TME_SPARC_IREG_PC);
assert ((page_offset >> ic->tme_sparc_tlb_page_size_log2) == 0);
_sparc_insn
= ((const tme_shared tme_uint32_t *)
(recode_itlb_current->_TME_SPARC_RECODE_SIZE(tme_recode_tlb_c16_a,_memory)
+ page_offset));
sparc_insn
= tme_memory_bus_read32(_sparc_insn,
(tme_rwlock_t *) NULL,
sizeof(tme_uint32_t),
sizeof(tme_sparc_ireg_t));
sparc_insn = tme_betoh_u32(sparc_insn);
return (sparc_insn);
}
/* this checks for conditions that require a redispatch out of recode
instruction thunks: */
tme_recode_uguest_t
tme_sparc_recode_insn_assist_redispatch(struct tme_sparc *ic)
{
int redispatch_required;
/* if the instruction TLB entry for this PC is invalid, or doesn't
cover the PC and context and ASI, or doesn't allow fast reading,
or isn't covered by any cacheable, or the page containing this PC
isn't still cache-valid (i.e., its contents have changed since we
cached recoded instructions for it), a redispatch is required: */
/* NB: we check for the current PC, even though it's for an
instruction that has already completed.
if PC and PC_next are on the same page, this does the right
thing. if PC and PC_next are on different pages, and the PC page
requires a redispatch but the PC_next page does not, this will
cause a needless redispatch. if PC and PC_next are on different
pages, and the PC page doesn't require a redispatch but the
PC_next page does, that will get handled by either
_TME_SPARC_EXECUTE_NAME()/tme_sparc_recode() or the host chain
out/chain in, because the current instructions thunk can't
cover both PC and PC_next.
so, checking for the current PC never does the wrong thing, and
is useful for at least _tme_sparc_recode_ls_assist_ld(), which
must redispatch after executing a ld instruction that might be
different from the ld instruction at the time the instructions
thunk was recoded - because if the destination register has been
changed in the new instruction, if
_tme_sparc_recode_ls_assist_ld() did return to the instructions
thunk, it would incorrectly take the return value for the
destination register in the original instruction: */
redispatch_required
= (_tme_sparc_recode_chain_src_key(ic,
ic->tme_sparc_ireg(TME_SPARC_IREG_PC))
== TME_SPARC_RECODE_SRC_KEY_UNDEF);
/* if the instruction burst has been set to zero by a
TME_BUS_CYCLE_SYNCHRONOUS_EVENT, a redispatch is required: */
redispatch_required |= (ic->_tme_sparc_instruction_burst_remaining == 0);
/* if a redispatch is required: */
if (__tme_predict_false(redispatch_required)) {
/* do the redispatch: */
tme_sparc_redispatch(ic);
}
return (0);
}
/* the assist functions for format three instructions reference the
opmap in struct tme_sparc: */
/* the assist function for TME_SPARC_RECODE_INSN_ASSIST and
TME_SPARC_RECODE_INSN_ASSIST_CANTRAP instructions: */
static tme_recode_uguest_t
_tme_sparc_recode_insn_assist(struct tme_ic *_ic,
tme_recode_uguest_t rs1,
tme_recode_uguest_t rs2)
{
struct tme_sparc *ic;
tme_uint32_t sparc_insn;
tme_uint32_t sparc_opcode;
#if TME_RECODE_SIZE_GUEST_MAX > TME_SPARC_RECODE_SIZE(ic)
tme_sparc_ireg_t rs1_buffer;
tme_sparc_ireg_t rs2_buffer;
#endif /* TME_RECODE_SIZE_GUEST_MAX > TME_SPARC_RECODE_SIZE(ic) */
tme_sparc_ireg_t *_rs1;
tme_sparc_ireg_t *_rs2;
tme_sparc_ireg_t rd;
/* recover our ic: */
ic = (struct tme_sparc *) _ic;
TME_SPARC_STAT(ic, tme_sparc_stats_recode_assist);
/* set PC_next_next from PC_next: */
/* NB that this sets PC_next_next to garbage unless this instruction
was a TME_SPARC_RECODE_INSN_ASSIST_CANTRAP: */
ic->tme_sparc_ireg(TME_SPARC_IREG_PC_NEXT_NEXT)
= (ic->tme_sparc_ireg(TME_SPARC_IREG_PC_NEXT)
+ sizeof(tme_uint32_t));
/* copy the instruction from the internal register into its normal
position: */
sparc_insn = ic->tme_sparc_ireg(TME_SPARC_IREG_INSN);
ic->_tme_sparc_insn = sparc_insn;
/* form the opcode index: */
sparc_opcode = TME_FIELD_MASK_EXTRACTU(sparc_insn, (0x3f << 19));
sparc_opcode += ((sparc_insn >> (30 - 6)) & 0x40);
TME_SPARC_STAT(ic, tme_sparc_stats_recode_assist_opcode[sparc_opcode]);
/* run the instruction: */
#if TME_RECODE_SIZE_GUEST_MAX > TME_SPARC_RECODE_SIZE(ic)
rs1_buffer = rs1;
rs2_buffer = rs2;
_rs1 = &rs1_buffer;
_rs2 = &rs2_buffer;
#else /* TME_RECODE_SIZE_GUEST_MAX == TME_SPARC_RECODE_SIZE(ic) */
_rs1 = &rs1;
_rs2 = &rs2;
#endif /* TME_RECODE_SIZE_GUEST_MAX == TME_SPARC_RECODE_SIZE(ic) */
#ifdef _TME_SPARC_RECODE_VERIFY
rd = 0;
#endif /* _TME_SPARC_RECODE_VERIFY */
(*ic->_tme_sparc_execute_opmap[sparc_opcode])
(ic, _rs1, _rs2, &rd);
/* return the result: */
return (rd);
}
/* the assist function for TME_SPARC_RECODE_INSN_ASSIST_FULL
instructions: */
static tme_recode_uguest_t
_tme_sparc_recode_insn_assist_full(struct tme_ic *_ic,
tme_recode_uguest_t _rs1,
tme_recode_uguest_t _rs2)
{
struct tme_sparc *ic;
tme_uint32_t sparc_insn;
unsigned int reg_rs1;
unsigned int reg_rs2;
unsigned int reg_rd;
tme_uint32_t sparc_opcode;
/* recover our ic: */
ic = (struct tme_sparc *) _ic;
TME_SPARC_STAT(ic, tme_sparc_stats_recode_assist_full);
/* set PC_next_next from PC_next: */
ic->tme_sparc_ireg(TME_SPARC_IREG_PC_NEXT_NEXT)
= (ic->tme_sparc_ireg(TME_SPARC_IREG_PC_NEXT)
+ sizeof(tme_uint32_t));
/* copy the instruction from the internal register into its normal
position: */
sparc_insn = ic->tme_sparc_ireg(TME_SPARC_IREG_INSN);
ic->_tme_sparc_insn = sparc_insn;
/* if the i bit is zero: */
if (__tme_predict_true((sparc_insn & TME_BIT(13)) == 0)) {
/* decode rs2: */
reg_rs2 = TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS2);
TME_SPARC_REG_INDEX(ic, reg_rs2);
}
/* otherwise, the i bit is one: */
else {
/* decode simm13: */
ic->tme_sparc_ireg(TME_SPARC_IREG_TMP(0)) = TME_FIELD_MASK_EXTRACTS(sparc_insn, (tme_sparc_ireg_t) 0x1fff);
reg_rs2 = TME_SPARC_IREG_TMP(0);
}
/* decode rs1: */
reg_rs1 = TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS1);
TME_SPARC_REG_INDEX(ic, reg_rs1);
/* decode rd: */
reg_rd = TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RD);
TME_SPARC_REG_INDEX(ic, reg_rd);
/* form the opcode index: */
sparc_opcode = TME_FIELD_MASK_EXTRACTU(sparc_insn, (0x3f << 19));
sparc_opcode += ((sparc_insn >> (30 - 6)) & 0x40);
TME_SPARC_STAT(ic, tme_sparc_stats_recode_assist_opcode[sparc_opcode]);
/* run the instruction: */
(*ic->_tme_sparc_execute_opmap[sparc_opcode])
(ic,
&ic->tme_sparc_ireg(reg_rs1),
&ic->tme_sparc_ireg(reg_rs2),
&ic->tme_sparc_ireg(reg_rd));
/* set %g0 to zero, in case the instruction changed it: */
ic->tme_sparc_ireg(TME_SPARC_G0_OFFSET(ic) + TME_SPARC_IREG_G0) = 0;
/* return no result */
return (tme_sparc_recode_insn_assist_redispatch(ic));
/* unused: */
_rs1 = 0;
_rs2 = 0;
}
/* the assist function for jmpl instructions: */
static tme_recode_uguest_t
_tme_sparc_recode_insn_assist_jmpl(struct tme_ic *_ic,
tme_recode_uguest_t rs1,
tme_recode_uguest_t rs2)
{
struct tme_sparc *ic;
tme_sparc_ireg_t pc_next_next;
tme_uint32_t ls_faults;
/* recover our ic: */
ic = (struct tme_sparc *) _ic;
TME_SPARC_STAT(ic, tme_sparc_stats_recode_assist_opcode[0x38 /* jmpl */]);
/* get the target address: */
pc_next_next = ((tme_sparc_ireg_t) rs1) + (tme_sparc_ireg_t) rs2;
/* assume that the target address will not fault: */
ls_faults = TME_SPARC_LS_FAULT_NONE;
#if TME_SPARC_VERSION(ic) >= 9
/* mask the target address: */
pc_next_next &= ic->tme_sparc_address_mask;
/* if the target address is in a virtual address hole: */
if (__tme_predict_false((pc_next_next
+ ic->tme_sparc64_ireg_va_hole_start)
> ((ic->tme_sparc64_ireg_va_hole_start * 2) - 1))) {
/* the target address is out of range: */
ls_faults += TME_SPARC64_LS_FAULT_VA_RANGE_NNPC;
}
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* if the target address is not 32-bit aligned: */
if (__tme_predict_false((((tme_uint32_t) pc_next_next) % sizeof(tme_uint32_t)) != 0)) {
/* the target address is misaligned: */
ls_faults += TME_SPARC_LS_FAULT_ADDRESS_NOT_ALIGNED;
}
/* if the target address has faulted: */
if (__tme_predict_false(ls_faults != TME_SPARC_LS_FAULT_NONE)) {
/* set PC_next from PC: */
/* NB: we don't need to set PC_next_next, because we're
faulting: */
ic->tme_sparc_ireg(TME_SPARC_IREG_PC_NEXT)
= (ic->tme_sparc_ireg(TME_SPARC_IREG_PC)
+ sizeof(tme_uint32_t));
/* in case a CPU-specific trap function needs it, store a dummy
jmpl instruction: */
ic->_tme_sparc_insn
= ((((tme_uint32_t) 2) << 30)
+ (0x38 << 19) /* jmpl */
);
/* fault: */
tme_sparc_nnpc_trap(ic, ls_faults);
}
/* return the target address: */
return (pc_next_next);
}
/* the assist function for unimplemented instructions: */
static tme_recode_uguest_t
_tme_sparc_recode_insn_assist_unimpl(struct tme_ic *_ic,
tme_recode_uguest_t _rs1,
tme_recode_uguest_t _rs2)
{
struct tme_sparc *ic;
/* recover our ic: */
ic = (struct tme_sparc *) _ic;
/* set PC_next_next from PC_next: */
ic->tme_sparc_ireg(TME_SPARC_IREG_PC_NEXT_NEXT)
= (ic->tme_sparc_ireg(TME_SPARC_IREG_PC_NEXT)
+ sizeof(tme_uint32_t));
/* trap: */
#if TME_SPARC_VERSION(ic) < 9
tme_sparc32_trap(ic, TME_SPARC32_TRAP_illegal_instruction);
#else /* TME_SPARC_VERSION(ic) >= 9 */
tme_sparc64_trap(ic, TME_SPARC64_TRAP_illegal_instruction);
#endif /* TME_SPARC_VERSION(ic) >= 9 */
/* return no result */
return (0);
/* unused: */
_rs1 = 0;
_rs2 = 0;
}
#ifdef _TME_SPARC_STATS
static tme_recode_uguest_t
_tme_sparc_recode_insns_total(struct tme_ic *_ic,
tme_recode_uguest_t _rs1,
tme_recode_uguest_t _rs2)
{
TME_SPARC_STAT_N(((struct tme_sparc *) _ic),
tme_sparc_stats_recode_insns_total,
(tme_uint32_t) _rs2);
return (0);
}
#endif /* _TME_SPARC_STATS */
/* this returns nonzero if the given instruction can't be recoded in a
branch delay slot. in general, an instruction can't be recoded in
a branch delay slot if it may set PC_next_next without doing a
redispatch, or if it may use a recode if/endif: */
static int
_tme_sparc_recode_insn_branch_delay_bad(tme_uint32_t sparc_insn)
{
unsigned int sparc_opcode;
/* if this is a format three instruction, and op is three: */
if (sparc_insn >= 0xc0000000) {
/* all of the instructions with op equal to three can be recoded
in a branch delay slot: */
return (FALSE);
}
/* otherwise, if this is a format three instruction, and op is
two: */
else if (sparc_insn >= 0x80000000) {
/* form the opcode index: */
sparc_opcode = TME_FIELD_MASK_EXTRACTU(sparc_insn, (0x3f << 19));
/* instructions that may set PC_next_next without doing a
redispatch, and instructions that may use a recode if/endif
can't be recoded in a branch delay slot: */
return (sparc_opcode == 0x38 /* jmpl */
|| (TME_SPARC_VERSION(ic) >= 9
&& (sparc_opcode == 0x2c /* v9: movcc */
|| sparc_opcode == 0x2f))); /* v9: movr */
}
/* otherwise, if this is a format two instruction: */
else if (__tme_predict_true(sparc_insn < 0x40000000)) {
/* except for sethi instructions, format two instructions can't be
recoded in a branch delay slot - the other implemented format
two instructions are all branches that set PC_next_next: */
return ((sparc_insn & (0x7 << 22)) != (0x4 << 22));
}
/* otherwise, this is a format three instruction: */
else {
/* we can't recode call instructions in branch delay slots, since
they set PC_next_next: */
return (TRUE);
}
}
/* the sparc instruction recoder: */
static tme_recode_thunk_off_t
_tme_sparc_recode_recode(struct tme_sparc *ic,
const struct tme_sparc_tlb *itlb)
{
tme_uint32_t page_size;
unsigned int reg_guest_pc;
unsigned int recode_size_address;
struct tme_recode_insn *recode_insn;
struct tme_recode_insn *recode_insns_last_start;
tme_sparc_ireg_t pc_next;
tme_uint32_t sparc_insn_next;
tme_uint32_t sparc_insn_count;
tme_uint32_t pc_advance;
tme_uint32_t sparc_recode;
tme_sparc_ireg_t itlb_addr_last;
tme_recode_uguest_t (*sparc_assist) _TME_P((struct tme_ic *, tme_recode_uguest_t, tme_recode_uguest_t));
tme_uint32_t sparc_insn;
tme_uint32_t sparc_opcode;
int recode_operand;
tme_sparc_ireg_t imm;
unsigned int shift_count_mask;
unsigned int cond;
const struct tme_recode_conds_thunk *conds_thunk;
tme_int32_t branch_displacement_raw;
tme_sparc_ireg_t branch_displacement;
const struct tme_recode_flags_thunk *flags_thunk;
int recode_operand_address;
tme_uint32_t chain_info;
/* get the page size: */
page_size = (((tme_uint32_t) 1) << ic->tme_sparc_tlb_page_size_log2);
/* on entry to this function, PC has already been updated to be the
first instruction. on entry to an instructions thunk, PC hasn't
been updated yet, and PC_next is the first instruction: */
reg_guest_pc = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC_NEXT);
/* assume that addresses are guest size: */
recode_size_address = TME_SPARC_RECODE_SIZE(ic);
/* if this is a v9 CPU and PSTATE.AM is set: */
if (TME_SPARC_VERSION(ic) >= 9
&& (ic->tme_sparc64_ireg_pstate
& TME_SPARC64_PSTATE_AM)) {
/* addresses are only 32 bits wide: */
recode_size_address = TME_RECODE_SIZE_32;
}
/* start at the beginning of the recode instructions buffer: */
recode_insn = &ic->tme_sparc_recode_insns[0];
/* calculate the worst-case position in the recode instructions
buffer, past which we may not be able to recode another sparc
instruction. this is actually an upper bound on the worst case,
found by counting the number of "recode_insn++" statements in
this function, and multiplying that by two so that branch
instructions can loop to recode a branch delay slot: */
recode_insns_last_start = &ic->tme_sparc_recode_insns[TME_ARRAY_ELS(ic->tme_sparc_recode_insns) - (35 * 2)];
/* fetch the first instruction as the next instruction: */
pc_next = ic->tme_sparc_ireg(TME_SPARC_IREG_PC);
sparc_insn_next
= tme_memory_bus_read32(((const tme_shared tme_uint32_t *)
(itlb->tme_sparc_tlb_emulator_off_read
+ pc_next)),
itlb->tme_sparc_tlb_bus_rwlock,
sizeof(tme_uint32_t),
sizeof(tme_sparc_ireg_t));
sparc_insn_next = tme_betoh_u32(sparc_insn_next);
sparc_insn_count = 0;
/* the next instruction will be the first instruction: */
pc_advance = 0 - (tme_uint32_t) sizeof(sparc_insn_next);
/* clear the recode information: */
sparc_recode = 0;
/* if PC is at the last instruction address before tme_sparc_ireg_t
wrapping: */
/* NB: we don't consider the v9 address mask here, because we only
need to protect against the pc_next local variable itself
wrapping its tme_sparc_ireg_t. if the v9 address mask is set for
32 bits and PC is 0xfffffffc, pc_next will not wrap to zero -
instead it will advance to 0x100000000, but we also won't
continue to recode because that address is on a different
page: */
assert (TME_SPARC_VERSION(ic) < 9
|| (pc_next < ic->tme_sparc_address_mask));
if (__tme_predict_false(pc_next
== (tme_sparc_ireg_t) (0 - (tme_sparc_ireg_t) sizeof(tme_uint32_t)))) {
/* recode is impossible here: */
return (0);
}
/* stop recoding after the last instruction on the page, or right
before the last instruction before tme_sparc_ireg_t wrapping,
whichever comes first: */
itlb_addr_last
= ((pc_next
| (page_size - 1))
- (sizeof(tme_uint32_t) - 1));
if (__tme_predict_false(itlb_addr_last
== (tme_sparc_ireg_t) (0 - (tme_sparc_ireg_t) sizeof(tme_uint32_t)))) {
itlb_addr_last -= sizeof(tme_uint32_t);
}
assert (itlb_addr_last >= pc_next);
assert (itlb->tme_sparc_tlb_addr_last >= itlb_addr_last);
/* loop recoding instructions: */
for (;;) {
/* reset the assist function: */
sparc_assist = _tme_sparc_recode_insn_assist;
/* make the next instruction the current instruction: */
pc_advance += sizeof(sparc_insn);
sparc_insn = sparc_insn_next;
sparc_insn_count++;
/* advance the PC of the next instruction: */
pc_next += sizeof(sparc_insn);
/* if the PC of the next instruction is still in this
page: */
if (__tme_predict_true(pc_next <= itlb_addr_last)) {
/* fetch the next instruction: */
sparc_insn_next
= tme_memory_bus_read32(((const tme_shared tme_uint32_t *)
(itlb->tme_sparc_tlb_emulator_off_read
+ pc_next)),
itlb->tme_sparc_tlb_bus_rwlock,
sizeof(tme_uint32_t),
sizeof(tme_sparc_ireg_t));
sparc_insn_next = tme_betoh_u32(sparc_insn_next);
}
/* otherwise, the instruction TLB entry doesn't cover the PC of
the next instruction: */
else {
/* the current instruction is the last instruction, and we need
to update the PC registers: */
sparc_recode |= TME_SPARC_RECODE_INSN_LAST | TME_SPARC_RECODE_INSN_UPDATE_PCS;
/* in case the current instruction is a control transfer
instruction, poison the next instruction to look like another
control transfer instruction. we use a call instruction: */
sparc_insn_next = 0x40000000;
}
/* if this is a format three instruction (op is two or three): */
if (__tme_predict_true(sparc_insn >= 0x80000000)) {
/* form the opcode index: */
sparc_opcode = TME_FIELD_MASK_EXTRACTU(sparc_insn, (0x3f << 19));
sparc_opcode += ((sparc_insn >> (30 - 6)) & 0x40);
/* add in the recode information for this instruction: */
sparc_recode |= _tme_sparc_recode_insn_opmap[sparc_opcode];
/* if this is a jmpl instruction: */
if (__tme_predict_false(sparc_opcode == 0x38)) {
/* if the next instruction can't be recoded when it's in a
branch delay slot (usually because it's also a control
transfer instruction): */
if (__tme_predict_false(_tme_sparc_recode_insn_branch_delay_bad(sparc_insn_next))) {
/* if we haven't recoded any instructions yet, recode is
impossible here: */
if (recode_insn == &ic->tme_sparc_recode_insns[0]) {
return (0);
}
/* stop recoding immediately, leaving the updated PC
pointing to the instruction before the jmpl: */
assert (pc_advance >= sizeof(sparc_insn));
pc_advance -= sizeof(sparc_insn);
sparc_insn_count--;
sparc_recode
|= (TME_SPARC_RECODE_INSN_UPDATE_PCS
| TME_SPARC_RECODE_INSN_NONE
| TME_SPARC_RECODE_INSN_LAST);
}
/* otherwise, the next instruction can be recoded when it's in
a branch delay slot: */
else {
/* if PC doesn't already point to the jmpl instruction: */
if (pc_advance != 0
|| reg_guest_pc != TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC)) {
/* advance PC to the jmpl instruction: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = reg_guest_pc;
recode_insn->tme_recode_insn_operand_src[1]
= (pc_advance == 0
? TME_RECODE_OPERAND_ZERO
: TME_RECODE_OPERAND_IMM);
recode_insn->tme_recode_insn_imm_uguest = pc_advance;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
/* if rs1 is %i7 or %o7, assume that this jmpl is doing a
return, otherwise assume that this jmpl is doing a
call. either way, the branch delay slot will be the
last instruction: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS1));
sparc_recode
|= ((((recode_operand
- TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0))
& 15 /* %o7 */)
== 15 /* %o7 */)
? (TME_SPARC_RECODE_INSN_LAST
+ TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_RETURN
+ TME_RECODE_CHAIN_INFO_FAR
+ TME_RECODE_CHAIN_INFO_UNCONDITIONAL))
: (TME_SPARC_RECODE_INSN_LAST
+ TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_CALL
+ TME_RECODE_CHAIN_INFO_FAR
+ TME_RECODE_CHAIN_INFO_UNCONDITIONAL)));
/* call the jmpl assist function. if this doesn't trap, it
returns the target address for PC_next_next, which we put
directly into PC_next: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_GUEST;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0]
= (recode_operand == TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0)
? TME_RECODE_OPERAND_ZERO
: recode_operand);
if (__tme_predict_true(sparc_insn & TME_BIT(13))) {
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest
= TME_FIELD_MASK_EXTRACTS(sparc_insn, (tme_sparc_ireg_t) 0x1fff);
}
else {
recode_insn->tme_recode_insn_operand_src[1]
= TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS2));
}
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC_NEXT);
recode_insn->tme_recode_insn_guest_func = _tme_sparc_recode_insn_assist_jmpl;
recode_insn++;
/* assume that rd is %g0: */
reg_guest_pc = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
/* decode rd: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RD));
/* if rd is not %g0: */
if (recode_operand != TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0)) {
/* we will soon be able to find the PC of the jmpl in rd: */
reg_guest_pc = recode_operand;
/* write the PC of the jmpl into the destination register: */
/* NB: we don't use recode_size_address here because we need to
update the entire destination register: */
recode_insn->tme_recode_insn_operand_dst = recode_operand;
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
/* advance PC to the branch delay slot: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = reg_guest_pc;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest = sizeof(sparc_insn);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
pc_advance = 0 - (tme_uint32_t) sizeof(sparc_insn);
reg_guest_pc = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
/* loop now to fetch and recode the instruction in the branch
delay slot: */
continue;
}
}
/* otherwise, if this is a v9 movcc instruction: */
else if (__tme_predict_false(TME_SPARC_VERSION(ic) >= 9
&& sparc_opcode == 0x2c)) {
/* if cc2 is clear, or if cc1 is set, this is not an integer
movcc: */
if (__tme_predict_false((sparc_insn
& (TME_BIT(18)
| TME_BIT(11)))
!= TME_BIT(18))) {
/* this instruction needs a full assist: */
sparc_recode |= TME_SPARC_RECODE_INSN_ASSIST_FULL;
}
/* otherwise, this is an integer movcc: */
else {
/* get the condition field: */
cond = TME_FIELD_MASK_EXTRACTU(sparc_insn, (0xf << 14));
/* get the conditions thunk to test: */
conds_thunk
= ((sparc_insn & TME_BIT(12))
? ic->tme_sparc_recode_conds_thunk_xcc
: ic->tme_sparc_recode_conds_thunk_icc);
/* define the first recode flag with the condition: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_DEFC;
recode_insn->tme_recode_insn_operand_src[0] = (cond % TME_SPARC_COND_NOT);
recode_insn->tme_recode_insn_operand_src[1] = 0 - (int) (cond / TME_SPARC_COND_NOT);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_FLAG(0);
recode_insn->tme_recode_insn_conds_thunk = conds_thunk;
recode_insn++;
/* emit the if to test the first recode carry flag: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_IF;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_FLAG(0);
recode_insn++;
/* move the source operand to the destination register: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
if (sparc_insn & TME_BIT(13)) {
recode_insn->tme_recode_insn_imm_uguest
= TME_FIELD_MASK_EXTRACTS(sparc_insn, (tme_sparc_ireg_t) 0x7ff);
recode_operand = TME_RECODE_OPERAND_IMM;
}
else {
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS2));
}
recode_insn->tme_recode_insn_operand_src[1] = recode_operand;
recode_insn->tme_recode_insn_operand_dst
= TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RD));
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
/* emit the endif: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ENDIF;
recode_insn++;
/* suppress any instructions below: */
sparc_recode |= TME_SPARC_RECODE_INSN_NONE;
}
}
/* otherwise, if this is a v9 movr instruction: */
else if (__tme_predict_false(TME_SPARC_VERSION(ic) >= 9
&& sparc_opcode == 0x2f)) {
/* get the condition field: */
cond = TME_FIELD_MASK_EXTRACTU(sparc_insn, (0x7 << 10));
/* this shifts the "not" bit in the condition up from bit
two to bit three, to match the other branches: */
cond = (cond + 4) & (TME_SPARC_COND_NOT | 3);
/* if this is an unimplemented movr: */
if (__tme_predict_false(!TME_SPARC_COND_IS_CONDITIONAL(cond))) {
/* this instruction needs a full assist: */
sparc_recode |= TME_SPARC_RECODE_INSN_ASSIST_FULL;
}
/* otherwise, this is an implemented movr: */
else {
/* test the register and set the internal "rcc" flags: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS1));
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_AND;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = recode_operand;
recode_insn->tme_recode_insn_operand_src[1] = recode_operand;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_OPERAND_NULL;
recode_insn->tme_recode_insn_flags_thunk = ic->tme_sparc_recode_flags_thunk_rcc;
recode_insn++;
/* define the first recode flag with the condition: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_DEFC;
recode_insn->tme_recode_insn_operand_src[0] = (cond % TME_SPARC_COND_NOT);
recode_insn->tme_recode_insn_operand_src[1] = 0 - (int) (cond / TME_SPARC_COND_NOT);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_FLAG(0);
recode_insn->tme_recode_insn_conds_thunk = ic->tme_sparc_recode_conds_thunk_rcc;
recode_insn++;
/* emit the if to test the first recode carry flag: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_IF;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_FLAG(0);
recode_insn++;
/* move the source operand to the destination register: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
if (sparc_insn & TME_BIT(13)) {
recode_insn->tme_recode_insn_imm_uguest
= TME_FIELD_MASK_EXTRACTS(sparc_insn, (tme_sparc_ireg_t) 0x3ff);
recode_operand = TME_RECODE_OPERAND_IMM;
}
else {
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS2));
}
recode_insn->tme_recode_insn_operand_src[1] = recode_operand;
recode_insn->tme_recode_insn_operand_dst
= TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RD));
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
/* emit the endif: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ENDIF;
recode_insn++;
/* suppress any instructions below: */
sparc_recode |= TME_SPARC_RECODE_INSN_NONE;
}
}
/* otherwise, if this is a v9 ld*a instruction: */
/* NB: all of the simple ld and st instructions that we handle
here, have bit 21 (bit 2 in the opcode) set for a st, and
clear for a ld: */
else if (__tme_predict_false(TME_SPARC_VERSION(ic) >= 9
&& TME_SPARC_RECODE_INSN_OPCODE(sparc_recode) == TME_RECODE_OPCODE_RW
&& ((sparc_opcode
& (TME_BIT(4)
+ TME_BIT(2)))
== (TME_BIT(4)
+ !TME_BIT(2))))) {
/* if the i bit is clear, and the immediate ASI is not
ASI_PRIMARY_NOFAULT or ASI_PRIMARY_NOFAULT_LITTLE: */
if ((sparc_insn & TME_BIT(13)) == 0
&& (TME_FIELD_MASK_EXTRACTU(sparc_insn, (0xff << 5))
!= (TME_SPARC64_ASI_FLAG_UNRESTRICTED
+ ((ic->tme_sparc64_ireg_pstate & TME_SPARC64_PSTATE_CLE)
? TME_SPARC64_ASI_FLAG_LITTLE
: !TME_SPARC64_ASI_FLAG_LITTLE)
+ TME_SPARC64_ASI_FLAG_NO_FAULT))) {
/* this instruction can't be a simple ld instruction: */
sparc_recode
^= (TME_SPARC_RECODE_INSN_LDA
^ TME_SPARC_RECODE_INSN_ASSIST_CANTRAP);
}
}
/* if this instruction needs a full assist: */
if ((sparc_recode
& (TME_SPARC_RECODE_INSN_OPCODE_MASK
| TME_SPARC_RECODE_INSN_NO_RS2
| TME_SPARC_RECODE_INSN_NO_RS1))
== (TME_RECODE_OPCODE_GUEST
| TME_SPARC_RECODE_INSN_NO_RS2
| TME_SPARC_RECODE_INSN_NO_RS1)) {
/* set the full assist function: */
sparc_assist = _tme_sparc_recode_insn_assist_full;
}
}
/* otherwise, if this is a format two instruction: */
else if (__tme_predict_true(sparc_insn < 0x40000000)) {
/* dispatch on op2: */
switch (TME_FIELD_MASK_EXTRACTU(sparc_insn, (0x7 << 22))) {
default:
case 0: /* UNIMP: */
sparc_recode |= TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_LAST;
sparc_assist = _tme_sparc_recode_insn_assist_unimpl;
break;
#if TME_SPARC_VERSION(ic) >= 9
case 1: /* BPcc */
case 3: /* BPr */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
case 2: /* Bicc: */
/* if the next instruction can't be recoded when it's in a
branch delay slot (usually because it's also a control
transfer instruction): */
if (__tme_predict_false(_tme_sparc_recode_insn_branch_delay_bad(sparc_insn_next))) {
/* if we haven't recoded any instructions yet, recode is
impossible here: */
if (recode_insn == &ic->tme_sparc_recode_insns[0]) {
return (0);
}
/* stop recoding immediately, leaving the updated PC
pointing to the instruction before the jmpl: */
assert (pc_advance >= sizeof(sparc_insn));
pc_advance -= sizeof(sparc_insn);
sparc_insn_count--;
sparc_recode
|= (TME_SPARC_RECODE_INSN_UPDATE_PCS
| TME_SPARC_RECODE_INSN_NONE
| TME_SPARC_RECODE_INSN_LAST);
break;
}
/* get the condition field: */
cond = TME_FIELD_MASK_EXTRACTU(sparc_insn, (0xf << 25));
/* assume that this branch instruction tests a condition in
the icc flags: */
conds_thunk = ic->tme_sparc_recode_conds_thunk_icc;
/* if this is a BPr or a BPcc: */
if (TME_SPARC_VERSION(ic) >= 9
&& (sparc_insn & (0x1 << 22))) {
/* if this is a BPr: */
if (sparc_insn & (0x2 << 22)) {
/* if this is an unimplemented BPr instruction: */
if (__tme_predict_false((sparc_insn & (1 << 28)) != 0
|| (sparc_insn & (3 << 25)) == 0)) {
/* do a full assist and stop recoding: */
sparc_recode |= TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_LAST;
sparc_assist = _tme_sparc_recode_insn_assist_unimpl;
break;
}
/* test the register and set the internal "rcc" flags: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS1));
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_AND;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = recode_operand;
recode_insn->tme_recode_insn_operand_src[1] = recode_operand;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_OPERAND_NULL;
recode_insn->tme_recode_insn_flags_thunk = ic->tme_sparc_recode_flags_thunk_rcc;
recode_insn++;
/* this shifts the "not" bit in the condition up from bit
two to bit three, to match the other branches: */
cond = (cond + 4) & (TME_SPARC_COND_NOT | 3);
/* we need to test the condition in the internal "rcc" flags: */
conds_thunk = ic->tme_sparc_recode_conds_thunk_rcc;
/* get the raw branch displacement: */
branch_displacement_raw
= ((tme_int16_t)
(((sparc_insn & (0x3 << 20)) >> 6)
| (sparc_insn & 0x3fff)));
}
/* otherwise, this is a BPcc: */
else {
/* if this is an unimplemented BPcc instruction: */
if (__tme_predict_false((sparc_insn & (1 << 20)) != 0)) {
/* do a full assist and stop recoding: */
sparc_recode |= TME_SPARC_RECODE_INSN_ASSIST_FULL | TME_SPARC_RECODE_INSN_LAST;
sparc_assist = _tme_sparc_recode_insn_assist_unimpl;
break;
}
/* if this BPcc is testing the xcc flags: */
if (sparc_insn & (1 << 21)) {
conds_thunk = ic->tme_sparc_recode_conds_thunk_xcc;
}
/* get the raw branch displacement: */
branch_displacement_raw
= ((tme_int32_t)
TME_FIELD_MASK_EXTRACTS(sparc_insn, 0x0007ffff));
}
}
/* otherwise, this is a Bicc: */
else {
/* get the raw branch displacement: */
branch_displacement_raw
= ((tme_int32_t)
TME_FIELD_MASK_EXTRACTS(sparc_insn, 0x003fffff));
}
/* fully sign-extend the branch displacement: */
branch_displacement
= ((tme_sparc_ireg_t)
(tme_recode_guest_t)
branch_displacement_raw);
/* advance PC to the branch delay slot: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = reg_guest_pc;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest = pc_advance + sizeof(sparc_insn);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
pc_advance = 0 - (tme_uint32_t) sizeof(sparc_insn);
reg_guest_pc = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
/* if this isn't a branch always: */
if (cond != (TME_SPARC_COND_NOT | TME_SPARC_COND_N)) {
/* assuming that the branch won't be taken, set PC_next to
the instruction following the branch delay slot: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest = sizeof(sparc_insn);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC_NEXT);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
/* if this is a conditional branch: */
if (TME_SPARC_COND_IS_CONDITIONAL(cond)) {
/* define the recode jump flag with the condition: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_DEFC;
recode_insn->tme_recode_insn_operand_src[0] = (cond % TME_SPARC_COND_NOT);
recode_insn->tme_recode_insn_operand_src[1] = 0 - (int) (cond / TME_SPARC_COND_NOT);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_FLAG_JUMP;
recode_insn->tme_recode_insn_conds_thunk = conds_thunk;
recode_insn++;
/* emit the if to test the recode jump flag: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_IF;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_FLAG_JUMP;
recode_insn++;
}
/* if this isn't a branch never: */
if (cond != TME_SPARC_COND_N) {
/* if this branch target is within this same page, this chain
can be near, otherwise it must be far: */
if ((((pc_next - sizeof(sparc_insn))
+ (branch_displacement * sizeof(sparc_insn)))
^ (pc_next - sizeof(sparc_insn)))
< page_size) {
sparc_recode |= TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_NEAR);
}
else {
sparc_recode |= TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_FAR);
}
/* if this is a branch always: */
if (cond == (TME_SPARC_COND_NOT | TME_SPARC_COND_N)) {
/* the branch delay slot will be the last instruction, and
after that we need an unconditional jump: */
sparc_recode
|= (TME_SPARC_RECODE_INSN_LAST
| TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_JUMP
+ TME_RECODE_CHAIN_INFO_UNCONDITIONAL));
/* set PC_next to the branch target, using the address of
the branch delay slot already in PC (which is one
instruction away from the PC of the branch, to which
the branch displacement is relative): */
recode_operand = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
branch_displacement -= 1;
}
/* otherwise, this is a conditional branch: */
else {
/* the branch delay slot will be the last instruction, and
after that we need an conditional jump: */
sparc_recode
|= (TME_SPARC_RECODE_INSN_LAST
| TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_JUMP
+ TME_RECODE_CHAIN_INFO_CONDITIONAL));
/* change PC_next to the branch target, from the
instruction following the branch delay slot already in
PC_next (which is two instructions away from the PC of
the branch, to which the branch displacement is
relative): */
recode_operand = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC_NEXT);
branch_displacement -= 2;
}
/* set PC_next to the branch target: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = recode_operand;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest = branch_displacement * sizeof(sparc_insn);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC_NEXT);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
/* if this is a conditional branch: */
if (TME_SPARC_COND_IS_CONDITIONAL(cond)) {
/* if this conditional branch never annuls the branch delay
slot: */
if ((sparc_insn & TME_BIT(29)) == 0) {
/* emit the endif: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ENDIF;
recode_insn++;
}
/* otherwise, this conditional branch annuls the branch
delay slot if the branch is not taken: */
else {
/* we need to emit the endif after we emit the
instructions for the branch delay slot: */
sparc_recode |= TME_SPARC_RECODE_INSN_NEED_ENDIF;
}
}
/* otherwise, this is an unconditional branch: */
else {
/* if the branch delay slot is annulled: */
if (sparc_insn & TME_BIT(29)) {
/* don't bother to recode the instruction in the branch
delay slot: */
sparc_recode |= TME_SPARC_RECODE_INSN_NONE;
}
}
/* if this is a branch to . instruction: */
if (__tme_predict_false(branch_displacement_raw == 0)) {
/* if this isn't a branch never, and this isn't an
unconditional branch that annuls, and the branch with its
delay instruction are a supported timing loop: */
if (cond != TME_SPARC_COND_N
&& (sparc_recode & TME_SPARC_RECODE_INSN_NONE) == 0
&& tme_sparc_timing_loop_ok(sparc_insn,
sparc_insn_next)) {
/* store the branch delay instruction in the instruction register: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = TME_RECODE_SIZE_32;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_u32 = sparc_insn_next;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_INSN);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
/* call the timing loop assist function: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_GUEST;
recode_insn->tme_recode_insn_size = TME_RECODE_SIZE_32;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_u32 = sparc_insn;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_ZERO;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_OPERAND_NULL;
recode_insn->tme_recode_insn_guest_func = tme_sparc_timing_loop_assist;
recode_insn++;
/* the branch delay instruction must be the last instruction: */
assert (sparc_recode & TME_SPARC_RECODE_INSN_LAST);
/* advance to the branch delay instruction: */
assert (((tme_uint32_t) (pc_advance + sizeof(sparc_insn))) == 0
&& reg_guest_pc == TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC));
pc_advance = 0;
/* suppress the branch delay instruction: */
sparc_recode |= TME_SPARC_RECODE_INSN_NONE;
/* do *not* loop now to fetch and recode the instruction
in the branch delay slot: */
break;
}
}
/* loop now to fetch and recode the instruction in the branch
delay slot: */
continue;
case 4: /* SETHI: */
/* unless this instruction is suppressed, load the constant
into the register: */
if (!(sparc_recode & TME_SPARC_RECODE_INSN_NONE)) {
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RD));
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_u32 = (tme_uint32_t) (sparc_insn << 10);
recode_insn->tme_recode_insn_operand_dst = recode_operand;
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
/* suppress any instructions below: */
sparc_recode |= TME_SPARC_RECODE_INSN_NONE;
break;
#if TME_SPARC_VERSION(ic) >= 9
case 5: /* FBPfcc */
#endif /* TME_SPARC_VERSION(ic) >= 9 */
case 6: /* FBfcc: */
/* if we haven't recoded any instructions yet, recode is
impossible here: */
if (recode_insn == &ic->tme_sparc_recode_insns[0]) {
return (0);
}
/* stop recoding immediately, leaving the updated PC
pointing to the instruction before the jmpl: */
assert (pc_advance >= sizeof(sparc_insn));
pc_advance -= sizeof(sparc_insn);
sparc_insn_count--;
sparc_recode
|= (TME_SPARC_RECODE_INSN_UPDATE_PCS
| TME_SPARC_RECODE_INSN_NONE
| TME_SPARC_RECODE_INSN_LAST);
break;
}
}
/* otherwise, this is a format one instruction: */
else {
/* if the next instruction can't be recoded when it's in a
branch delay slot (usually because it's also a control
transfer instruction): */
if (__tme_predict_false(_tme_sparc_recode_insn_branch_delay_bad(sparc_insn_next))) {
/* if we haven't recoded any instructions yet, recode is
impossible here: */
if (recode_insn == &ic->tme_sparc_recode_insns[0]) {
return (0);
}
/* stop recoding immediately, leaving the updated PC
pointing to the instruction before the jmpl: */
assert (pc_advance >= sizeof(sparc_insn));
pc_advance -= sizeof(sparc_insn);
sparc_insn_count--;
sparc_recode
|= (TME_SPARC_RECODE_INSN_UPDATE_PCS
| TME_SPARC_RECODE_INSN_NONE
| TME_SPARC_RECODE_INSN_LAST);
}
/* otherwise, the next instruction can be recoded when it's in a
branch delay slot: */
else {
/* write the PC of the CALL into r[15]: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = reg_guest_pc;
recode_insn->tme_recode_insn_operand_src[1]
= (pc_advance == 0
? TME_RECODE_OPERAND_ZERO
: TME_RECODE_OPERAND_IMM);
recode_insn->tme_recode_insn_imm_uguest = pc_advance;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(15);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
/* if the PC is not guest size: */
if (recode_size_address < TME_SPARC_RECODE_SIZE(ic)) {
/* zero-extend r[15] to guest size: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_EXTZ;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_REG_GUEST(15);
recode_insn->tme_recode_insn_operand_src[1] = recode_size_address;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(15);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
/* advance PC to the branch delay slot: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_REG_GUEST(15);
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest = sizeof(sparc_insn);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
pc_advance = 0 - (tme_uint32_t) sizeof(sparc_insn);
reg_guest_pc = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
/* write the target of the call into PC_next. NB that we have
to account for the fact that PC already points to the
branch delay slot, but the displacement is relative to the
PC of the call instruction: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest
= ((tme_sparc_ireg_t)
(((tme_recode_guest_t)
(tme_int32_t)
(sparc_insn << 2))
- sizeof(sparc_insn)));
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC_NEXT);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
/* the branch delay slot will be the last instruction, and
after that we need an unconditional call: */
sparc_recode
|= (TME_SPARC_RECODE_INSN_LAST
| TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_CALL
+ TME_RECODE_CHAIN_INFO_UNCONDITIONAL));
/* if the call target is within this same page,
the call can be near, otherwise the call must be far: */
if ((((pc_next - sizeof(sparc_insn))
+ ((tme_sparc_ireg_t)
(tme_recode_guest_t)
(tme_int32_t)
(sparc_insn << 2)))
^ (pc_next - sizeof(sparc_insn)))
< page_size) {
sparc_recode |= TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_NEAR);
}
else {
sparc_recode |= TME_SPARC_RECODE_CHAIN_INFO(TME_RECODE_CHAIN_INFO_FAR);
}
continue;
}
}
/* if we might not have enough space for recode insns for the next
instruction: */
if (__tme_predict_false(recode_insn >= recode_insns_last_start)) {
/* this will be the last instruction, and we need to update the
PCs: */
sparc_recode |= TME_SPARC_RECODE_INSN_UPDATE_PCS | TME_SPARC_RECODE_INSN_LAST;
}
/* if this is the last instruction, we must be updating PC and
PC_next: */
assert ((sparc_recode & TME_SPARC_RECODE_INSN_LAST) == 0
|| (sparc_recode & TME_SPARC_RECODE_INSN_UPDATE_PCS)
|| (pc_advance == 0
&& reg_guest_pc == TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC)));
/* if we need PC and PC_next to be valid for this instruction: */
if (sparc_recode & TME_SPARC_RECODE_INSN_UPDATE_PCS) {
/* if we need to update PC and PC_next: */
if (pc_advance > 0
|| reg_guest_pc != TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC)) {
/* advance PC: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = reg_guest_pc;
recode_insn->tme_recode_insn_operand_src[1]
= (pc_advance == 0
? TME_RECODE_OPERAND_ZERO
: TME_RECODE_OPERAND_IMM);
recode_insn->tme_recode_insn_imm_uguest = pc_advance;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
pc_advance = 0;
reg_guest_pc = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
/* set PC_next: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC);
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest = sizeof(sparc_insn);
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_PC_NEXT);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
}
/* if this instruction hasn't been suppressed: */
if (__tme_predict_true((sparc_recode & TME_SPARC_RECODE_INSN_NONE) == 0)) {
/* if this instruction always or sometimes needs an assist: */
if (sparc_recode & TME_SPARC_RECODE_INSN_UPDATE_INSN) {
/* store the instruction in the instruction register: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = TME_RECODE_SIZE_32;
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_u32 = sparc_insn;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_INSN);
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
/* if this instruction doesn't take rs2: */
if (sparc_recode & TME_SPARC_RECODE_INSN_NO_RS2) {
/* clear the i bit and rs2, to force the second source
operand to be %g0, or zero: */
sparc_insn &= ~(TME_BIT(13) | TME_SPARC_FORMAT3_MASK_RS2);
}
/* if this instruction doesn't take rs1: */
if (sparc_recode & TME_SPARC_RECODE_INSN_NO_RS1) {
/* clear rs1, to force the first source operand to be %g0,
or zero: */
sparc_insn &= ~TME_SPARC_FORMAT3_MASK_RS1;
}
}
/* otherwise, this instruction doesn't need an assist: */
else {
/* if this is an addx, addxcc, subx, or subxcc instruction: */
if (sparc_recode & TME_SPARC_RECODE_INSN_DEFC) {
/* define the recode carry flag with the icc C flag: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_DEFC;
recode_insn->tme_recode_insn_operand_src[0] = TME_SPARC_COND_CS;
recode_insn->tme_recode_insn_operand_src[1] = 0;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_FLAG_CARRY;
recode_insn->tme_recode_insn_conds_thunk = ic->tme_sparc_recode_conds_thunk_icc;
recode_insn++;
}
/* because we set the flags thunk on the recode instruction
now, this instruction can't be a shift (because a shift may
make more than one recode instruction): */
assert ((sparc_recode
& (TME_SPARC_RECODE_INSN_CC
| TME_SPARC_RECODE_INSN_SHIFT))
!= (TME_SPARC_RECODE_INSN_CC
| TME_SPARC_RECODE_INSN_SHIFT));
/* assume that this instruction doesn't update flags: */
flags_thunk = NULL;
/* if this instruction updates flags: */
if (sparc_recode & TME_SPARC_RECODE_INSN_CC) {
/* set the right flags thunk for this instruction. NB that
the least significant two bits of op3 are zero only for
the additive instructions, and bit two of op3 is set only
for a subtraction instruction: */
flags_thunk
= ((sparc_insn & (0x3 << 19)) == 0
? ((sparc_insn & (0x4 << 19))
? ic->tme_sparc_recode_flags_thunk_sub
: ic->tme_sparc_recode_flags_thunk_add)
: ic->tme_sparc_recode_flags_thunk_logical);
}
/* set any flags thunk on this instruction: */
recode_insn->tme_recode_insn_flags_thunk = flags_thunk;
}
/* assume that if this is a shift, it's a 32-bit shift: */
shift_count_mask = 32 - 1;
/* if this is a v9 shift: */
if (TME_SPARC_VERSION(ic) >= 9
&& (sparc_recode & TME_SPARC_RECODE_INSN_SHIFT)) {
/* if this is a 64-bit shift: */
if (sparc_insn & TME_BIT(12)) {
/* this is a 64-bit shift: */
shift_count_mask = 64 - 1;
}
/* otherwise, this is a 32-bit shift: */
else {
/* if this is a right shift: */
if (sparc_insn & TME_BIT(20)) {
/* remember that this is a right shift: */
sparc_recode |= TME_SPARC_RECODE_INSN_SHIFT_RIGHT;
/* decode rs1: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS1));
/* extend rs1 into the second temporary register. if this
is a shift right arithmetic, sign extend, else zero
extend: */
recode_insn->tme_recode_insn_opcode
= ((sparc_insn & TME_BIT(19))
? TME_RECODE_OPCODE_EXTS
: TME_RECODE_OPCODE_EXTZ);
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = recode_operand;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_SIZE_32;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_TMP(1));
recode_insn++;
/* NB that the flags thunk for the final instruction was
already set above (to NULL, because sparc shift
instructions do not affect any condition codes), so we
have to do the same since we advanced to the next
instruction: */
recode_insn->tme_recode_insn_flags_thunk = NULL;
}
}
}
/* if the i bit is one: */
if (sparc_insn & TME_BIT(13)) {
/* decode simm13: */
imm = TME_FIELD_MASK_EXTRACTS(sparc_insn, (tme_sparc_ireg_t) 0x1fff);
/* if this is a shift instruction: */
if (__tme_predict_false(sparc_recode & TME_SPARC_RECODE_INSN_SHIFT)) {
/* mask the immediate with the shift count mask: */
imm &= shift_count_mask;
}
/* if the immediate is zero, the second source operand is zero,
otherwise the second source operand is the immediate: */
recode_insn->tme_recode_insn_imm_uguest = imm;
recode_operand
= (imm == 0
? TME_RECODE_OPERAND_ZERO
: TME_RECODE_OPERAND_IMM);
}
/* otherwise, the i bit is zero: */
else {
/* decode rs2: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS2));
if (recode_operand == TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0)) {
recode_operand = TME_RECODE_OPERAND_ZERO;
}
/* if this is a shift instruction: */
if (__tme_predict_false(sparc_recode & TME_SPARC_RECODE_INSN_SHIFT)) {
/* make a temporary copy of the register with shift count,
masked with the shift count mask, and use the temporary
register as the second source operand: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_AND;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = recode_operand;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_uguest = shift_count_mask;
recode_operand = TME_RECODE_REG_GUEST(TME_SPARC_IREG_TMP(0));
recode_insn->tme_recode_insn_operand_dst = recode_operand;
recode_insn++;
/* NB that the flags thunk for the final instruction was
already set above (to NULL, because sparc shift
instructions do not affect any condition codes), so we
have to do the same since we advanced to the next
instruction: */
recode_insn->tme_recode_insn_flags_thunk = NULL;
}
}
/* set the second source operand: */
recode_insn->tme_recode_insn_operand_src[1] = recode_operand;
/* decode rs1: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RS1));
if (recode_operand == TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0)) {
recode_operand = TME_RECODE_OPERAND_ZERO;
}
/* if this is a v9 32-bit right shift: */
if (__tme_predict_false(TME_SPARC_VERSION(ic) >= 9
&& (sparc_recode & TME_SPARC_RECODE_INSN_SHIFT_RIGHT))) {
/* the first source operand is actually in the second
temporary register: */
recode_operand = TME_RECODE_REG_GUEST(TME_SPARC_IREG_TMP(1));
}
/* set the first source operand: */
recode_insn->tme_recode_insn_operand_src[0] = recode_operand;
/* decode rd: */
recode_operand = TME_RECODE_REG_GUEST(TME_FIELD_MASK_EXTRACTU(sparc_insn, TME_SPARC_FORMAT3_MASK_RD));
if (recode_operand == TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0)) {
recode_operand = TME_RECODE_OPERAND_NULL;
}
/* if this is a simple ld or st instruction: */
if (TME_SPARC_RECODE_INSN_OPCODE(sparc_recode) == TME_RECODE_OPCODE_RW) {
/* assume that the first source operand is %g0, which makes
the second source operand the guest address: */
recode_operand_address = recode_insn->tme_recode_insn_operand_src[1];
/* if the second source operand is %g0: */
if (recode_operand_address == TME_RECODE_OPERAND_ZERO) {
/* nothing to do; the guest address is the first source
operand, which is already in
recode_insn->tme_recode_insn_operand_src[0]: */
}
/* otherwise, the second source operand is not %g0: */
else {
/* if the first source operand is not %g0: */
if (recode_insn->tme_recode_insn_operand_src[0] != TME_RECODE_OPERAND_ZERO) {
/* add the two parts of the guest address into the
temporary register: */
recode_operand_address = TME_RECODE_REG_GUEST(TME_SPARC_IREG_TMP(0));
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ADD;
recode_insn->tme_recode_insn_size = recode_size_address;
recode_insn->tme_recode_insn_operand_dst = recode_operand_address;
recode_insn->tme_recode_insn_flags_thunk = NULL;
recode_insn++;
}
/* set the guest address as the first source operand: */
recode_insn->tme_recode_insn_operand_src[0] = recode_operand_address;
}
/* if this is a st instruction: */
/* NB: all of the simple ld and st instructions that we handle
here, have bit 21 set for a st, and clear for a ld: */
if (sparc_insn & (4 << 19)) {
/* the second source operand is the register being
stored: */
recode_insn->tme_recode_insn_operand_src[1]
= ((TME_RECODE_OPERAND_NULL != TME_RECODE_OPERAND_ZERO
&& recode_operand == TME_RECODE_OPERAND_NULL)
? TME_RECODE_OPERAND_ZERO
: recode_operand);
/* the destination operand must be the recode %null: */
recode_operand = TME_RECODE_OPERAND_NULL;
}
/* otherwise, this is an ld instruction: */
else {
/* the second source operand must be zero: */
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_ZERO;
}
/* set the read/write thunk: */
assert (TME_SPARC_VERSION(ic) >= 9 || (sparc_insn & (16 << 19)) == 0);
recode_insn->tme_recode_insn_rw_thunk
= ic->tme_sparc_recode_rw_thunks
[TME_SPARC_RECODE_RW_THUNK_INDEX(ic,
(TME_SPARC_RECODE_SIZE(ic)
- (TME_SPARC_VERSION(ic) >= 9
&& (ic->tme_sparc64_ireg_pstate & TME_SPARC64_PSTATE_AM))),
((TME_SPARC_VERSION(ic) >= 9
&& (ic->tme_sparc64_ireg_pstate & TME_SPARC64_PSTATE_CLE))
? TME_ENDIAN_LITTLE
: TME_ENDIAN_BIG),
(TME_SPARC_VERSION(ic) >= 9
&& (sparc_insn & (16 << 19))),
TME_FIELD_MASK_EXTRACTU(sparc_insn, (0xf << 19)))];
}
/* otherwise, if this instruction needs an assist: */
else if (TME_SPARC_RECODE_INSN_OPCODE(sparc_recode) == TME_RECODE_OPCODE_GUEST) {
/* set the assist function: */
recode_insn->tme_recode_insn_guest_func = sparc_assist;
/* if this instruction needs a full assist: */
if ((sparc_recode
& (TME_SPARC_RECODE_INSN_NO_RS2
| TME_SPARC_RECODE_INSN_NO_RS1))
== (TME_SPARC_RECODE_INSN_NO_RS2
| TME_SPARC_RECODE_INSN_NO_RS1)) {
/* the source operands can be the recode %undef, since the
full assist functions don't use the source operands: */
assert (sparc_assist == _tme_sparc_recode_insn_assist_full
|| sparc_assist == _tme_sparc_recode_insn_assist_unimpl
);
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_UNDEF;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_UNDEF;
/* assume that this instruction is not also marked
TME_SPARC_RECODE_INSN_NO_RD, and force the destination
operand to be the recode %null, to indicate that the
assist can change any of the recode-visible ic state: */
recode_operand = TME_RECODE_OPERAND_NULL;
}
/* if this instruction's rd field doesn't encode a destination
general register: */
if (sparc_recode & TME_SPARC_RECODE_INSN_NO_RD) {
/* force the destination operand to be the fixed %g0: */
recode_operand = TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0);
}
}
/* set the destination operand: */
recode_insn->tme_recode_insn_operand_dst = recode_operand;
/* set the size: */
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
/* set the opcode: */
recode_insn->tme_recode_insn_opcode = TME_SPARC_RECODE_INSN_OPCODE(sparc_recode);
/* we have finished this instruction: */
recode_insn++;
}
/* if we need to, emit an endif: */
if (sparc_recode & TME_SPARC_RECODE_INSN_NEED_ENDIF) {
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_ENDIF;
recode_insn++;
#ifndef NDEBUG
sparc_recode &= ~TME_SPARC_RECODE_INSN_NEED_ENDIF;
#endif
}
/* if this was the last instruction: */
if (sparc_recode & TME_SPARC_RECODE_INSN_LAST) {
break;
}
/* clear sparc_recode: */
sparc_recode = 0;
}
/* make sure we didn't forget an endif: */
assert ((sparc_recode & TME_SPARC_RECODE_INSN_NEED_ENDIF) == 0);
#ifdef _TME_SPARC_STATS
/* update the recode instructions total: */
recode_insn->tme_recode_insn_opcode = TME_RECODE_OPCODE_GUEST;
recode_insn->tme_recode_insn_size = TME_SPARC_RECODE_SIZE(ic);
recode_insn->tme_recode_insn_operand_src[0] = TME_RECODE_OPERAND_ZERO;
recode_insn->tme_recode_insn_operand_src[1] = TME_RECODE_OPERAND_IMM;
recode_insn->tme_recode_insn_imm_u32 = sparc_insn_count;
recode_insn->tme_recode_insn_operand_dst = TME_RECODE_REG_GUEST(TME_SPARC_IREG_G0);
recode_insn->tme_recode_insn_guest_func = _tme_sparc_recode_insns_total;
recode_insn++;
#endif /* _TME_SPARC_STATS */
/* set the end of the instructions: */
ic->tme_sparc_recode_insns_group.tme_recode_insns_group_insns_end = recode_insn;
/* get the chain information: */
chain_info
= ((sparc_recode
/ TME_SPARC_RECODE_CHAIN_INFO(1))
& (TME_RECODE_CHAIN_INFO_UNCONDITIONAL
| TME_RECODE_CHAIN_INFO_CONDITIONAL
| TME_RECODE_CHAIN_INFO_NEAR
| TME_RECODE_CHAIN_INFO_FAR
| TME_RECODE_CHAIN_INFO_JUMP
| TME_RECODE_CHAIN_INFO_RETURN
| TME_RECODE_CHAIN_INFO_CALL
));
/* if there is no chain information: */
if (chain_info == 0) {
/* if the next PC is on the same page, make a chain jump near
unconditional, otherwise make a chain jump far
unconditional: */
if (pc_next <= itlb_addr_last) {
chain_info += TME_RECODE_CHAIN_INFO_NEAR;
}
else {
chain_info += TME_RECODE_CHAIN_INFO_FAR;
}
chain_info
+= (TME_RECODE_CHAIN_INFO_JUMP
+ TME_RECODE_CHAIN_INFO_UNCONDITIONAL);
}
/* otherwise, there is chain information. if the chain is
conditional: */
else if ((chain_info
& (TME_RECODE_CHAIN_INFO_UNCONDITIONAL
| TME_RECODE_CHAIN_INFO_CONDITIONAL))
!= TME_RECODE_CHAIN_INFO_UNCONDITIONAL) {
/* if the alternate PC is on the same page, the alternate target
can be near, otherwise it must be far: */
if (pc_next <= itlb_addr_last) {
chain_info += TME_RECODE_CHAIN_INFO_ALTERNATE_NEAR;
}
else {
chain_info += TME_RECODE_CHAIN_INFO_ALTERNATE_FAR;
}
}
/* set the chain information: */
ic->tme_sparc_recode_insns_group.tme_recode_insns_group_chain_info = chain_info;
/* make sure we didn't overflow the instructions buffer: */
assert (recode_insn <= &ic->tme_sparc_recode_insns[TME_ARRAY_ELS(ic->tme_sparc_recode_insns)]);
/* recode these instructions: */
return (tme_recode_insns_thunk(ic->tme_sparc_recode_ic,
&ic->tme_sparc_recode_insns_group));
}