blob: f39b64f651815578419fced6af5b764e04ad7e97 [file] [log] [blame]
/* $Id: sparc-rc-cc.c,v 1.2 2009/08/28 01:41:45 fredette Exp $ */
/* ic/sparc/sparc-rc-cc.c - SPARC recode condition code 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-cc.c,v 1.2 2009/08/28 01:41:45 fredette Exp $");
/* macros: */
/* this converts sparc64 icc flags into the current architecture's icc
flags: */
#define TME_SPARC_ICC(ccr_icc) \
((ccr_icc) \
* (((TME_SPARC_VERSION(ic) < 9) \
* (TME_SPARC32_PSR_ICC \
/ TME_SPARC64_CCR_ICC)) \
+ (TME_SPARC_VERSION(ic) >= 9)))
#define TME_SPARC_RECODE_CCR_NEED(insn_class, insn_size, size) \
((((insn_class == TME_RECODE_INSN_CLASS_ADDITIVE) \
* ((TME_SPARC64_CCR_ICC_C \
* (TME_RECODE_FLAG_NEED(insn_class, insn_size, \
TME_RECODE_COND_C, size) \
!= 0)) \
+ (TME_SPARC64_CCR_ICC_V \
* (TME_RECODE_FLAG_NEED(insn_class, insn_size, \
TME_RECODE_COND_V, size) \
!= 0)))) \
+ (TME_SPARC64_CCR_ICC_Z \
* (TME_RECODE_FLAG_NEED(insn_class, insn_size, \
TME_RECODE_COND_Z, size) \
!= 0)) \
+ (TME_SPARC64_CCR_ICC_N \
* (TME_RECODE_FLAG_NEED(insn_class, insn_size, \
TME_RECODE_COND_N, size) \
!= 0))) \
* ((TME_SPARC64_CCR_XCC \
/ TME_SPARC64_CCR_ICC) \
* (size > TME_RECODE_SIZE_32)))
/* this macro gives the %icc and %xcc N and Z flags: */
#define TME_SPARC_RECODE_CCR_N_Z(dst, ccr_need) \
(0 \
+ ((((tme_uint32_t) (dst)) == 0) \
* ((ccr_need) & TME_SPARC64_CCR_ICC_Z)) \
+ ((((tme_int32_t) (dst)) < 0) \
* ((ccr_need) & TME_SPARC64_CCR_ICC_N)) \
+ ((((tme_sparc_ireg_t) (dst)) == 0) \
* ((ccr_need) & TME_SPARC64_CCR_XCC_Z)) \
+ ((((dst) & TME_SPARC_IREG_MSBIT) != 0) \
* ((ccr_need) & TME_SPARC64_CCR_XCC_N)))
/* this macro updates %icc and %xcc: */
#define TME_SPARC_RECODE_CCR_UPDATE(ic, ccr, ccr_need) \
do { \
if (TME_SPARC_VERSION((struct tme_sparc *) (ic)) >= 9) { \
((struct tme_sparc *) (ic))->tme_sparc64_ireg_ccr \
= ((((struct tme_sparc *) (ic))->tme_sparc64_ireg_ccr \
& ~ccr_need) \
| ccr); \
} \
else { \
((struct tme_sparc *) (ic))->tme_sparc32_ireg_psr \
= ((((struct tme_sparc *) (ic))->tme_sparc32_ireg_psr \
& ~TME_SPARC_ICC(ccr_need)) \
| TME_SPARC_ICC(ccr_need)); \
} \
} while (/* CONSTCOND */ 0)
/* rename various things by the architecture size: */
#define _tme_sparc_flags_additive _TME_SPARC_RECODE_SIZE(_tme_sparc,_flags_additive)
#define _tme_sparc_flags_logical _TME_SPARC_RECODE_SIZE(_tme_sparc,_flags_logical)
#define _tme_sparc_flags_func_logical _TME_SPARC_RECODE_SIZE(_tme_sparc,_flags_func_logical)
#define _tme_sparc_flags_func_add _TME_SPARC_RECODE_SIZE(_tme_sparc,_flags_func_add)
#define _tme_sparc_flags_func_sub _TME_SPARC_RECODE_SIZE(_tme_sparc,_flags_func_sub)
#define _tme_sparc_recode_conds_func_icc _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_conds_func_icc)
#define _tme_sparc_recode_cc_init _TME_SPARC_RECODE_SIZE(_tme_sparc,_recode_cc_init)
/* the sparc64 recode xcc conditions function: */
static int
_tme_sparc64_recode_conds_func_xcc(tme_recode_uguest_t flags, tme_uint32_t cond)
{
return (_tme_sparc_conds_icc
[TME_FIELD_MASK_EXTRACTU(flags, TME_SPARC64_CCR_XCC)]
& (1 << cond));
}
#endif /* TME_SPARC_RECODE_SIZE(ic) == TME_RECODE_SIZE_32 */
/* the additive instruction flags: */
static const struct tme_recode_flag _tme_sparc_flags_additive[] = {
#if TME_SPARC_VERSION(ic) >= 9
{
TME_RECODE_COND_N,
TME_RECODE_SIZE_64,
TME_SPARC64_CCR_XCC_N
},
{
TME_RECODE_COND_Z,
TME_RECODE_SIZE_64,
TME_SPARC64_CCR_XCC_Z
},
{
TME_RECODE_COND_V,
TME_RECODE_SIZE_64,
TME_SPARC64_CCR_XCC_V
},
{
TME_RECODE_COND_C,
TME_RECODE_SIZE_64,
TME_SPARC64_CCR_XCC_C
},
#endif /* TME_SPARC_VERSION(ic) >= 9 */
{
TME_RECODE_COND_N,
TME_RECODE_SIZE_32,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_N)
},
{
TME_RECODE_COND_Z,
TME_RECODE_SIZE_32,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_Z)
},
{
TME_RECODE_COND_V,
TME_RECODE_SIZE_32,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_V)
},
{
TME_RECODE_COND_C,
TME_RECODE_SIZE_32,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_C)
},
{
0,
0,
0
}
};
/* the logical instruction flags: */
static const struct tme_recode_flag _tme_sparc_flags_logical[] = {
#if TME_SPARC_VERSION(ic) >= 9
{
TME_RECODE_COND_N,
TME_RECODE_SIZE_64,
TME_SPARC64_CCR_XCC_N
},
{
TME_RECODE_COND_Z,
TME_RECODE_SIZE_64,
TME_SPARC64_CCR_XCC_Z
},
{
TME_RECODE_COND_FALSE,
0,
TME_SPARC64_CCR_XCC_V
},
{
TME_RECODE_COND_FALSE,
0,
TME_SPARC64_CCR_XCC_C
},
#endif /* TME_SPARC_VERSION(ic) >= 9 */
{
TME_RECODE_COND_N,
TME_RECODE_SIZE_32,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_N)
},
{
TME_RECODE_COND_Z,
TME_RECODE_SIZE_32,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_Z)
},
{
TME_RECODE_COND_FALSE,
0,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_V)
},
{
TME_RECODE_COND_FALSE,
0,
TME_SPARC_ICC(TME_SPARC64_CCR_ICC_C)
},
{
0,
0,
0
}
};
/* the logical instruction flags function: */
static void
_tme_sparc_flags_func_logical(struct tme_ic *ic,
tme_recode_uguest_t src1,
tme_recode_uguest_t src2,
tme_recode_uguest_t dst)
{
unsigned int ccr_need;
ccr_need
= (TME_SPARC_RECODE_CCR_NEED(TME_RECODE_INSN_CLASS_LOGICAL,
TME_SPARC_RECODE_SIZE(ic),
TME_RECODE_SIZE_32)
| TME_SPARC_RECODE_CCR_NEED(TME_RECODE_INSN_CLASS_LOGICAL,
TME_SPARC_RECODE_SIZE(ic),
TME_SPARC_RECODE_SIZE(ic)));
TME_SPARC_RECODE_CCR_UPDATE(ic,
TME_SPARC_RECODE_CCR_N_Z(dst, ccr_need),
ccr_need);
}
/* the add instruction flags function: */
static void
_tme_sparc_flags_func_add(struct tme_ic *ic,
tme_recode_uguest_t src1,
tme_recode_uguest_t src2,
tme_recode_uguest_t dst)
{
unsigned int ccr_need;
unsigned int ccr;
ccr_need
= (TME_SPARC_RECODE_CCR_NEED(TME_RECODE_INSN_CLASS_ADDITIVE,
TME_SPARC_RECODE_SIZE(ic),
TME_RECODE_SIZE_32)
| TME_SPARC_RECODE_CCR_NEED(TME_RECODE_INSN_CLASS_ADDITIVE,
TME_SPARC_RECODE_SIZE(ic),
TME_SPARC_RECODE_SIZE(ic)));
/* make the %icc and %xcc N and Z flags: */
ccr = TME_SPARC_RECODE_CCR_N_Z(dst, ccr_need);
/* if src1 and src2 both have bit 31 set, or if dst does not have
bit 31 set and either src1 or src2 does, set %icc.C: */
if (((tme_int32_t) ((src1 & src2) | (~dst & (src1 | src2)))) < 0) {
ccr += (ccr_need & TME_SPARC64_CCR_ICC_C);
}
/* if src1 and src2 have the same bit 31, and dst has a different
bit 31, set %icc.V: */
if (((tme_int32_t) ((src2 ^ dst) & (src1 ^ ~src2))) < 0) {
ccr += (ccr_need & TME_SPARC64_CCR_ICC_V);
}
/* if src1 and src2 both have the most-significant bit set, or if
dst does not have the most-significant bit set either src1 or
src2 does, set %xcc.C: */
if (((src1 & src2) | (~dst & (src1 | src2))) & TME_SPARC_IREG_MSBIT) {
ccr += (ccr_need & TME_SPARC64_CCR_XCC_C);
}
/* if src1 and src2 have the same most-significant bit, and dst has
a different most-significant bit, set %xcc.V: */
if (((src2 ^ dst) & (src1 ^ ~src2)) & TME_SPARC_IREG_MSBIT) {
ccr += (ccr_need & TME_SPARC64_CCR_XCC_V);
}
TME_SPARC_RECODE_CCR_UPDATE(ic, ccr, ccr_need);
}
/* the sub instruction flags function: */
static void
_tme_sparc_flags_func_sub(struct tme_ic *ic,
tme_recode_uguest_t src1,
tme_recode_uguest_t src2,
tme_recode_uguest_t dst)
{
unsigned int ccr_need;
unsigned int ccr;
ccr_need
= (TME_SPARC_RECODE_CCR_NEED(TME_RECODE_INSN_CLASS_ADDITIVE,
TME_SPARC_RECODE_SIZE(ic),
TME_RECODE_SIZE_32)
| TME_SPARC_RECODE_CCR_NEED(TME_RECODE_INSN_CLASS_ADDITIVE,
TME_SPARC_RECODE_SIZE(ic),
TME_SPARC_RECODE_SIZE(ic)));
/* make the %icc and %xcc N and Z flags: */
ccr = TME_SPARC_RECODE_CCR_N_Z(dst, ccr_need);
/* if src2 is greater than src1 in 32 bits, set %icc.C: */
if (((tme_uint32_t) src2) > ((tme_uint32_t) src1)) {
ccr += (ccr_need & TME_SPARC64_CCR_ICC_C);
}
/* if src1 and src2 have different bit 31s, and dst has a different
bit 31 than src1, set %icc.V: */
if (((tme_int32_t) ((src1 ^ src2) & (src1 ^ dst))) < 0) {
ccr += (ccr_need & TME_SPARC64_CCR_ICC_V);
}
/* if src2 is greater than src1, set %xcc.C: */
if (((tme_sparc_ireg_t) src2) > ((tme_sparc_ireg_t) src1)) {
ccr += (ccr_need & TME_SPARC64_CCR_XCC_C);
}
/* if src1 and src2 have different most-significant bits, and dst
has a different most-significant bit than src1, set %xcc.V: */
if (((src1 ^ src2) & (src1 ^ dst)) & TME_SPARC_IREG_MSBIT) {
ccr += (ccr_need & TME_SPARC64_CCR_XCC_V);
}
TME_SPARC_RECODE_CCR_UPDATE(ic, ccr, ccr_need);
}
/* the sparc icc conditions function: */
static int
_tme_sparc_recode_conds_func_icc(tme_recode_uguest_t flags, tme_uint32_t cond)
{
return (_tme_sparc_conds_icc
[TME_FIELD_MASK_EXTRACTU(flags, TME_SPARC_ICC(TME_SPARC64_CCR_ICC))]
& (1 << cond));
}
/* this initializes for condition codes: */
static void
_tme_sparc_recode_cc_init(struct tme_sparc *ic)
{
struct tme_recode_ic *recode_ic;
struct tme_recode_flags_group flags_group;
struct tme_recode_conds_group conds_group;
/* recover the recode ic: */
recode_ic = ic->tme_sparc_recode_ic;
/* make the common parts of a flags group: */
memset((char *) &flags_group, 0, sizeof(flags_group));
flags_group.tme_recode_flags_group_insn_size = TME_SPARC_RECODE_SIZE(ic);
if (TME_SPARC_VERSION(ic) <= 8) {
flags_group.tme_recode_flags_group_flags_reg_size = TME_RECODE_SIZE_32;
flags_group.tme_recode_flags_group_flags_reg = TME_SPARC32_IREG_PSR;
}
else {
flags_group.tme_recode_flags_group_flags_reg_size = TME_RECODE_SIZE_8;
flags_group.tme_recode_flags_group_flags_reg = TME_SPARC64_IREG_CCR << 3;
}
/* make the flags thunk for addcc and addxcc: */
flags_group.tme_recode_flags_group_insn_class = TME_RECODE_INSN_CLASS_ADDITIVE;
flags_group.tme_recode_flags_group_flags = _tme_sparc_flags_additive;
flags_group.tme_recode_flags_group_guest_func = _tme_sparc_flags_func_add;
ic->tme_sparc_recode_flags_thunk_add
= tme_recode_flags_thunk(recode_ic,
&flags_group);
/* make the flags thunk for subcc and subxcc: */
flags_group.tme_recode_flags_group_insn_class = TME_RECODE_INSN_CLASS_ADDITIVE;
flags_group.tme_recode_flags_group_flags = _tme_sparc_flags_additive;
flags_group.tme_recode_flags_group_guest_func = _tme_sparc_flags_func_sub;
ic->tme_sparc_recode_flags_thunk_sub
= tme_recode_flags_thunk(recode_ic,
&flags_group);
/* make the flags thunk for the logical instructions: */
flags_group.tme_recode_flags_group_insn_class = TME_RECODE_INSN_CLASS_LOGICAL;
flags_group.tme_recode_flags_group_flags = _tme_sparc_flags_logical;
flags_group.tme_recode_flags_group_guest_func = _tme_sparc_flags_func_logical;
ic->tme_sparc_recode_flags_thunk_logical
= tme_recode_flags_thunk(recode_ic,
&flags_group);
/* make the common parts of a conditions group: */
memset((char *) &conds_group, 0, sizeof(conds_group));
conds_group.tme_recode_conds_group_flags_reg_size = flags_group.tme_recode_flags_group_flags_reg_size;
conds_group.tme_recode_conds_group_flags_reg = flags_group.tme_recode_flags_group_flags_reg;
conds_group.tme_recode_conds_group_cond_count = TME_SPARC_COND_NOT;
/* make the conditions thunk for %icc: */
conds_group.tme_recode_conds_group_flags = TME_SPARC_ICC(TME_SPARC64_CCR_ICC);
conds_group.tme_recode_conds_group_guest_func = _tme_sparc_recode_conds_func_icc;
ic->tme_sparc_recode_conds_thunk_icc
= tme_recode_conds_thunk(recode_ic,
&conds_group);
/* if this is a sparc64: */
if (TME_SPARC_VERSION(ic) >= 9) {
/* make the flags thunk for the sparc64 internal "rcc" flags.
this is identical to the flags thunk for the logical
instructions, except it uses the internal "rcc" register: */
flags_group.tme_recode_flags_group_flags_reg = TME_SPARC64_IREG_RCC << 3;
ic->tme_sparc_recode_flags_thunk_rcc
= tme_recode_flags_thunk(recode_ic,
&flags_group);
/* make the conditions thunk for %xcc: */
conds_group.tme_recode_conds_group_flags = TME_SPARC64_CCR_XCC;
conds_group.tme_recode_conds_group_guest_func = _tme_sparc64_recode_conds_func_xcc;
ic->tme_sparc_recode_conds_thunk_xcc
= tme_recode_conds_thunk(recode_ic,
&conds_group);
/* make the conditions thunk for the internal "rcc" flags. this
is identical to the conditions thunk for %xcc, except it uses
the internal "rcc" register: */
conds_group.tme_recode_conds_group_flags_reg = flags_group.tme_recode_flags_group_flags_reg;
ic->tme_sparc_recode_conds_thunk_rcc
= tme_recode_conds_thunk(recode_ic,
&conds_group);
}
}