mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 19:12:58 -04:00
982 lines
35 KiB
C
982 lines
35 KiB
C
/* $Id: recode-regs.c,v 1.5 2010/06/05 19:09:05 fredette Exp $ */
|
|
|
|
/* libtme/recode-insn.c - generic recode instruction support: */
|
|
|
|
/*
|
|
* Copyright (c) 2007 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.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: recode-regs.c,v 1.5 2010/06/05 19:09:05 fredette Exp $");
|
|
|
|
#if TME_HAVE_RECODE
|
|
|
|
/* includes: */
|
|
#include "recode-impl.h"
|
|
|
|
/* this frees all host registers, starting from the given host
|
|
register: */
|
|
void
|
|
tme_recode_regs_host_free_many(struct tme_recode_ic *ic,
|
|
unsigned long reg_host)
|
|
{
|
|
int freed_all;
|
|
unsigned long reg_host_spacing;
|
|
tme_uint32_t ruses;
|
|
unsigned long reg_guest;
|
|
tme_uint32_t reginfo_all;
|
|
const tme_uint16_t *ruses_record;
|
|
unsigned long ruses_record_next;
|
|
|
|
/* remember if we're freeing all host registers: */
|
|
freed_all = (reg_host == TME_RECODE_REG_HOST(0));
|
|
|
|
reg_host_spacing
|
|
= (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
|
|
? 2
|
|
: 1);
|
|
|
|
reg_host &= (0 - reg_host_spacing);
|
|
|
|
/* loop over the host registers: */
|
|
do {
|
|
|
|
/* if this host register has a guest register: */
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host];
|
|
assert (ruses != TME_RECODE_REG_RUSES_RESERVED);
|
|
if (ruses != TME_RECODE_REG_RUSES_FREE) {
|
|
|
|
/* get the guest register: */
|
|
reg_guest = ic->tme_recode_ic_reg_host_to_reg_guest[reg_host];
|
|
|
|
/* get all of the guest register's information: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_all;
|
|
|
|
/* the guest register must have valid tags for this host register: */
|
|
assert ((reginfo_all & TME_RECODE_REGINFO_TAGS_VALID)
|
|
&& TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all) == reg_host);
|
|
|
|
/* write back the read-uses count for the guest register and
|
|
invalidate its tags: */
|
|
ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses
|
|
= ruses;
|
|
|
|
/* this host register no longer has any guest register: */
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host]
|
|
= TME_RECODE_REG_RUSES_FREE;
|
|
|
|
/* if the host register is dirty: */
|
|
if (TME_RECODE_REGINFO_TAGS_ARE_DIRTY(reginfo_all)) {
|
|
|
|
/* store the host register: */
|
|
tme_recode_host_reg_move(ic,
|
|
reg_guest,
|
|
reginfo_all);
|
|
}
|
|
}
|
|
} while ((reg_host += reg_host_spacing) < TME_RECODE_REG_HOST_UNDEF);
|
|
|
|
/* if all host registers are now free: */
|
|
if (freed_all) {
|
|
|
|
/* get as many initial read-uses records as we can: */
|
|
ruses_record
|
|
= (ic->tme_recode_ic_reg_guest_ruses_records
|
|
+ ic->tme_recode_ic_reg_guest_ruses_record_next);
|
|
for (;;) {
|
|
|
|
/* if the next read-uses record isn't an initial read-uses
|
|
record, stop now: */
|
|
reg_guest = *ruses_record;
|
|
reg_guest -= TME_RECODE_REG_RUSES_RECORD_REG_GUEST(0);
|
|
if (reg_guest
|
|
>= (TME_RECODE_REG_RUSES_RECORD_UNDEF
|
|
- TME_RECODE_REG_RUSES_RECORD_REG_GUEST(0))) {
|
|
break;
|
|
}
|
|
|
|
/* get what must be the read-uses part of an initial read-uses record: */
|
|
ruses = ruses_record[1];
|
|
assert (ruses >= (TME_RECODE_REG_RUSES_FREE + 1)
|
|
&& ruses < TME_RECODE_REG_RUSES_RESERVED);
|
|
|
|
/* set this guest register's read-uses count: */
|
|
ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses = ruses;
|
|
|
|
/* advance: */
|
|
ruses_record += 2;
|
|
}
|
|
|
|
/* get the index of the read-uses record that we stopped on: */
|
|
ruses_record_next
|
|
= (ruses_record
|
|
- ic->tme_recode_ic_reg_guest_ruses_records);
|
|
|
|
/* the read-uses record that we stopped on is either a write
|
|
read-uses record, a delimiter between groups of initial
|
|
read-uses records, or the terminator at the end of the
|
|
read-uses records. we want to skip past a delimiter but not
|
|
the terminator, which are both
|
|
TME_RECODE_REG_RUSES_RECORD_UNDEF, so when we see that we have
|
|
to make sure it's not the terminator: */
|
|
if (reg_guest
|
|
== (TME_RECODE_REG_RUSES_RECORD_UNDEF
|
|
- TME_RECODE_REG_RUSES_RECORD_REG_GUEST(0))) {
|
|
ruses_record_next
|
|
+= (ruses_record_next
|
|
< ic->tme_recode_ic_reg_guest_ruses_record_count);
|
|
}
|
|
|
|
/* set the index of the next read-uses record: */
|
|
ic->tme_recode_ic_reg_guest_ruses_record_next = ruses_record_next;
|
|
}
|
|
}
|
|
|
|
/* this cleans all dirty host registers, freeing those that weren't
|
|
dirty at the full guest register size: */
|
|
void
|
|
tme_recode_regs_host_clean_all(struct tme_recode_ic *ic)
|
|
{
|
|
unsigned long reg_host;
|
|
unsigned long reg_host_spacing;
|
|
tme_uint32_t ruses_tags;
|
|
unsigned long reg_guest;
|
|
tme_uint32_t reginfo_all;
|
|
|
|
reg_host = 0;
|
|
|
|
reg_host_spacing
|
|
= (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
|
|
? 2
|
|
: 1);
|
|
|
|
/* loop over the host registers: */
|
|
do {
|
|
|
|
/* if this host register has a guest register: */
|
|
ruses_tags = ic->tme_recode_ic_reg_host_to_ruses[reg_host];
|
|
assert (ruses_tags != TME_RECODE_REG_RUSES_RESERVED);
|
|
if (ruses_tags != TME_RECODE_REG_RUSES_FREE) {
|
|
|
|
/* get the guest register: */
|
|
reg_guest = ic->tme_recode_ic_reg_host_to_reg_guest[reg_host];
|
|
|
|
/* get all of the guest register's information: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_all;
|
|
|
|
/* the guest register must have valid tags for this host register: */
|
|
assert ((reginfo_all & TME_RECODE_REGINFO_TAGS_VALID)
|
|
&& TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all) == reg_host);
|
|
|
|
/* if the host register is dirty: */
|
|
if (TME_RECODE_REGINFO_TAGS_ARE_DIRTY(reginfo_all)) {
|
|
|
|
/* update the tags for the guest register to mark it as clean,
|
|
without changing its valid size: */
|
|
ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses
|
|
= (reginfo_all
|
|
+ TME_RECODE_REGINFO_TAGS_CLEAN);
|
|
|
|
/* store the host register: */
|
|
tme_recode_host_reg_move(ic,
|
|
reg_guest,
|
|
reginfo_all);
|
|
}
|
|
}
|
|
} while ((reg_host += reg_host_spacing) < TME_RECODE_REG_HOST_UNDEF);
|
|
}
|
|
|
|
/* this returns any best host register with the smallest read-uses
|
|
count less than the given count: */
|
|
unsigned long
|
|
tme_recode_regs_host_best(const struct tme_recode_ic *ic,
|
|
tme_uint32_t ruses_best)
|
|
{
|
|
unsigned long reg_host_spacing;
|
|
unsigned long reg_host_other;
|
|
unsigned long reg_host;
|
|
tme_uint32_t ruses;
|
|
|
|
reg_host_spacing
|
|
= (TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
|
|
? 2
|
|
: 1);
|
|
|
|
reg_host_other = (TME_RECODE_REG_HOST_UNDEF - reg_host_spacing) & (0 - reg_host_spacing);
|
|
reg_host = TME_RECODE_REG_HOST_UNDEF;
|
|
do {
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host_other];
|
|
if (ruses < ruses_best) {
|
|
reg_host = reg_host_other;
|
|
ruses_best = ruses;
|
|
}
|
|
} while (((long) (reg_host_other -= reg_host_spacing)) >= 0);
|
|
|
|
return (reg_host);
|
|
}
|
|
|
|
/* this frees and returns a specific host register: */
|
|
unsigned long
|
|
tme_recode_regs_host_free_specific(struct tme_recode_ic *ic,
|
|
unsigned long reg_host)
|
|
{
|
|
tme_uint32_t ruses;
|
|
unsigned long reg_guest;
|
|
tme_uint32_t reginfo_all;
|
|
|
|
/* if this host register has a guest register: */
|
|
assert (reg_host < TME_RECODE_REG_HOST_UNDEF
|
|
&& (!TME_RECODE_SIZE_IS_DOUBLE_HOST(ic->tme_recode_ic_reg_size)
|
|
|| (reg_host % 2) == 0));
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host];
|
|
assert (ruses != TME_RECODE_REG_RUSES_RESERVED);
|
|
if (ruses != TME_RECODE_REG_RUSES_FREE) {
|
|
|
|
/* get the guest register: */
|
|
reg_guest = ic->tme_recode_ic_reg_host_to_reg_guest[reg_host];
|
|
|
|
/* get all of the guest register's information: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_all;
|
|
|
|
/* the guest register must have valid tags for this host register: */
|
|
assert ((reginfo_all & TME_RECODE_REGINFO_TAGS_VALID)
|
|
&& TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all) == reg_host);
|
|
|
|
/* write back the read-uses count for the guest register and
|
|
invalidate its tags: */
|
|
ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses
|
|
= ruses;
|
|
|
|
/* this host register no longer has any guest register: */
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host]
|
|
= TME_RECODE_REG_RUSES_FREE;
|
|
|
|
/* if the host register is dirty: */
|
|
if (TME_RECODE_REGINFO_TAGS_ARE_DIRTY(reginfo_all)) {
|
|
|
|
/* store the host register: */
|
|
tme_recode_host_reg_move(ic,
|
|
reg_guest,
|
|
reginfo_all);
|
|
}
|
|
}
|
|
|
|
return (reg_host);
|
|
}
|
|
|
|
/* this frees and returns any host register: */
|
|
#define tme_recode_regs_host_free_any(ic) tme_recode_regs_host_free_specific(ic, tme_recode_regs_host_best(ic, TME_RECODE_REG_RUSES_RESERVED))
|
|
|
|
/* this reserves one host register, and returns the host register: */
|
|
unsigned long
|
|
tme_recode_regs_host_reserve(struct tme_recode_ic *ic,
|
|
unsigned long reg_host)
|
|
{
|
|
unsigned long reserve_i;
|
|
tme_uint32_t ruses;
|
|
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host];
|
|
assert (ruses != TME_RECODE_REG_RUSES_RESERVED);
|
|
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host] = TME_RECODE_REG_RUSES_RESERVED;
|
|
|
|
reserve_i = ic->tme_recode_ic_reg_host_reserve_next;
|
|
assert (reserve_i < TME_ARRAY_ELS(ic->tme_recode_ic_reg_host_reserve));
|
|
ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_reg_host = reg_host;
|
|
ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_ruses = ruses;
|
|
ic->tme_recode_ic_reg_host_reserve_next = reserve_i + 1;
|
|
|
|
return (reg_host);
|
|
}
|
|
|
|
/* this unreserves all host registers: */
|
|
void
|
|
tme_recode_regs_host_unreserve_all(struct tme_recode_ic *ic)
|
|
{
|
|
unsigned long reserve_i;
|
|
unsigned long reg_host;
|
|
|
|
for (reserve_i = ic->tme_recode_ic_reg_host_reserve_next;
|
|
((long) --reserve_i) >= 0;) {
|
|
reg_host = ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_reg_host;
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host]
|
|
= ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_ruses;
|
|
}
|
|
ic->tme_recode_ic_reg_host_reserve_next = 0;
|
|
}
|
|
|
|
/* this loads a source guest register into its host register, and
|
|
reserves and returns the host register: */
|
|
static unsigned long
|
|
_tme_recode_regs_src_load(struct tme_recode_ic *ic,
|
|
const struct tme_recode_insn *insn,
|
|
unsigned long reg_guest)
|
|
{
|
|
tme_uint32_t reginfo_all;
|
|
unsigned long reg_host;
|
|
tme_uint32_t ruses;
|
|
unsigned long reserve_i;
|
|
|
|
/* the guest register must already have a host register: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_all;
|
|
assert (reginfo_all & TME_RECODE_REGINFO_TAGS_VALID);
|
|
|
|
/* if the host register is not valid at the instruction size: */
|
|
if (__tme_predict_false(!TME_RECODE_REGINFO_TAGS_ARE_VALID_SIZE(reginfo_all,
|
|
insn->tme_recode_insn_size))) {
|
|
|
|
/* if the host register is dirty: */
|
|
if (TME_RECODE_REGINFO_TAGS_ARE_DIRTY(reginfo_all)) {
|
|
|
|
/* store the host register: */
|
|
tme_recode_host_reg_move(ic,
|
|
reg_guest,
|
|
reginfo_all);
|
|
}
|
|
|
|
/* get the host register: */
|
|
reg_host = TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all);
|
|
|
|
/* get the read-uses count for the guest register: */
|
|
/* NB: this will be TME_RECODE_REG_RUSES_RESERVED if the host
|
|
register is already reserved: */
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host];
|
|
|
|
/* clear the valid size and clean information: */
|
|
reginfo_all
|
|
&= ~ (tme_uint32_t) (TME_RECODE_REGINFO_TAGS_VALID_SIZE_MASK
|
|
+ TME_RECODE_REGINFO_TAGS_CLEAN);
|
|
|
|
/* if the read-uses count for this guest register is more than one
|
|
(which includes the case when the host register is already
|
|
reserved), load the entire guest register, otherwise only load
|
|
the size that this instruction needs: */
|
|
/* NB: the only way that an already-reserved host register can be
|
|
not valid at the instruction size is if the instruction size
|
|
has changed since the host register was reserved. this should
|
|
be very rare, making the cost of loading the entire guest
|
|
register in that case also rare: */
|
|
reginfo_all
|
|
+= TME_RECODE_REGINFO_TAGS_VALID_SIZE(ruses > (TME_RECODE_REG_RUSES_FREE + 1)
|
|
? ic->tme_recode_ic_reg_size
|
|
: insn->tme_recode_insn_size);
|
|
|
|
/* set the clean information: */
|
|
reginfo_all += TME_RECODE_REGINFO_TAGS_CLEAN;
|
|
|
|
/* load the guest register: */
|
|
tme_recode_host_reg_move(ic,
|
|
reg_guest,
|
|
reginfo_all);
|
|
|
|
/* update the tags for the guest register: */
|
|
ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses
|
|
= reginfo_all;
|
|
}
|
|
|
|
/* get the host register: */
|
|
reg_host = TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all);
|
|
|
|
/* get the read-uses count for the guest register: */
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host];
|
|
|
|
/* if this host register has already been reserved: */
|
|
if (__tme_predict_false(ruses == TME_RECODE_REG_RUSES_RESERVED)) {
|
|
|
|
/* in between two writes of a guest temporary register, the
|
|
guest temporary register may be read at most once. if a
|
|
guest temporary register has a reserved host register, it
|
|
has been read more than once: */
|
|
assert (!(reginfo_all & TME_RECODE_REGINFO_TYPE_TEMPORARY));
|
|
|
|
/* find the reservation for this host register: */
|
|
reserve_i = ic->tme_recode_ic_reg_host_reserve_next;
|
|
do {
|
|
assert (reserve_i > 0);
|
|
reserve_i--;
|
|
} while (ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_reg_host
|
|
!= reg_host);
|
|
|
|
/* update the read-uses count for the guest register. we can't
|
|
let the read-uses count fall to TME_RECODE_REG_USES_FREE, since
|
|
the host register might be dirty: */
|
|
ruses = ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_ruses;
|
|
if (ruses > (TME_RECODE_REG_RUSES_FREE + 1)) {
|
|
ruses = ruses - 1;
|
|
}
|
|
ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_ruses = ruses;
|
|
|
|
/* return the host register, which has already been reserved: */
|
|
return (reg_host);
|
|
}
|
|
|
|
/* if the guest register is a temporary: */
|
|
if (reginfo_all & TME_RECODE_REGINFO_TYPE_TEMPORARY) {
|
|
|
|
/* write back a dummy read-uses count for the guest temporary
|
|
register: */
|
|
ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_tags_ruses
|
|
= (TME_RECODE_REG_RUSES_FREE + 1);
|
|
|
|
/* this host register no longer has any guest register: */
|
|
ruses = TME_RECODE_REG_RUSES_FREE;
|
|
}
|
|
|
|
/* otherwise, the guest register is not a temporary: */
|
|
else {
|
|
|
|
/* update the read-uses count for the guest register. we can't let
|
|
the read-uses count fall to TME_RECODE_REG_RUSES_FREE, since the
|
|
host register might be dirty: */
|
|
if (ruses > (TME_RECODE_REG_RUSES_FREE + 1)) {
|
|
ruses = ruses - 1;
|
|
}
|
|
}
|
|
|
|
/* update the read-uses count for the host register: */
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host] = ruses;
|
|
|
|
/* reserve and return the host register: */
|
|
return (tme_recode_regs_host_reserve(ic, reg_host));
|
|
}
|
|
|
|
/* this notifies about a source operand before a call to
|
|
tme_recode_regs_src_any() or tme_recode_regs_src_specific(). calls
|
|
to this function aren't needed for correctness, but they may enable
|
|
optimizations. if called, the next call to
|
|
tme_recode_regs_src_any() or tme_recode_regs_src_specific() *must*
|
|
be for the same source operand: */
|
|
void
|
|
tme_recode_regs_src_notify(struct tme_recode_ic *ic,
|
|
const struct tme_recode_insn *insn,
|
|
signed long operand_src)
|
|
{
|
|
tme_uint32_t reginfo_all;
|
|
|
|
/* get all of the source operand's register information. NB that
|
|
the non-register source operands also have entries in
|
|
tme_recode_ic_reginfo[], as fixed registers, with invalid
|
|
tags: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_all;
|
|
|
|
/* if the source operand is a guest register that has a host
|
|
register: */
|
|
if (reginfo_all & TME_RECODE_REGINFO_TAGS_VALID) {
|
|
assert (operand_src >= TME_RECODE_REG_GUEST(0));
|
|
|
|
/* if the source operand is a guest temporary register, and its
|
|
host register is valid at the instruction size: */
|
|
if ((reginfo_all & TME_RECODE_REGINFO_TYPE_TEMPORARY)
|
|
&& TME_RECODE_REGINFO_TAGS_ARE_VALID_SIZE(reginfo_all,
|
|
insn->tme_recode_insn_size)) {
|
|
|
|
/* force the host register to be clean: */
|
|
/* NB: in most cases, the guest temporary register handling in
|
|
tme_recode_regs_src_specific() and
|
|
_tme_recode_regs_src_load() is enough to make sure that guest
|
|
temporary registers aren't unnecessarily stored. this is
|
|
usually needed when an insn needs to call
|
|
tme_recode_regs_host_clean_all() before setting up its
|
|
registers. tme_recode_regs_host_clean_all() must store any
|
|
dirty guest temporary registers: */
|
|
/* NB: this forcing the host register to be clean is why the
|
|
next call to tme_recode_regs_src_any() or
|
|
tme_recode_regs_src_specific() *must* be for the same source
|
|
operand. if it isn't, this host register might be reused for
|
|
that other source operand, with the unstored value of the
|
|
guest temporary register lost as a result: */
|
|
reginfo_all |= TME_RECODE_REGINFO_TAGS_CLEAN;
|
|
}
|
|
}
|
|
|
|
/* update the source operand's register information: */
|
|
assert (operand_src >= TME_RECODE_REG_GUEST(0)
|
|
|| reginfo_all == ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_all);
|
|
ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_all = reginfo_all;
|
|
}
|
|
|
|
/* this loads a source operand into any host register, and reserves
|
|
and returns the host register: */
|
|
unsigned long
|
|
tme_recode_regs_src_any(struct tme_recode_ic *ic,
|
|
const struct tme_recode_insn *insn,
|
|
signed long operand_src)
|
|
{
|
|
tme_uint32_t reginfo_all;
|
|
unsigned long reg_host;
|
|
|
|
/* get all of the source operand's register information. NB that
|
|
the non-register source operands also have entries in
|
|
tme_recode_ic_reginfo[], as fixed registers, with invalid
|
|
tags: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_all;
|
|
|
|
/* assume that this source operand has a host register: */
|
|
reg_host = TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all);
|
|
|
|
/* if this source operand does not have a host register: */
|
|
if (!(reginfo_all & TME_RECODE_REGINFO_TAGS_VALID)) {
|
|
|
|
/* free any host register: */
|
|
reg_host = tme_recode_regs_host_free_any(ic);
|
|
}
|
|
|
|
/* if this source operand is a guest register: */
|
|
if (operand_src >= TME_RECODE_REG_GUEST(0)) {
|
|
|
|
/* if the guest register did not have a host register: */
|
|
if (!(ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_all & TME_RECODE_REGINFO_TAGS_VALID)) {
|
|
|
|
/* this host register now has this guest register: */
|
|
ic->tme_recode_ic_reg_host_to_reg_guest[reg_host] = operand_src;
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host]
|
|
= ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_tags_ruses;
|
|
|
|
/* this guest register now has this host register: */
|
|
ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_tags_ruses
|
|
= (TME_RECODE_REGINFO_TAGS_VALID
|
|
+ TME_RECODE_REGINFO_TAGS_REG_HOST(reg_host));
|
|
}
|
|
|
|
/* load this guest register into the host register, and reserve
|
|
and return the host register: */
|
|
return (_tme_recode_regs_src_load(ic,
|
|
insn,
|
|
operand_src));
|
|
}
|
|
|
|
/* if this source operand is an immediate: */
|
|
if (operand_src == TME_RECODE_OPERAND_IMM) {
|
|
|
|
/* fill this host register with the immediate, and reserve and
|
|
return the host register: */
|
|
return (tme_recode_host_reg_imm(ic, insn, reg_host));
|
|
}
|
|
|
|
/* this source operand must be a zero: */
|
|
assert (operand_src == TME_RECODE_OPERAND_ZERO);
|
|
|
|
/* zero this host register, and reserve and return the host register: */
|
|
return (tme_recode_host_reg_zero(ic, insn, reg_host));
|
|
}
|
|
|
|
/* this loads a source operand into a specific host register, and
|
|
reserves and returns that host register: */
|
|
unsigned long
|
|
tme_recode_regs_src_specific(struct tme_recode_ic *ic,
|
|
const struct tme_recode_insn *insn,
|
|
signed long operand_src,
|
|
unsigned long reg_host_specific)
|
|
{
|
|
tme_uint32_t ruses;
|
|
tme_uint32_t reginfo_all;
|
|
unsigned long reg_host;
|
|
|
|
/* if this specific host register has a guest register: */
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host_specific];
|
|
assert (ruses != TME_RECODE_REG_RUSES_RESERVED);
|
|
if (__tme_predict_true(ruses != TME_RECODE_REG_RUSES_FREE)) {
|
|
|
|
/* if that guest register is the source operand: */
|
|
if (ic->tme_recode_ic_reg_host_to_reg_guest[reg_host_specific] == operand_src) {
|
|
|
|
/* reload this guest register into the host register if needed,
|
|
and reserve and return the host register: */
|
|
return (_tme_recode_regs_src_load(ic,
|
|
insn,
|
|
operand_src));
|
|
}
|
|
|
|
/* free the specific host register: */
|
|
tme_recode_regs_host_free_specific(ic, reg_host_specific);
|
|
}
|
|
|
|
/* if this source operand is a guest register: */
|
|
if (operand_src >= 0) {
|
|
|
|
/* get all of the guest register's information: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_all;
|
|
|
|
/* if the guest register has a host register: */
|
|
if (reginfo_all & TME_RECODE_REGINFO_TAGS_VALID) {
|
|
|
|
/* get the other host register: */
|
|
reg_host = TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all);
|
|
|
|
/* the other host register can't be the specific host register,
|
|
because we were supposed to catch that above: */
|
|
assert(reg_host != reg_host_specific);
|
|
|
|
/* if the other host register is valid at the instruction
|
|
size: */
|
|
if (__tme_predict_true(TME_RECODE_REGINFO_TAGS_ARE_VALID_SIZE(reginfo_all,
|
|
insn->tme_recode_insn_size))) {
|
|
|
|
/* if the guest register is a temporary: */
|
|
if (reginfo_all & TME_RECODE_REGINFO_TYPE_TEMPORARY) {
|
|
|
|
/* in between two writes of a guest temporary register, the
|
|
guest temporary register may be read at most once. if a
|
|
guest temporary register has a reserved host register, it
|
|
has been read more than once: */
|
|
assert (ic->tme_recode_ic_reg_host_to_ruses[reg_host] != TME_RECODE_REG_RUSES_RESERVED);
|
|
|
|
/* this host register no longer has any guest register: */
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host] = TME_RECODE_REG_RUSES_FREE;
|
|
|
|
/* write back a dummy read-uses count for the guest
|
|
temporary register: */
|
|
ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_tags_ruses
|
|
= (TME_RECODE_REG_RUSES_FREE + 1);
|
|
}
|
|
|
|
/* copy the other host register into the specific host register: */
|
|
tme_recode_host_reg_copy(ic,
|
|
reg_host,
|
|
reg_host_specific);
|
|
|
|
/* reserve and return the specific host register: */
|
|
return (tme_recode_regs_host_reserve(ic, reg_host_specific));
|
|
}
|
|
|
|
/* free the other host register: */
|
|
tme_recode_regs_host_free_specific(ic, reg_host);
|
|
|
|
/* get all of the guest register's information again: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_all;
|
|
}
|
|
|
|
/* the specific host register now has this guest register: */
|
|
ic->tme_recode_ic_reg_host_to_reg_guest[reg_host_specific] = operand_src;
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host_specific]
|
|
= TME_RECODE_REGINFO_RUSES(reginfo_all);
|
|
|
|
/* this guest register now has this specific host register: */
|
|
ic->tme_recode_ic_reginfo[operand_src].tme_recode_reginfo_tags_ruses
|
|
= (TME_RECODE_REGINFO_TAGS_VALID
|
|
+ TME_RECODE_REGINFO_TAGS_REG_HOST(reg_host_specific));
|
|
|
|
/* load this guest register into the specific host register, and
|
|
reserve and return the specific host register: */
|
|
return (_tme_recode_regs_src_load(ic,
|
|
insn,
|
|
operand_src));
|
|
}
|
|
|
|
/* if this source operand is an immediate: */
|
|
if (operand_src == TME_RECODE_OPERAND_IMM) {
|
|
|
|
/* fill this host register with the immediate, and reserve and
|
|
return the host register: */
|
|
return (tme_recode_host_reg_imm(ic, insn, reg_host_specific));
|
|
}
|
|
|
|
/* this source operand must be a zero: */
|
|
assert (operand_src == TME_RECODE_OPERAND_ZERO);
|
|
|
|
/* zero this host register, and reserve and return the host register: */
|
|
return (tme_recode_host_reg_zero(ic, insn, reg_host_specific));
|
|
}
|
|
|
|
/* this allocates any host register for the destination operand, and
|
|
returns the host register: */
|
|
unsigned long
|
|
tme_recode_regs_dst_any(struct tme_recode_ic *ic,
|
|
const struct tme_recode_insn *insn)
|
|
{
|
|
signed long operand_dst;
|
|
tme_uint32_t reginfo_all;
|
|
unsigned long reg_host;
|
|
unsigned long ruses_record_next;
|
|
tme_uint32_t ruses;
|
|
|
|
/* get the destination operand: */
|
|
operand_dst = insn->tme_recode_insn_operand_dst;
|
|
|
|
/* get all of the destination operand's register information. NB
|
|
that the non-register destination operands also have entries in
|
|
tme_recode_ic_reginfo[], as fixed registers, with invalid
|
|
tags: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[operand_dst].tme_recode_reginfo_all;
|
|
|
|
/* assume that this destination operand has a host register: */
|
|
reg_host = TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all);
|
|
|
|
/* if this destination operand is fixed or doesn't have a host
|
|
register: */
|
|
if ((reginfo_all
|
|
& (TME_RECODE_REGINFO_TYPE_FIXED
|
|
+ TME_RECODE_REGINFO_TAGS_VALID))
|
|
!= (!TME_RECODE_REGINFO_TYPE_FIXED
|
|
+ TME_RECODE_REGINFO_TAGS_VALID)) {
|
|
|
|
/* free any host register: */
|
|
reg_host = tme_recode_regs_host_free_any(ic);
|
|
}
|
|
|
|
/* unreserve all registers: */
|
|
tme_recode_regs_host_unreserve_all(ic);
|
|
|
|
/* if the destination operand is not fixed: */
|
|
if ((reginfo_all & TME_RECODE_REGINFO_TYPE_FIXED) == 0) {
|
|
|
|
/* if the guest register has a host register: */
|
|
if (reginfo_all & TME_RECODE_REGINFO_TAGS_VALID) {
|
|
|
|
/* if the guest register has a dirty size greater than this
|
|
instruction's size: */
|
|
if (TME_RECODE_REGINFO_TAGS_WHICH_DIRTY_SIZE(reginfo_all)
|
|
> insn->tme_recode_insn_size) {
|
|
|
|
/* store the guest register: */
|
|
tme_recode_host_reg_move(ic,
|
|
operand_dst,
|
|
reginfo_all);
|
|
}
|
|
}
|
|
|
|
/* otherwise, the guest register did not have a host register: */
|
|
else {
|
|
|
|
/* this host register now has this guest register: */
|
|
ic->tme_recode_ic_reg_host_to_reg_guest[reg_host] = operand_dst;
|
|
}
|
|
|
|
/* this guest register now has this host register, and is dirty at
|
|
the instruction size: */
|
|
ic->tme_recode_ic_reginfo[operand_dst].tme_recode_reginfo_tags_ruses
|
|
= (TME_RECODE_REGINFO_TAGS_VALID
|
|
+ TME_RECODE_REGINFO_TAGS_VALID_SIZE(insn->tme_recode_insn_size)
|
|
+ !TME_RECODE_REGINFO_TAGS_CLEAN
|
|
+ TME_RECODE_REGINFO_TAGS_REG_HOST(reg_host));
|
|
|
|
/* get what should be a write read-uses record: */
|
|
ruses_record_next = ic->tme_recode_ic_reg_guest_ruses_record_next;
|
|
ruses = ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_next];
|
|
ic->tme_recode_ic_reg_guest_ruses_record_next = ruses_record_next + 1;
|
|
|
|
/* if this wasn't a write read-uses record: */
|
|
if (__tme_predict_false(ruses >= TME_RECODE_REG_RUSES_RESERVED)) {
|
|
|
|
/* this only happens when we ran out of space for read-uses
|
|
records. back up the read-uses record index, and make up a
|
|
read-uses count. this doesn't affect correctness, only
|
|
performance: */
|
|
ic->tme_recode_ic_reg_guest_ruses_record_next--;
|
|
ruses = (TME_RECODE_REG_RUSES_FREE + 1) + 2;
|
|
}
|
|
|
|
/* set the new read-uses count for this guest register: */
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host] = ruses;
|
|
}
|
|
|
|
/* return this host register: */
|
|
return (reg_host);
|
|
}
|
|
|
|
/* this allocates a specific host register for the destination operand
|
|
and returns the host register: */
|
|
unsigned long
|
|
tme_recode_regs_dst_specific(struct tme_recode_ic *ic,
|
|
const struct tme_recode_insn *insn,
|
|
unsigned long reserve_i)
|
|
{
|
|
signed long operand_dst;
|
|
tme_uint32_t reginfo_all;
|
|
unsigned long reg_host;
|
|
tme_uint32_t ruses_tags;
|
|
tme_uint32_t ruses;
|
|
unsigned long reg_host_specific;
|
|
unsigned long reg_guest;
|
|
unsigned long ruses_record_next;
|
|
|
|
/* get the destination operand: */
|
|
operand_dst = insn->tme_recode_insn_operand_dst;
|
|
|
|
/* get all of the destination operand's register information. NB
|
|
that the non-register destination operands also have entries in
|
|
tme_recode_ic_reginfo[], as fixed registers, with invalid
|
|
tags: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[operand_dst].tme_recode_reginfo_all;
|
|
|
|
/* if this destination operand has a host register: */
|
|
if (__tme_predict_true(reginfo_all & TME_RECODE_REGINFO_TAGS_VALID)) {
|
|
|
|
/* get the host register: */
|
|
reg_host = TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all);
|
|
|
|
/* assume that this host register is reserved, and that we will
|
|
rewrite the tags for this guest register so that it's neither
|
|
dirty nor clean: */
|
|
ruses_tags = TME_RECODE_REGINFO_TAGS_VALID + reg_host;
|
|
|
|
/* if this host register is not reserved: */
|
|
ruses = ic->tme_recode_ic_reg_host_to_ruses[reg_host];
|
|
assert (ruses != TME_RECODE_REG_RUSES_FREE);
|
|
if (ruses != TME_RECODE_REG_RUSES_RESERVED) {
|
|
|
|
/* we will rewrite the tags for this guest register so that it
|
|
doesn't have a host register: */
|
|
ruses_tags = ruses;
|
|
|
|
/* this host register is free: */
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host] = TME_RECODE_REG_RUSES_FREE;
|
|
}
|
|
|
|
/* rewrite the tags for this guest register: */
|
|
ic->tme_recode_ic_reginfo[operand_dst].tme_recode_reginfo_tags_ruses = ruses_tags;
|
|
|
|
/* if the guest register has a dirty size greater than this
|
|
instruction's size: */
|
|
if (TME_RECODE_REGINFO_TAGS_WHICH_DIRTY_SIZE(reginfo_all)
|
|
> insn->tme_recode_insn_size) {
|
|
|
|
/* store the guest register: */
|
|
tme_recode_host_reg_move(ic,
|
|
operand_dst,
|
|
reginfo_all);
|
|
}
|
|
}
|
|
|
|
/* if this specific host register has a guest register that may
|
|
still be read later: */
|
|
assert (reserve_i < ic->tme_recode_ic_reg_host_reserve_next);
|
|
ruses = ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_ruses;
|
|
assert (ruses < TME_RECODE_REG_RUSES_RESERVED);
|
|
if (ruses > (TME_RECODE_REG_RUSES_FREE + 1)) {
|
|
|
|
/* get the specific host register: */
|
|
reg_host_specific = ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_reg_host;
|
|
|
|
/* if that guest register is not the destination operand: */
|
|
if (ic->tme_recode_ic_reg_host_to_reg_guest[reg_host_specific] != operand_dst) {
|
|
|
|
/* if there is any other host register that we can free up and
|
|
copy the specific host register into: */
|
|
reg_host = tme_recode_regs_host_best(ic, ruses);
|
|
if (reg_host != TME_RECODE_REG_HOST_UNDEF) {
|
|
|
|
/* free this other host register: */
|
|
reg_host = tme_recode_regs_host_free_specific(ic, reg_host);
|
|
|
|
/* get the guest register we are copying: */
|
|
reg_guest = ic->tme_recode_ic_reg_host_to_reg_guest[reg_host_specific];
|
|
|
|
/* disconnect the guest register we are copying from the
|
|
specific host register, and connect it to this other host
|
|
register: */
|
|
ic->tme_recode_ic_reginfo[reg_guest].tme_recode_reginfo_all
|
|
+= (TME_RECODE_REGINFO_TAGS_REG_HOST(reg_host)
|
|
- TME_RECODE_REGINFO_TAGS_REG_HOST(reg_host_specific));
|
|
ic->tme_recode_ic_reg_host_to_reg_guest[reg_host] = reg_guest;
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host]
|
|
= ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_ruses;
|
|
|
|
/* arrange for the specific host register to be free after we
|
|
unreserve all registers: */
|
|
ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_ruses = TME_RECODE_REG_RUSES_FREE;
|
|
|
|
/* copy the specific host register into this other host register: */
|
|
tme_recode_host_reg_copy(ic,
|
|
reg_host_specific,
|
|
reg_host);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* unreserve all registers: */
|
|
tme_recode_regs_host_unreserve_all(ic);
|
|
|
|
/* get the specific host register: */
|
|
reg_host_specific = ic->tme_recode_ic_reg_host_reserve[reserve_i].tme_reg_host_reserve_reg_host;
|
|
|
|
/* free the specific host register: */
|
|
reg_host_specific = tme_recode_regs_host_free_specific(ic, reg_host_specific);
|
|
|
|
/* get the destination operand: */
|
|
operand_dst = insn->tme_recode_insn_operand_dst;
|
|
|
|
/* get all of the destination operand's register information. NB
|
|
that the non-register destination operands also have entries in
|
|
tme_recode_ic_reginfo[], as fixed registers, with invalid
|
|
tags: */
|
|
reginfo_all = ic->tme_recode_ic_reginfo[operand_dst].tme_recode_reginfo_all;
|
|
|
|
/* if the destination operand is not fixed: */
|
|
if (__tme_predict_true((reginfo_all & TME_RECODE_REGINFO_TYPE_FIXED) == 0)) {
|
|
|
|
/* if the guest register had a host register: */
|
|
if (reginfo_all & TME_RECODE_REGINFO_TAGS_VALID) {
|
|
|
|
/* get the host register: */
|
|
reg_host = TME_RECODE_REGINFO_TAGS_WHICH_REG_HOST(reginfo_all);
|
|
|
|
/* this guest register can't have the specific host register,
|
|
since we freed the specific host register above: */
|
|
assert (reg_host != reg_host_specific);
|
|
|
|
/* free this other host register: */
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host] = TME_RECODE_REG_RUSES_FREE;
|
|
}
|
|
|
|
/* get what should be a write read-uses record: */
|
|
ruses_record_next = ic->tme_recode_ic_reg_guest_ruses_record_next;
|
|
ruses = ic->tme_recode_ic_reg_guest_ruses_records[ruses_record_next];
|
|
ic->tme_recode_ic_reg_guest_ruses_record_next = ruses_record_next + 1;
|
|
|
|
/* if this wasn't a write read-uses record: */
|
|
if (__tme_predict_false(ruses >= TME_RECODE_REG_RUSES_RESERVED)) {
|
|
|
|
/* this only happens when we ran out of space for read-uses
|
|
records. back up the read-uses record index, and make up a
|
|
read-uses count. this doesn't affect correctness, only
|
|
performance: */
|
|
ic->tme_recode_ic_reg_guest_ruses_record_next--;
|
|
ruses = (TME_RECODE_REG_RUSES_FREE + 1) + 2;
|
|
}
|
|
|
|
ic->tme_recode_ic_reg_host_to_reg_guest[reg_host_specific] = operand_dst;
|
|
ic->tme_recode_ic_reg_host_to_ruses[reg_host_specific] = ruses;
|
|
|
|
/* this guest register now has this host register, and is dirty at
|
|
the instruction size: */
|
|
ic->tme_recode_ic_reginfo[operand_dst].tme_recode_reginfo_tags_ruses
|
|
= (TME_RECODE_REGINFO_TAGS_VALID
|
|
+ TME_RECODE_REGINFO_TAGS_VALID_SIZE(insn->tme_recode_insn_size)
|
|
+ !TME_RECODE_REGINFO_TAGS_CLEAN
|
|
+ TME_RECODE_REGINFO_TAGS_REG_HOST(reg_host_specific));
|
|
}
|
|
|
|
/* return this host register: */
|
|
return (reg_host_specific);
|
|
}
|
|
|
|
#endif /* TME_HAVE_RECODE */
|