mirror of
https://github.com/ioacademy-jikim/debugging
synced 2025-06-10 09:26:15 +00:00
487 lines
15 KiB
C
487 lines
15 KiB
C
|
|
/*---------------------------------------------------------------*/
|
|
/*--- begin host_generic_regs.h ---*/
|
|
/*---------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2004-2015 OpenWorks LLP
|
|
info@open-works.net
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
|
|
Neither the names of the U.S. Department of Energy nor the
|
|
University of California nor the names of its contributors may be
|
|
used to endorse or promote products derived from this software
|
|
without prior written permission.
|
|
*/
|
|
|
|
#ifndef __VEX_HOST_GENERIC_REGS_H
|
|
#define __VEX_HOST_GENERIC_REGS_H
|
|
|
|
#include "libvex_basictypes.h"
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- Representing HOST REGISTERS ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
/* Host registers. Stuff to represent:
|
|
|
|
- The register index. This is a zero-based, sequential index that
|
|
facilitates indexing into arrays or virtual or real registers.
|
|
Virtual and real registers both have indices starting at zero.
|
|
Interpreting a real register index requires having the host's
|
|
RRegUniverse to hand.
|
|
|
|
- The register's hardware encoding. This applies only for real
|
|
registers and should be zero for virtual registers. This is the
|
|
number as used in a target architecture encoding.
|
|
|
|
- The register class
|
|
|
|
- Whether or not the register is a virtual reg.
|
|
|
|
Registers are sized so as to fit into 32 bits.
|
|
|
|
Note that since the class field is never 1111b, no valid register
|
|
can have the value INVALID_HREG.
|
|
|
|
There are currently 6 register classes:
|
|
|
|
int32 int64 float32 float64 simd64 simd128
|
|
*/
|
|
|
|
/* Registers are represented as 32 bit integers, with the following layout:
|
|
|
|
31 30..27 26..20 19..0
|
|
isV:1 rc:4 enc:7 ix:20
|
|
|
|
where
|
|
UInt ix:20; // Zero based index
|
|
UInt enc:7; // Hardware encoding number
|
|
HRegClass rc:4; // the register's HRegClass
|
|
Bool isV:1; // is it a virtual register?
|
|
|
|
The obvious thing to do here would be to use bitfields. But gcc
|
|
seems to have problems constant folding calls to mkHReg() with all
|
|
4 parameters constant to a 32 bit number, when using bitfields.
|
|
Hence the use of the traditional shift-and-mask by-hand bitfields
|
|
instead.
|
|
*/
|
|
typedef struct { UInt u32; } HReg;
|
|
|
|
/* HRegClass describes host register classes which the instruction
|
|
selectors can speak about. We would not expect all of them to be
|
|
available on any specific host. For example on x86, the available
|
|
classes are: Int32, Flt64, Vec128 only.
|
|
|
|
IMPORTANT NOTE: host_generic_reg_alloc2.c needs how much space is
|
|
needed to spill each class of register. It allocates the following
|
|
amount of space:
|
|
|
|
HRcInt32 64 bits
|
|
HRcInt64 64 bits
|
|
HRcFlt32 64 bits
|
|
HRcFlt64 128 bits (on x86 these are spilled by fstpt/fldt and
|
|
so won't fit in a 64-bit slot)
|
|
HRcVec64 64 bits
|
|
HRcVec128 128 bits
|
|
|
|
If you add another regclass, you must remember to update
|
|
host_generic_reg_alloc2.c accordingly.
|
|
|
|
When adding entries to enum HRegClass, do not use any value > 14 or < 1.
|
|
*/
|
|
typedef
|
|
enum {
|
|
HRcINVALID=1, /* NOT A VALID REGISTER CLASS */
|
|
HRcInt32=3, /* 32-bit int */
|
|
HRcInt64=4, /* 64-bit int */
|
|
HRcFlt32=5, /* 32-bit float */
|
|
HRcFlt64=6, /* 64-bit float */
|
|
HRcVec64=7, /* 64-bit SIMD */
|
|
HRcVec128=8 /* 128-bit SIMD */
|
|
}
|
|
HRegClass;
|
|
|
|
extern void ppHRegClass ( HRegClass );
|
|
|
|
|
|
/* Print an HReg in a generic (non-target-specific) way. */
|
|
extern void ppHReg ( HReg );
|
|
|
|
/* Construct. The goal here is that compiler can fold this down to a
|
|
constant in the case where the four arguments are constants, which
|
|
is often the case. */
|
|
static inline HReg mkHReg ( Bool virtual, HRegClass rc, UInt enc, UInt ix )
|
|
{
|
|
vassert(ix <= 0xFFFFF);
|
|
vassert(enc <= 0x7F);
|
|
vassert(((UInt)rc) <= 0xF);
|
|
vassert(((UInt)virtual) <= 1);
|
|
if (virtual) vassert(enc == 0);
|
|
HReg r;
|
|
r.u32 = ((((UInt)virtual) & 1) << 31) |
|
|
((((UInt)rc) & 0xF) << 27) |
|
|
((((UInt)enc) & 0x7F) << 20) |
|
|
((((UInt)ix) & 0xFFFFF) << 0);
|
|
return r;
|
|
}
|
|
|
|
static inline HRegClass hregClass ( HReg r )
|
|
{
|
|
HRegClass rc = (HRegClass)((r.u32 >> 27) & 0xF);
|
|
vassert(rc >= HRcInt32 && rc <= HRcVec128);
|
|
return rc;
|
|
}
|
|
|
|
static inline UInt hregIndex ( HReg r )
|
|
{
|
|
return r.u32 & 0xFFFFF;
|
|
}
|
|
|
|
static inline UInt hregEncoding ( HReg r )
|
|
{
|
|
return (r.u32 >> 20) & 0x7F;
|
|
}
|
|
|
|
static inline Bool hregIsVirtual ( HReg r )
|
|
{
|
|
return toBool((r.u32 >> 31) & 1);
|
|
}
|
|
|
|
static inline Bool sameHReg ( HReg r1, HReg r2 )
|
|
{
|
|
return toBool(r1.u32 == r2.u32);
|
|
}
|
|
|
|
static const HReg INVALID_HREG = { .u32 = 0xFFFFFFFF };
|
|
|
|
static inline Bool hregIsInvalid ( HReg r )
|
|
{
|
|
return sameHReg(r, INVALID_HREG);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- Real register Universes. ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
/* A "Real Register Universe" is a read-only structure that contains
|
|
all information about real registers on a given host. It serves
|
|
several purposes:
|
|
|
|
* defines the mapping from real register indices to the registers
|
|
themselves
|
|
|
|
* defines the size of the initial section of that mapping that is
|
|
available to the register allocator for use, so that the register
|
|
allocator can treat the registers under its control as a zero
|
|
based, contiguous array. This is important for its efficiency.
|
|
|
|
* gives meaning to RRegSets, which otherwise would merely be a
|
|
bunch of bits.
|
|
|
|
This is a big structure, but it's readonly, and we expect to
|
|
allocate only one instance for each run of Valgrind. It is sized
|
|
so as to be able to deal with up to 64 real registers. AFAICS none
|
|
of the back ends actually mention more than 64, despite the fact
|
|
that many of the host architectures have more than 64 registers
|
|
when all classes are taken into consideration.
|
|
*/
|
|
|
|
#define N_RREGUNIVERSE_REGS 64
|
|
|
|
typedef
|
|
struct {
|
|
/* Total number of registers in this universe .. */
|
|
UInt size;
|
|
/* .. of which the first |allocable| are available to regalloc. */
|
|
UInt allocable;
|
|
/* The registers themselves. All must be real registers, and
|
|
all must have their index number (.s.ix) equal to the array
|
|
index here, since this is the only place where we map index
|
|
numbers to actual registers. */
|
|
HReg regs[N_RREGUNIVERSE_REGS];
|
|
}
|
|
RRegUniverse;
|
|
|
|
/* Nominally initialise (zero out) an RRegUniverse. */
|
|
void RRegUniverse__init ( /*OUT*/RRegUniverse* );
|
|
|
|
/* Check an RRegUniverse is valid, and assert if not.*/
|
|
void RRegUniverse__check_is_sane ( const RRegUniverse* );
|
|
|
|
/* Print an RRegUniverse, for debugging. */
|
|
void RRegUniverse__show ( const RRegUniverse* );
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- Real register sets. ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
/* Represents sets of real registers. |bitset| is interpreted in the
|
|
context of |univ|. That is, each bit index |i| in |bitset|
|
|
corresponds to the register |univ->regs[i]|. This relies
|
|
entirely on the fact that N_RREGUNIVERSE_REGS <= 64. */
|
|
typedef
|
|
struct {
|
|
ULong bitset;
|
|
RRegUniverse* univ;
|
|
}
|
|
RRegSet;
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- Recording register usage (for reg-alloc) ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
typedef
|
|
enum { HRmRead, HRmWrite, HRmModify }
|
|
HRegMode;
|
|
|
|
|
|
/* This isn't entirely general, and is specialised towards being fast,
|
|
for the reg-alloc. It represents real registers using a bitmask
|
|
and can also represent up to four virtual registers, in an
|
|
unordered array. This is based on the observation that no
|
|
instruction that we generate can mention more than four registers
|
|
at once.
|
|
*/
|
|
#define N_HREGUSAGE_VREGS 5
|
|
|
|
typedef
|
|
struct {
|
|
/* The real registers. The associated universe is not stored
|
|
here -- callers will have to pass it around separately, as
|
|
needed. */
|
|
ULong rRead; /* real regs that are read */
|
|
ULong rWritten; /* real regs that are written */
|
|
/* The virtual registers. */
|
|
HReg vRegs[N_HREGUSAGE_VREGS];
|
|
HRegMode vMode[N_HREGUSAGE_VREGS];
|
|
UInt n_vRegs;
|
|
}
|
|
HRegUsage;
|
|
|
|
extern void ppHRegUsage ( const RRegUniverse*, HRegUsage* );
|
|
|
|
static inline void initHRegUsage ( HRegUsage* tab )
|
|
{
|
|
tab->rRead = 0;
|
|
tab->rWritten = 0;
|
|
tab->n_vRegs = 0;
|
|
}
|
|
|
|
/* Add a register to a usage table. Combine incoming read uses with
|
|
existing write uses into a modify use, and vice versa. Do not
|
|
create duplicate entries -- each reg should only be mentioned once.
|
|
*/
|
|
extern void addHRegUse ( HRegUsage*, HRegMode, HReg );
|
|
|
|
extern Bool HRegUsage__contains ( const HRegUsage*, HReg );
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- Indicating register remappings (for reg-alloc) ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
/* Note that such maps can only map virtual regs to real regs.
|
|
addToHRegRenap will barf if given a pair not of that form. As a
|
|
result, no valid HRegRemap will bind a real reg to anything, and so
|
|
if lookupHRegMap is given a real reg, it returns it unchanged.
|
|
This is precisely the behaviour that the register allocator needs
|
|
to impose its decisions on the instructions it processes. */
|
|
|
|
#define N_HREG_REMAP 6
|
|
|
|
typedef
|
|
struct {
|
|
HReg orig [N_HREG_REMAP];
|
|
HReg replacement[N_HREG_REMAP];
|
|
Int n_used;
|
|
}
|
|
HRegRemap;
|
|
|
|
extern void ppHRegRemap ( HRegRemap* );
|
|
extern void addToHRegRemap ( HRegRemap*, HReg, HReg );
|
|
extern HReg lookupHRegRemap ( HRegRemap*, HReg );
|
|
|
|
static inline void initHRegRemap ( HRegRemap* map )
|
|
{
|
|
map->n_used = 0;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- Abstract instructions ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
/* A type is needed to refer to pointers to instructions of any
|
|
target. Defining it like this means that HInstr* can stand in for
|
|
X86Instr*, ArmInstr*, etc. */
|
|
|
|
typedef void HInstr;
|
|
|
|
|
|
/* An expandable array of HInstr*'s. Handy for insn selection and
|
|
register allocation. n_vregs indicates the number of virtual
|
|
registers mentioned in the code, something that reg-alloc needs to
|
|
know. These are required to be numbered 0 .. n_vregs-1.
|
|
*/
|
|
typedef
|
|
struct {
|
|
HInstr** arr;
|
|
Int arr_size;
|
|
Int arr_used;
|
|
Int n_vregs;
|
|
}
|
|
HInstrArray;
|
|
|
|
extern HInstrArray* newHInstrArray ( void );
|
|
|
|
/* Never call this directly. It's the slow and incomplete path for
|
|
addHInstr. */
|
|
__attribute__((noinline))
|
|
extern void addHInstr_SLOW ( HInstrArray*, HInstr* );
|
|
|
|
static inline void addHInstr ( HInstrArray* ha, HInstr* instr )
|
|
{
|
|
if (LIKELY(ha->arr_used < ha->arr_size)) {
|
|
ha->arr[ha->arr_used] = instr;
|
|
ha->arr_used++;
|
|
} else {
|
|
addHInstr_SLOW(ha, instr);
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- C-Call return-location descriptions ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
/* This is common to all back ends. It describes where the return
|
|
value from a C call is located. This is important in the case that
|
|
the call is conditional, since the return locations will need to be
|
|
set to 0x555..555 in the case that the call does not happen. */
|
|
|
|
typedef
|
|
enum {
|
|
RLPri_INVALID, /* INVALID */
|
|
RLPri_None, /* no return value (a.k.a C "void") */
|
|
RLPri_Int, /* in the primary int return reg */
|
|
RLPri_2Int, /* in both primary and secondary int ret regs */
|
|
RLPri_V128SpRel, /* 128-bit value, on the stack */
|
|
RLPri_V256SpRel /* 256-bit value, on the stack */
|
|
}
|
|
RetLocPrimary;
|
|
|
|
typedef
|
|
struct {
|
|
/* Primary description */
|
|
RetLocPrimary pri;
|
|
/* For .pri == RLPri_V128SpRel or RLPri_V256SpRel only, gives
|
|
the offset of the lowest addressed byte of the value,
|
|
relative to the stack pointer. For all other .how values,
|
|
has no meaning and should be zero. */
|
|
Int spOff;
|
|
}
|
|
RetLoc;
|
|
|
|
extern void ppRetLoc ( RetLoc rloc );
|
|
|
|
static inline RetLoc mk_RetLoc_simple ( RetLocPrimary pri ) {
|
|
vassert(pri >= RLPri_INVALID && pri <= RLPri_2Int);
|
|
return (RetLoc){pri, 0};
|
|
}
|
|
|
|
static inline RetLoc mk_RetLoc_spRel ( RetLocPrimary pri, Int off ) {
|
|
vassert(pri >= RLPri_V128SpRel && pri <= RLPri_V256SpRel);
|
|
return (RetLoc){pri, off};
|
|
}
|
|
|
|
static inline Bool is_sane_RetLoc ( RetLoc rloc ) {
|
|
switch (rloc.pri) {
|
|
case RLPri_None: case RLPri_Int: case RLPri_2Int:
|
|
return rloc.spOff == 0;
|
|
case RLPri_V128SpRel: case RLPri_V256SpRel:
|
|
return True;
|
|
default:
|
|
return False;
|
|
}
|
|
}
|
|
|
|
static inline RetLoc mk_RetLoc_INVALID ( void ) {
|
|
return (RetLoc){RLPri_INVALID, 0};
|
|
}
|
|
|
|
static inline Bool is_RetLoc_INVALID ( RetLoc rl ) {
|
|
return rl.pri == RLPri_INVALID && rl.spOff == 0;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------*/
|
|
/*--- Reg alloc: TODO: move somewhere else ---*/
|
|
/*---------------------------------------------------------*/
|
|
|
|
extern
|
|
HInstrArray* doRegisterAllocation (
|
|
|
|
/* Incoming virtual-registerised code. */
|
|
HInstrArray* instrs_in,
|
|
|
|
/* The real-register universe to use. This contains facts about
|
|
real registers, one of which is the set of registers available
|
|
for allocation. */
|
|
const RRegUniverse* univ,
|
|
|
|
/* Return True iff the given insn is a reg-reg move, in which
|
|
case also return the src and dst regs. */
|
|
Bool (*isMove) (const HInstr*, HReg*, HReg*),
|
|
|
|
/* Get info about register usage in this insn. */
|
|
void (*getRegUsage) (HRegUsage*, const HInstr*, Bool),
|
|
|
|
/* Apply a reg-reg mapping to an insn. */
|
|
void (*mapRegs) (HRegRemap*, HInstr*, Bool),
|
|
|
|
/* Return insn(s) to spill/restore a real reg to a spill slot
|
|
offset. And optionally a function to do direct reloads. */
|
|
void (*genSpill) ( HInstr**, HInstr**, HReg, Int, Bool ),
|
|
void (*genReload) ( HInstr**, HInstr**, HReg, Int, Bool ),
|
|
HInstr* (*directReload) ( HInstr*, HReg, Short ),
|
|
Int guest_sizeB,
|
|
|
|
/* For debug printing only. */
|
|
void (*ppInstr) ( const HInstr*, Bool ),
|
|
void (*ppReg) ( HReg ),
|
|
|
|
/* 32/64bit mode */
|
|
Bool mode64
|
|
);
|
|
|
|
|
|
#endif /* ndef __VEX_HOST_GENERIC_REGS_H */
|
|
|
|
/*---------------------------------------------------------------*/
|
|
/*--- host_generic_regs.h ---*/
|
|
/*---------------------------------------------------------------*/
|