gem5/arch/alpha/isa_desc
Steve Reinhardt 0aaf8ec6b8 Add support for CPU models to execute the effective
address calculation and memory access portions separately.
Not currently used by any CPU models, but Kevin says he needs this.

Also clean up handling of execution tracing for memory accesses
(move it all into isa_desc and out of CPU models).

Got rid of some ancient unused code too.

arch/alpha/isa_desc:
    Add execute() methods to EAComp and MemAcc portions of memory
    access instructions, to allow CPU models to execute the effective
    address calculation and memory access portions separately.

    Requires the execution context to remember the effective address
    across the two invocations.  Added setEA() and getEA() methods to
    execution context to support this.  A model that does not use the
    split execution model can panic if these methods are called.

    Also added hook to call traceData->setAddr() after EA computation
    on any load or store operation.
arch/isa_parser.py:
    Call traceData->setData() on memory writes (stores).
cpu/simple_cpu/simple_cpu.cc:
    Get rid of unused code.
cpu/simple_cpu/simple_cpu.hh:
    Add (non-functional) setEA() and getEA() methods for new
    split memory access execution support.

--HG--
extra : convert_revision : bc2d2c758c4ca753812b9fa81f21038e55929ff0
2005-02-03 20:47:11 -05:00

2616 lines
72 KiB
C++

// -*- mode:c++ -*-
////////////////////////////////////////////////////////////////////
//
// Alpha ISA description file.
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
//
// Output include file directives.
//
output header {{
#include <sstream>
#include <iostream>
#include <iomanip>
#include "cpu/static_inst.hh"
#include "mem/mem_req.hh" // some constructors use MemReq flags
}};
output decoder {{
#include "base/cprintf.hh"
#include "base/loader/symtab.hh"
#include "cpu/exec_context.hh" // for Jump::branchTarget()
#include <math.h>
#if defined(linux)
#include <fenv.h>
#endif
}};
output exec {{
#include <math.h>
#if defined(linux)
#include <fenv.h>
#endif
#ifdef FULL_SYSTEM
#include "arch/alpha/pseudo_inst.hh"
#endif
#include "cpu/base_cpu.hh"
#include "cpu/exetrace.hh"
#include "sim/sim_exit.hh"
}};
////////////////////////////////////////////////////////////////////
//
// Namespace statement. Everything below this line will be in the
// AlphaISAInst namespace.
//
namespace AlphaISA;
////////////////////////////////////////////////////////////////////
//
// Bitfield definitions.
//
// Universal (format-independent) fields
def bitfield OPCODE <31:26>;
def bitfield RA <25:21>;
def bitfield RB <20:16>;
// Memory format
def signed bitfield MEMDISP <15: 0>; // displacement
def bitfield MEMFUNC <15: 0>; // function code (same field, unsigned)
// Memory-format jumps
def bitfield JMPFUNC <15:14>; // function code (disp<15:14>)
def bitfield JMPHINT <13: 0>; // tgt Icache idx hint (disp<13:0>)
// Branch format
def signed bitfield BRDISP <20: 0>; // displacement
// Integer operate format(s>;
def bitfield INTIMM <20:13>; // integer immediate (literal)
def bitfield IMM <12:12>; // immediate flag
def bitfield INTFUNC <11: 5>; // function code
def bitfield RC < 4: 0>; // dest reg
// Floating-point operate format
def bitfield FA <25:21>;
def bitfield FB <20:16>;
def bitfield FP_FULLFUNC <15: 5>; // complete function code
def bitfield FP_TRAPMODE <15:13>; // trapping mode
def bitfield FP_ROUNDMODE <12:11>; // rounding mode
def bitfield FP_TYPEFUNC <10: 5>; // type+func: handiest for decoding
def bitfield FP_SRCTYPE <10: 9>; // source reg type
def bitfield FP_SHORTFUNC < 8: 5>; // short function code
def bitfield FP_SHORTFUNC_TOP2 <8:7>; // top 2 bits of short func code
def bitfield FC < 4: 0>; // dest reg
// PALcode format
def bitfield PALFUNC <25: 0>; // function code
// EV5 PAL instructions:
// HW_LD/HW_ST
def bitfield HW_LDST_PHYS <15>; // address is physical
def bitfield HW_LDST_ALT <14>; // use ALT_MODE IPR
def bitfield HW_LDST_WRTCK <13>; // HW_LD only: fault if no write acc
def bitfield HW_LDST_QUAD <12>; // size: 0=32b, 1=64b
def bitfield HW_LDST_VPTE <11>; // HW_LD only: is PTE fetch
def bitfield HW_LDST_LOCK <10>; // HW_LD only: is load locked
def bitfield HW_LDST_COND <10>; // HW_ST only: is store conditional
def signed bitfield HW_LDST_DISP <9:0>; // signed displacement
// HW_REI
def bitfield HW_REI_TYP <15:14>; // type: stalling vs. non-stallingk
def bitfield HW_REI_MBZ <13: 0>; // must be zero
// HW_MTPR/MW_MFPR
def bitfield HW_IPR_IDX <15:0>; // IPR index
// M5 instructions
def bitfield M5FUNC <7:0>;
def operand_types {{
'sb' : ('signed int', 8),
'ub' : ('unsigned int', 8),
'sw' : ('signed int', 16),
'uw' : ('unsigned int', 16),
'sl' : ('signed int', 32),
'ul' : ('unsigned int', 32),
'sq' : ('signed int', 64),
'uq' : ('unsigned int', 64),
'sf' : ('float', 32),
'df' : ('float', 64)
}};
def operands {{
# Int regs default to unsigned, but code should not count on this.
# For clarity, descriptions that depend on unsigned behavior should
# explicitly specify '.uq'.
'Ra': IntRegOperandTraits('uq', 'RA', 'IsInteger', 1),
'Rb': IntRegOperandTraits('uq', 'RB', 'IsInteger', 2),
'Rc': IntRegOperandTraits('uq', 'RC', 'IsInteger', 3),
'Fa': FloatRegOperandTraits('df', 'FA', 'IsFloating', 1),
'Fb': FloatRegOperandTraits('df', 'FB', 'IsFloating', 2),
'Fc': FloatRegOperandTraits('df', 'FC', 'IsFloating', 3),
'Mem': MemOperandTraits('uq', None,
('IsMemRef', 'IsLoad', 'IsStore'), 4),
'NPC': NPCOperandTraits('uq', None, ( None, None, 'IsControl' ), 4),
'Runiq': ControlRegOperandTraits('uq', 'Uniq', None, 1),
'FPCR': ControlRegOperandTraits('uq', 'Fpcr', None, 1),
# The next two are hacks for non-full-system call-pal emulation
'R0': IntRegOperandTraits('uq', '0', None, 1),
'R16': IntRegOperandTraits('uq', '16', None, 1)
}};
////////////////////////////////////////////////////////////////////
//
// Basic instruction classes/templates/formats etc.
//
output header {{
// uncomment the following to get SimpleScalar-compatible disassembly
// (useful for diffing output traces).
// #define SS_COMPATIBLE_DISASSEMBLY
/**
* Base class for all Alpha static instructions.
*/
class AlphaStaticInst : public StaticInst<AlphaISA>
{
protected:
/// Make AlphaISA register dependence tags directly visible in
/// this class and derived classes. Maybe these should really
/// live here and not in the AlphaISA namespace.
enum DependenceTags {
FP_Base_DepTag = AlphaISA::FP_Base_DepTag,
Fpcr_DepTag = AlphaISA::Fpcr_DepTag,
Uniq_DepTag = AlphaISA::Uniq_DepTag,
IPR_Base_DepTag = AlphaISA::IPR_Base_DepTag
};
/// Constructor.
AlphaStaticInst(const char *mnem, MachInst _machInst,
OpClass __opClass)
: StaticInst<AlphaISA>(mnem, _machInst, __opClass)
{
}
/// Print a register name for disassembly given the unique
/// dependence tag number (FP or int).
void printReg(std::ostream &os, int reg);
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
void
AlphaStaticInst::printReg(std::ostream &os, int reg)
{
if (reg < FP_Base_DepTag) {
ccprintf(os, "r%d", reg);
}
else {
ccprintf(os, "f%d", reg - FP_Base_DepTag);
}
}
std::string
AlphaStaticInst::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
std::stringstream ss;
ccprintf(ss, "%-10s ", mnemonic);
// just print the first two source regs... if there's
// a third one, it's a read-modify-write dest (Rc),
// e.g. for CMOVxx
if (_numSrcRegs > 0) {
printReg(ss, _srcRegIdx[0]);
}
if (_numSrcRegs > 1) {
ss << ",";
printReg(ss, _srcRegIdx[1]);
}
// just print the first dest... if there's a second one,
// it's generally implicit
if (_numDestRegs > 0) {
if (_numSrcRegs > 0)
ss << ",";
printReg(ss, _destRegIdx[0]);
}
return ss.str();
}
}};
// Declarations for execute() methods.
def template BasicExecDeclare {{
Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *);
}};
// Basic instruction class declaration template.
def template BasicDeclare {{
/**
* Static instruction class for "%(mnemonic)s".
*/
class %(class_name)s : public %(base_class)s
{
public:
/// Constructor.
%(class_name)s(MachInst machInst);
%(BasicExecDeclare)s
};
}};
// Basic instruction class constructor template.
def template BasicConstructor {{
inline %(class_name)s::%(class_name)s(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
}
}};
// Basic instruction class execute method template.
def template BasicExecute {{
Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
Trace::InstRecord *traceData)
{
Fault fault = No_Fault;
%(fp_enable_check)s;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == No_Fault) {
%(op_wb)s;
}
return fault;
}
}};
// Basic decode template.
def template BasicDecode {{
return new %(class_name)s(machInst);
}};
// Basic decode template, passing mnemonic in as string arg to constructor.
def template BasicDecodeWithMnemonic {{
return new %(class_name)s("%(mnemonic)s", machInst);
}};
// The most basic instruction format... used only for a few misc. insts
def format BasicOperate(code, *flags) {{
iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), flags)
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
decode_block = BasicDecode.subst(iop)
exec_output = BasicExecute.subst(iop)
}};
////////////////////////////////////////////////////////////////////
//
// Nop
//
output header {{
/**
* Static instruction class for no-ops. This is a leaf class.
*/
class Nop : public AlphaStaticInst
{
/// Disassembly of original instruction.
const std::string originalDisassembly;
public:
/// Constructor
Nop(const std::string _originalDisassembly, MachInst _machInst)
: AlphaStaticInst("nop", _machInst, No_OpClass),
originalDisassembly(_originalDisassembly)
{
flags[IsNop] = true;
}
~Nop() { }
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
%(BasicExecDeclare)s
};
}};
output decoder {{
std::string Nop::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
#ifdef SS_COMPATIBLE_DISASSEMBLY
return originalDisassembly;
#else
return csprintf("%-10s (%s)", "nop", originalDisassembly);
#endif
}
/// Helper function for decoding nops. Substitute Nop object
/// for original inst passed in as arg (and delete latter).
inline
AlphaStaticInst *
makeNop(AlphaStaticInst *inst)
{
AlphaStaticInst *nop = new Nop(inst->disassemble(0), inst->machInst);
delete inst;
return nop;
}
}};
output exec {{
Fault
Nop::execute(%(CPU_exec_context)s *, Trace::InstRecord *)
{
return No_Fault;
}
}};
// integer & FP operate instructions use Rc as dest, so check for
// Rc == 31 to detect nops
def template OperateNopCheckDecode {{
{
AlphaStaticInst *i = new %(class_name)s(machInst);
if (RC == 31) {
i = makeNop(i);
}
return i;
}
}};
// Like BasicOperate format, but generates NOP if RC/FC == 31
def format BasicOperateWithNopCheck(code, *opt_args) {{
iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code),
opt_args)
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
decode_block = OperateNopCheckDecode.subst(iop)
exec_output = BasicExecute.subst(iop)
}};
////////////////////////////////////////////////////////////////////
//
// Integer operate instructions
//
output header {{
/**
* Base class for integer immediate instructions.
*/
class IntegerImm : public AlphaStaticInst
{
protected:
/// Immediate operand value (unsigned 8-bit int).
uint8_t imm;
/// Constructor
IntegerImm(const char *mnem, MachInst _machInst, OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass), imm(INTIMM)
{
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
std::string
IntegerImm::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
std::stringstream ss;
ccprintf(ss, "%-10s ", mnemonic);
// just print the first source reg... if there's
// a second one, it's a read-modify-write dest (Rc),
// e.g. for CMOVxx
if (_numSrcRegs > 0) {
printReg(ss, _srcRegIdx[0]);
ss << ",";
}
ss << (int)imm;
if (_numDestRegs > 0) {
ss << ",";
printReg(ss, _destRegIdx[0]);
}
return ss.str();
}
}};
def template RegOrImmDecode {{
{
AlphaStaticInst *i =
(IMM) ? (AlphaStaticInst *)new %(class_name)sImm(machInst)
: (AlphaStaticInst *)new %(class_name)s(machInst);
if (RC == 31) {
i = makeNop(i);
}
return i;
}
}};
// Primary format for integer operate instructions:
// - Generates both reg-reg and reg-imm versions if Rb_or_imm is used.
// - Generates NOP if RC == 31.
def format IntegerOperate(code, *opt_flags) {{
# If the code block contains 'Rb_or_imm', we define two instructions,
# one using 'Rb' and one using 'imm', and have the decoder select
# the right one.
uses_imm = (code.find('Rb_or_imm') != -1)
if uses_imm:
orig_code = code
# base code is reg version:
# rewrite by substituting 'Rb' for 'Rb_or_imm'
code = re.sub(r'Rb_or_imm', 'Rb', orig_code)
# generate immediate version by substituting 'imm'
# note that imm takes no extenstion, so we extend
# the regexp to replace any extension as well
imm_code = re.sub(r'Rb_or_imm(\.\w+)?', 'imm', orig_code)
# generate declaration for register version
cblk = CodeBlock(code)
iop = InstObjParams(name, Name, 'AlphaStaticInst', cblk, opt_flags)
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
exec_output = BasicExecute.subst(iop)
if uses_imm:
# append declaration for imm version
imm_cblk = CodeBlock(imm_code)
imm_iop = InstObjParams(name, Name + 'Imm', 'IntegerImm', imm_cblk,
opt_flags)
header_output += BasicDeclare.subst(imm_iop)
decoder_output += BasicConstructor.subst(imm_iop)
exec_output += BasicExecute.subst(imm_iop)
# decode checks IMM bit to pick correct version
decode_block = RegOrImmDecode.subst(iop)
else:
# no imm version: just check for nop
decode_block = OperateNopCheckDecode.subst(iop)
}};
////////////////////////////////////////////////////////////////////
//
// Floating-point instructions
//
// Note that many FP-type instructions which do not support all the
// various rounding & trapping modes use the simpler format
// BasicOperateWithNopCheck.
//
output exec {{
/// Check "FP enabled" machine status bit. Called when executing any FP
/// instruction in full-system mode.
/// @retval Full-system mode: No_Fault if FP is enabled, Fen_Fault
/// if not. Non-full-system mode: always returns No_Fault.
#ifdef FULL_SYSTEM
inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc)
{
Fault fault = No_Fault; // dummy... this ipr access should not fault
if (!EV5::ICSR_FPE(xc->readIpr(AlphaISA::IPR_ICSR, fault))) {
fault = Fen_Fault;
}
return fault;
}
#else
inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc)
{
return No_Fault;
}
#endif
}};
output header {{
/**
* Base class for general floating-point instructions. Includes
* support for various Alpha rounding and trapping modes. Only FP
* instructions that require this support are derived from this
* class; the rest derive directly from AlphaStaticInst.
*/
class AlphaFP : public AlphaStaticInst
{
public:
/// Alpha FP rounding modes.
enum RoundingMode {
Chopped = 0, ///< round toward zero
Minus_Infinity = 1, ///< round toward minus infinity
Normal = 2, ///< round to nearest (default)
Dynamic = 3, ///< use FPCR setting (in instruction)
Plus_Infinity = 3 ///< round to plus inifinity (in FPCR)
};
/// Alpha FP trapping modes.
/// For instructions that produce integer results, the
/// "Underflow Enable" modes really mean "Overflow Enable", and
/// the assembly modifier is V rather than U.
enum TrappingMode {
/// default: nothing enabled
Imprecise = 0, ///< no modifier
/// underflow/overflow traps enabled, inexact disabled
Underflow_Imprecise = 1, ///< /U or /V
Underflow_Precise = 5, ///< /SU or /SV
/// underflow/overflow and inexact traps enabled
Underflow_Inexact_Precise = 7 ///< /SUI or /SVI
};
protected:
#if defined(linux)
static const int alphaToC99RoundingMode[];
#endif
/// Map enum RoundingMode values to disassembly suffixes.
static const char *roundingModeSuffix[];
/// Map enum TrappingMode values to FP disassembly suffixes.
static const char *fpTrappingModeSuffix[];
/// Map enum TrappingMode values to integer disassembly suffixes.
static const char *intTrappingModeSuffix[];
/// This instruction's rounding mode.
RoundingMode roundingMode;
/// This instruction's trapping mode.
TrappingMode trappingMode;
/// Constructor
AlphaFP(const char *mnem, MachInst _machInst, OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass),
roundingMode((enum RoundingMode)FP_ROUNDMODE),
trappingMode((enum TrappingMode)FP_TRAPMODE)
{
if (trappingMode != Imprecise) {
warn("precise FP traps unimplemented\n");
}
}
#if defined(linux)
int getC99RoundingMode(uint64_t fpcr_val);
#endif
// This differs from the AlphaStaticInst version only in
// printing suffixes for non-default rounding & trapping modes.
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
def template FloatingPointDecode {{
{
bool fast = (FP_TRAPMODE == AlphaFP::Imprecise
&& FP_ROUNDMODE == AlphaFP::Normal);
AlphaStaticInst *i =
fast ? (AlphaStaticInst *)new %(class_name)sFast(machInst) :
(AlphaStaticInst *)new %(class_name)sGeneral(machInst);
if (FC == 31) {
i = makeNop(i);
}
return i;
}
}};
output decoder {{
#if defined(linux)
int
AlphaFP::getC99RoundingMode(uint64_t fpcr_val)
{
if (roundingMode == Dynamic) {
return alphaToC99RoundingMode[bits(fpcr_val, 59, 58)];
}
else {
return alphaToC99RoundingMode[roundingMode];
}
}
#endif
std::string
AlphaFP::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
std::string mnem_str(mnemonic);
#ifndef SS_COMPATIBLE_DISASSEMBLY
std::string suffix("");
suffix += ((_destRegIdx[0] >= FP_Base_DepTag)
? fpTrappingModeSuffix[trappingMode]
: intTrappingModeSuffix[trappingMode]);
suffix += roundingModeSuffix[roundingMode];
if (suffix != "") {
mnem_str = csprintf("%s/%s", mnemonic, suffix);
}
#endif
std::stringstream ss;
ccprintf(ss, "%-10s ", mnem_str.c_str());
// just print the first two source regs... if there's
// a third one, it's a read-modify-write dest (Rc),
// e.g. for CMOVxx
if (_numSrcRegs > 0) {
printReg(ss, _srcRegIdx[0]);
}
if (_numSrcRegs > 1) {
ss << ",";
printReg(ss, _srcRegIdx[1]);
}
// just print the first dest... if there's a second one,
// it's generally implicit
if (_numDestRegs > 0) {
if (_numSrcRegs > 0)
ss << ",";
printReg(ss, _destRegIdx[0]);
}
return ss.str();
}
#if defined(linux)
const int AlphaFP::alphaToC99RoundingMode[] = {
FE_TOWARDZERO, // Chopped
FE_DOWNWARD, // Minus_Infinity
FE_TONEAREST, // Normal
FE_UPWARD // Dynamic in inst, Plus_Infinity in FPCR
};
#endif
const char *AlphaFP::roundingModeSuffix[] = { "c", "m", "", "d" };
// mark invalid trapping modes, but don't fail on them, because
// you could decode anything on a misspeculated path
const char *AlphaFP::fpTrappingModeSuffix[] =
{ "", "u", "INVTM2", "INVTM3", "INVTM4", "su", "INVTM6", "sui" };
const char *AlphaFP::intTrappingModeSuffix[] =
{ "", "v", "INVTM2", "INVTM3", "INVTM4", "sv", "INVTM6", "svi" };
}};
// General format for floating-point operate instructions:
// - Checks trapping and rounding mode flags. Trapping modes
// currently unimplemented (will fail).
// - Generates NOP if FC == 31.
def format FloatingPointOperate(code, *opt_args) {{
iop = InstObjParams(name, Name, 'AlphaFP', CodeBlock(code), opt_args)
decode_block = FloatingPointDecode.subst(iop)
fast_iop = InstObjParams(name, Name + 'Fast', 'AlphaFP',
CodeBlock(code), opt_args)
header_output = BasicDeclare.subst(fast_iop)
decoder_output = BasicConstructor.subst(fast_iop)
exec_output = BasicExecute.subst(fast_iop)
gen_code_prefix = r'''
#if defined(linux)
fesetround(getC99RoundingMode(xc->readFpcr()));
#endif
'''
gen_code_suffix = r'''
#if defined(linux)
fesetround(FE_TONEAREST);
#endif
'''
gen_iop = InstObjParams(name, Name + 'General', 'AlphaFP',
CodeBlock(gen_code_prefix + code + gen_code_suffix), opt_args)
header_output += BasicDeclare.subst(gen_iop)
decoder_output += BasicConstructor.subst(gen_iop)
exec_output += BasicExecute.subst(gen_iop)
}};
////////////////////////////////////////////////////////////////////
//
// Memory-format instructions: LoadAddress, Load, Store
//
output header {{
/**
* Base class for general Alpha memory-format instructions.
*/
class Memory : public AlphaStaticInst
{
protected:
/// Memory request flags. See mem_req_base.hh.
unsigned memAccessFlags;
/// Pointer to EAComp object.
const StaticInstPtr<AlphaISA> eaCompPtr;
/// Pointer to MemAcc object.
const StaticInstPtr<AlphaISA> memAccPtr;
/// Constructor
Memory(const char *mnem, MachInst _machInst, OpClass __opClass,
StaticInstPtr<AlphaISA> _eaCompPtr = nullStaticInstPtr,
StaticInstPtr<AlphaISA> _memAccPtr = nullStaticInstPtr)
: AlphaStaticInst(mnem, _machInst, __opClass),
memAccessFlags(0), eaCompPtr(_eaCompPtr), memAccPtr(_memAccPtr)
{
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
public:
const StaticInstPtr<AlphaISA> &eaCompInst() const { return eaCompPtr; }
const StaticInstPtr<AlphaISA> &memAccInst() const { return memAccPtr; }
};
/**
* Base class for memory-format instructions using a 32-bit
* displacement (i.e. most of them).
*/
class MemoryDisp32 : public Memory
{
protected:
/// Displacement for EA calculation (signed).
int32_t disp;
/// Constructor.
MemoryDisp32(const char *mnem, MachInst _machInst, OpClass __opClass,
StaticInstPtr<AlphaISA> _eaCompPtr = nullStaticInstPtr,
StaticInstPtr<AlphaISA> _memAccPtr = nullStaticInstPtr)
: Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr),
disp(MEMDISP)
{
}
};
/**
* Base class for a few miscellaneous memory-format insts
* that don't interpret the disp field: wh64, fetch, fetch_m, ecb.
* None of these instructions has a destination register either.
*/
class MemoryNoDisp : public Memory
{
protected:
/// Constructor
MemoryNoDisp(const char *mnem, MachInst _machInst, OpClass __opClass,
StaticInstPtr<AlphaISA> _eaCompPtr = nullStaticInstPtr,
StaticInstPtr<AlphaISA> _memAccPtr = nullStaticInstPtr)
: Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr)
{
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
std::string
Memory::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s %c%d,%d(r%d)", mnemonic,
flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB);
}
std::string
MemoryNoDisp::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s (r%d)", mnemonic, RB);
}
}};
def format LoadAddress(code) {{
iop = InstObjParams(name, Name, 'MemoryDisp32', CodeBlock(code))
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
decode_block = BasicDecode.subst(iop)
exec_output = BasicExecute.subst(iop)
}};
def template LoadStoreDeclare {{
/**
* Static instruction class for "%(mnemonic)s".
*/
class %(class_name)s : public %(base_class)s
{
protected:
/**
* "Fake" effective address computation class for "%(mnemonic)s".
*/
class EAComp : public %(base_class)s
{
public:
/// Constructor
EAComp(MachInst machInst);
%(BasicExecDeclare)s
};
/**
* "Fake" memory access instruction class for "%(mnemonic)s".
*/
class MemAcc : public %(base_class)s
{
public:
/// Constructor
MemAcc(MachInst machInst);
%(BasicExecDeclare)s
};
public:
/// Constructor.
%(class_name)s(MachInst machInst);
%(BasicExecDeclare)s
};
}};
def template LoadStoreConstructor {{
/** TODO: change op_class to AddrGenOp or something (requires
* creating new member of OpClass enum in op_class.hh, updating
* config files, etc.). */
inline %(class_name)s::EAComp::EAComp(MachInst machInst)
: %(base_class)s("%(mnemonic)s (EAComp)", machInst, IntAluOp)
{
%(ea_constructor)s;
}
inline %(class_name)s::MemAcc::MemAcc(MachInst machInst)
: %(base_class)s("%(mnemonic)s (MemAcc)", machInst, %(op_class)s)
{
%(memacc_constructor)s;
}
inline %(class_name)s::%(class_name)s(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s,
new EAComp(machInst), new MemAcc(machInst))
{
%(constructor)s;
}
}};
def template EACompExecute {{
Fault
%(class_name)s::EAComp::execute(%(CPU_exec_context)s *xc,
Trace::InstRecord *traceData)
{
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(op_decl)s;
%(op_rd)s;
%(code)s;
if (fault == No_Fault) {
%(op_wb)s;
xc->setEA(EA);
}
return fault;
}
}};
def template MemAccExecute {{
Fault
%(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc,
Trace::InstRecord *traceData)
{
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(op_decl)s;
%(op_nonmem_rd)s;
EA = xc->getEA();
if (fault == No_Fault) {
%(op_mem_rd)s;
%(code)s;
}
if (fault == No_Fault) {
%(op_mem_wb)s;
}
if (fault == No_Fault) {
%(postacc_code)s;
}
if (fault == No_Fault) {
%(op_nonmem_wb)s;
}
return fault;
}
}};
def template LoadStoreExecute {{
Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
Trace::InstRecord *traceData)
{
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(op_decl)s;
%(op_nonmem_rd)s;
%(ea_code)s;
if (fault == No_Fault) {
%(op_mem_rd)s;
%(memacc_code)s;
}
if (fault == No_Fault) {
%(op_mem_wb)s;
}
if (fault == No_Fault) {
%(postacc_code)s;
}
if (fault == No_Fault) {
%(op_nonmem_wb)s;
}
return fault;
}
}};
def template PrefetchExecute {{
Fault %(class_name)s::execute(%(CPU_exec_context)s *xc,
Trace::InstRecord *traceData)
{
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(op_decl)s;
%(op_nonmem_rd)s;
%(ea_code)s;
if (fault == No_Fault) {
xc->prefetch(EA, memAccessFlags);
}
return No_Fault;
}
}};
// load instructions use Ra as dest, so check for
// Ra == 31 to detect nops
def template LoadNopCheckDecode {{
{
AlphaStaticInst *i = new %(class_name)s(machInst);
if (RA == 31) {
i = makeNop(i);
}
return i;
}
}};
// for some load instructions, Ra == 31 indicates a prefetch (not a nop)
def template LoadPrefetchCheckDecode {{
{
if (RA != 31) {
return new %(class_name)s(machInst);
}
else {
return new %(class_name)sPrefetch(machInst);
}
}
}};
let {{
def LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code = '',
base_class = 'MemoryDisp32', flags = [],
decode_template = BasicDecode,
exec_template = LoadStoreExecute):
# Segregate flags into instruction flags (handled by InstObjParams)
# and memory access flags (handled here).
# Would be nice to autogenerate this list, but oh well.
valid_mem_flags = ['LOCKED', 'NO_FAULT', 'EVICT_NEXT', 'PF_EXCLUSIVE']
mem_flags = [f for f in flags if f in valid_mem_flags]
inst_flags = [f for f in flags if f not in valid_mem_flags]
# add hook to get effective addresses into execution trace output.
ea_code += '\nif (traceData) { traceData->setAddr(EA); }\n'
# generate code block objects
ea_cblk = CodeBlock(ea_code)
memacc_cblk = CodeBlock(memacc_code)
postacc_cblk = CodeBlock(postacc_code)
# Some CPU models execute the memory operation as an atomic unit,
# while others want to separate them into an effective address
# computation and a memory access operation. As a result, we need
# to generate three StaticInst objects. Note that the latter two
# are nested inside the larger "atomic" one.
# generate InstObjParams for EAComp object
ea_iop = InstObjParams(name, Name, base_class, ea_cblk, inst_flags)
# generate InstObjParams for MemAcc object
memacc_iop = InstObjParams(name, Name, base_class, memacc_cblk, inst_flags)
# in the split execution model, the MemAcc portion is responsible
# for the post-access code.
memacc_iop.postacc_code = postacc_cblk.code
# generate InstObjParams for unified execution
cblk = CodeBlock(ea_code + memacc_code + postacc_code)
iop = InstObjParams(name, Name, base_class, cblk, inst_flags)
iop.ea_constructor = ea_cblk.constructor
iop.ea_code = ea_cblk.code
iop.memacc_constructor = memacc_cblk.constructor
iop.memacc_code = memacc_cblk.code
iop.postacc_code = postacc_cblk.code
if mem_flags:
s = '\n\tmemAccessFlags = ' + string.join(mem_flags, '|') + ';'
iop.constructor += s
memacc_iop.constructor += s
# (header_output, decoder_output, decode_block, exec_output)
return (LoadStoreDeclare.subst(iop), LoadStoreConstructor.subst(iop),
decode_template.subst(iop),
EACompExecute.subst(ea_iop)
+ MemAccExecute.subst(memacc_iop)
+ exec_template.subst(iop))
}};
def format LoadOrNop(ea_code, memacc_code, *flags) {{
(header_output, decoder_output, decode_block, exec_output) = \
LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags,
decode_template = LoadNopCheckDecode)
}};
// Note that the flags passed in apply only to the prefetch version
def format LoadOrPrefetch(ea_code, memacc_code, *pf_flags) {{
# declare the load instruction object and generate the decode block
(header_output, decoder_output, decode_block, exec_output) = \
LoadStoreBase(name, Name, ea_code, memacc_code,
decode_template = LoadPrefetchCheckDecode)
# Declare the prefetch instruction object.
# convert flags from tuple to list to make them mutable
pf_flags = list(pf_flags) + ['IsMemRef', 'IsLoad', 'IsDataPrefetch', 'MemReadOp', 'NO_FAULT']
(pf_header_output, pf_decoder_output, _, pf_exec_output) = \
LoadStoreBase(name, Name + 'Prefetch', ea_code, '',
flags = pf_flags, exec_template = PrefetchExecute)
header_output += pf_header_output
decoder_output += pf_decoder_output
exec_output += pf_exec_output
}};
def format Store(ea_code, memacc_code, *flags) {{
(header_output, decoder_output, decode_block, exec_output) = \
LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags)
}};
def format StoreCond(ea_code, memacc_code, postacc_code, *flags) {{
(header_output, decoder_output, decode_block, exec_output) = \
LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code,
flags = flags)
}};
// Use 'MemoryNoDisp' as base: for wh64, fetch, ecb
def format MiscPrefetch(ea_code, memacc_code, *flags) {{
(header_output, decoder_output, decode_block, exec_output) = \
LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags,
base_class = 'MemoryNoDisp')
}};
////////////////////////////////////////////////////////////////////
//
// Control transfer instructions
//
output header {{
/**
* Base class for instructions whose disassembly is not purely a
* function of the machine instruction (i.e., it depends on the
* PC). This class overrides the disassemble() method to check
* the PC and symbol table values before re-using a cached
* disassembly string. This is necessary for branches and jumps,
* where the disassembly string includes the target address (which
* may depend on the PC and/or symbol table).
*/
class PCDependentDisassembly : public AlphaStaticInst
{
protected:
/// Cached program counter from last disassembly
Addr cachedPC;
/// Cached symbol table pointer from last disassembly
const SymbolTable *cachedSymtab;
/// Constructor
PCDependentDisassembly(const char *mnem, MachInst _machInst,
OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass),
cachedPC(0), cachedSymtab(0)
{
}
const std::string &disassemble(Addr pc, const SymbolTable *symtab);
};
/**
* Base class for branches (PC-relative control transfers),
* conditional or unconditional.
*/
class Branch : public PCDependentDisassembly
{
protected:
/// Displacement to target address (signed).
int32_t disp;
/// Constructor.
Branch(const char *mnem, MachInst _machInst, OpClass __opClass)
: PCDependentDisassembly(mnem, _machInst, __opClass),
disp(BRDISP << 2)
{
}
Addr branchTarget(Addr branchPC) const;
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
/**
* Base class for jumps (register-indirect control transfers). In
* the Alpha ISA, these are always unconditional.
*/
class Jump : public PCDependentDisassembly
{
protected:
/// Displacement to target address (signed).
int32_t disp;
public:
/// Constructor
Jump(const char *mnem, MachInst _machInst, OpClass __opClass)
: PCDependentDisassembly(mnem, _machInst, __opClass),
disp(BRDISP)
{
}
Addr branchTarget(ExecContext *xc) const;
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
Addr
Branch::branchTarget(Addr branchPC) const
{
return branchPC + 4 + disp;
}
Addr
Jump::branchTarget(ExecContext *xc) const
{
Addr NPC = xc->readPC() + 4;
uint64_t Rb = xc->readIntReg(_srcRegIdx[0]);
return (Rb & ~3) | (NPC & 1);
}
const std::string &
PCDependentDisassembly::disassemble(Addr pc, const SymbolTable *symtab)
{
if (!cachedDisassembly ||
pc != cachedPC || symtab != cachedSymtab)
{
if (cachedDisassembly)
delete cachedDisassembly;
cachedDisassembly =
new std::string(generateDisassembly(pc, symtab));
cachedPC = pc;
cachedSymtab = symtab;
}
return *cachedDisassembly;
}
std::string
Branch::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
std::stringstream ss;
ccprintf(ss, "%-10s ", mnemonic);
// There's only one register arg (RA), but it could be
// either a source (the condition for conditional
// branches) or a destination (the link reg for
// unconditional branches)
if (_numSrcRegs > 0) {
printReg(ss, _srcRegIdx[0]);
ss << ",";
}
else if (_numDestRegs > 0) {
printReg(ss, _destRegIdx[0]);
ss << ",";
}
#ifdef SS_COMPATIBLE_DISASSEMBLY
if (_numSrcRegs == 0 && _numDestRegs == 0) {
printReg(ss, 31);
ss << ",";
}
#endif
Addr target = pc + 4 + disp;
std::string str;
if (symtab && symtab->findSymbol(target, str))
ss << str;
else
ccprintf(ss, "0x%x", target);
return ss.str();
}
std::string
Jump::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
std::stringstream ss;
ccprintf(ss, "%-10s ", mnemonic);
#ifdef SS_COMPATIBLE_DISASSEMBLY
if (_numDestRegs == 0) {
printReg(ss, 31);
ss << ",";
}
#endif
if (_numDestRegs > 0) {
printReg(ss, _destRegIdx[0]);
ss << ",";
}
ccprintf(ss, "(r%d)", RB);
return ss.str();
}
}};
def template JumpOrBranchDecode {{
return (RA == 31)
? (StaticInst<AlphaISA> *)new %(class_name)s(machInst)
: (StaticInst<AlphaISA> *)new %(class_name)sAndLink(machInst);
}};
def format CondBranch(code) {{
code = 'bool cond;\n' + code + '\nif (cond) NPC = NPC + disp;\n';
iop = InstObjParams(name, Name, 'Branch', CodeBlock(code),
('IsDirectControl', 'IsCondControl'))
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
decode_block = BasicDecode.subst(iop)
exec_output = BasicExecute.subst(iop)
}};
let {{
def UncondCtrlBase(name, Name, base_class, npc_expr, flags):
# Declare basic control transfer w/o link (i.e. link reg is R31)
nolink_code = 'NPC = %s;\n' % npc_expr
nolink_iop = InstObjParams(name, Name, base_class,
CodeBlock(nolink_code), flags)
header_output = BasicDeclare.subst(nolink_iop)
decoder_output = BasicConstructor.subst(nolink_iop)
exec_output = BasicExecute.subst(nolink_iop)
# Generate declaration of '*AndLink' version, append to decls
link_code = 'Ra = NPC & ~3;\n' + nolink_code
link_iop = InstObjParams(name, Name + 'AndLink', base_class,
CodeBlock(link_code), flags)
header_output += BasicDeclare.subst(link_iop)
decoder_output += BasicConstructor.subst(link_iop)
exec_output += BasicExecute.subst(link_iop)
# need to use link_iop for the decode template since it is expecting
# the shorter version of class_name (w/o "AndLink")
return (header_output, decoder_output,
JumpOrBranchDecode.subst(nolink_iop), exec_output)
}};
def format UncondBranch(*flags) {{
flags += ('IsUncondControl', 'IsDirectControl')
(header_output, decoder_output, decode_block, exec_output) = \
UncondCtrlBase(name, Name, 'Branch', 'NPC + disp', flags)
}};
def format Jump(*flags) {{
flags += ('IsUncondControl', 'IsIndirectControl')
(header_output, decoder_output, decode_block, exec_output) = \
UncondCtrlBase(name, Name, 'Jump', '(Rb & ~3) | (NPC & 1)', flags)
}};
////////////////////////////////////////////////////////////////////
//
// PAL calls
//
output header {{
/**
* Base class for emulated call_pal calls (used only in
* non-full-system mode).
*/
class EmulatedCallPal : public AlphaStaticInst
{
protected:
/// Constructor.
EmulatedCallPal(const char *mnem, MachInst _machInst,
OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass)
{
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
std::string
EmulatedCallPal::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
#ifdef SS_COMPATIBLE_DISASSEMBLY
return csprintf("%s %s", "call_pal", mnemonic);
#else
return csprintf("%-10s %s", "call_pal", mnemonic);
#endif
}
}};
def format EmulatedCallPal(code, *flags) {{
iop = InstObjParams(name, Name, 'EmulatedCallPal', CodeBlock(code), flags)
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
decode_block = BasicDecode.subst(iop)
exec_output = BasicExecute.subst(iop)
}};
output header {{
/**
* Base class for full-system-mode call_pal instructions.
* Probably could turn this into a leaf class and get rid of the
* parser template.
*/
class CallPalBase : public AlphaStaticInst
{
protected:
int palFunc; ///< Function code part of instruction
int palOffset; ///< Target PC, offset from IPR_PAL_BASE
bool palValid; ///< is the function code valid?
bool palPriv; ///< is this call privileged?
/// Constructor.
CallPalBase(const char *mnem, MachInst _machInst,
OpClass __opClass);
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
inline
CallPalBase::CallPalBase(const char *mnem, MachInst _machInst,
OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass),
palFunc(PALFUNC)
{
// From the 21164 HRM (paraphrased):
// Bit 7 of the function code (mask 0x80) indicates
// whether the call is privileged (bit 7 == 0) or
// unprivileged (bit 7 == 1). The privileged call table
// starts at 0x2000, the unprivielged call table starts at
// 0x3000. Bits 5-0 (mask 0x3f) are used to calculate the
// offset.
const int palPrivMask = 0x80;
const int palOffsetMask = 0x3f;
// Pal call is invalid unless all other bits are 0
palValid = ((machInst & ~(palPrivMask | palOffsetMask)) == 0);
palPriv = ((machInst & palPrivMask) == 0);
int shortPalFunc = (machInst & palOffsetMask);
// Add 1 to base to set pal-mode bit
palOffset = (palPriv ? 0x2001 : 0x3001) + (shortPalFunc << 6);
}
std::string
CallPalBase::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s %#x", "call_pal", palFunc);
}
}};
def format CallPal(code, *flags) {{
iop = InstObjParams(name, Name, 'CallPalBase', CodeBlock(code), flags)
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
decode_block = BasicDecode.subst(iop)
exec_output = BasicExecute.subst(iop)
}};
////////////////////////////////////////////////////////////////////
//
// hw_ld, hw_st
//
output header {{
/**
* Base class for hw_ld and hw_st.
*/
class HwLoadStore : public Memory
{
protected:
/// Displacement for EA calculation (signed).
int16_t disp;
/// Constructor
HwLoadStore(const char *mnem, MachInst _machInst, OpClass __opClass,
StaticInstPtr<AlphaISA> _eaCompPtr = nullStaticInstPtr,
StaticInstPtr<AlphaISA> _memAccPtr = nullStaticInstPtr);
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
inline
HwLoadStore::HwLoadStore(const char *mnem, MachInst _machInst,
OpClass __opClass,
StaticInstPtr<AlphaISA> _eaCompPtr,
StaticInstPtr<AlphaISA> _memAccPtr)
: Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr),
disp(HW_LDST_DISP)
{
memAccessFlags = 0;
if (HW_LDST_PHYS) memAccessFlags |= PHYSICAL;
if (HW_LDST_ALT) memAccessFlags |= ALTMODE;
if (HW_LDST_VPTE) memAccessFlags |= VPTE;
if (HW_LDST_LOCK) memAccessFlags |= LOCKED;
}
std::string
HwLoadStore::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
#ifdef SS_COMPATIBLE_DISASSEMBLY
return csprintf("%-10s r%d,%d(r%d)", mnemonic, RA, disp, RB);
#else
// HW_LDST_LOCK and HW_LDST_COND are the same bit.
const char *lock_str =
(HW_LDST_LOCK) ? (flags[IsLoad] ? ",LOCK" : ",COND") : "";
return csprintf("%-10s r%d,%d(r%d)%s%s%s%s%s",
mnemonic, RA, disp, RB,
HW_LDST_PHYS ? ",PHYS" : "",
HW_LDST_ALT ? ",ALT" : "",
HW_LDST_QUAD ? ",QUAD" : "",
HW_LDST_VPTE ? ",VPTE" : "",
lock_str);
#endif
}
}};
def format HwLoadStore(ea_code, memacc_code, class_ext, *flags) {{
(header_output, decoder_output, decode_block, exec_output) = \
LoadStoreBase(name, Name + class_ext, ea_code, memacc_code,
flags = flags, base_class = 'HwLoadStore')
}};
def format HwStoreCond(ea_code, memacc_code, postacc_code, class_ext, *flags) {{
(header_output, decoder_output, decode_block, exec_output) = \
LoadStoreBase(name, Name + class_ext, ea_code, memacc_code,
postacc_code, flags = flags, base_class = 'HwLoadStore')
}};
output header {{
/**
* Base class for hw_mfpr and hw_mtpr.
*/
class HwMoveIPR : public AlphaStaticInst
{
protected:
/// Index of internal processor register.
int ipr_index;
/// Constructor
HwMoveIPR(const char *mnem, MachInst _machInst, OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass),
ipr_index(HW_IPR_IDX)
{
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
std::string
HwMoveIPR::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
if (_numSrcRegs > 0) {
// must be mtpr
return csprintf("%-10s r%d,IPR(%#x)",
mnemonic, RA, ipr_index);
}
else {
// must be mfpr
return csprintf("%-10s IPR(%#x),r%d",
mnemonic, ipr_index, RA);
}
}
}};
def format HwMoveIPR(code) {{
iop = InstObjParams(name, Name, 'HwMoveIPR', CodeBlock(code))
header_output = BasicDeclare.subst(iop)
decoder_output = BasicConstructor.subst(iop)
decode_block = BasicDecode.subst(iop)
exec_output = BasicExecute.subst(iop)
}};
////////////////////////////////////////////////////////////////////
//
// Unimplemented instructions
//
output header {{
/**
* Static instruction class for unimplemented instructions that
* cause simulator termination. Note that these are recognized
* (legal) instructions that the simulator does not support; the
* 'Unknown' class is used for unrecognized/illegal instructions.
* This is a leaf class.
*/
class FailUnimplemented : public AlphaStaticInst
{
public:
/// Constructor
FailUnimplemented(const char *_mnemonic, MachInst _machInst)
: AlphaStaticInst(_mnemonic, _machInst, No_OpClass)
{
// don't call execute() (which panics) if we're on a
// speculative path
flags[IsNonSpeculative] = true;
}
%(BasicExecDeclare)s
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
/**
* Base class for unimplemented instructions that cause a warning
* to be printed (but do not terminate simulation). This
* implementation is a little screwy in that it will print a
* warning for each instance of a particular unimplemented machine
* instruction, not just for each unimplemented opcode. Should
* probably make the 'warned' flag a static member of the derived
* class.
*/
class WarnUnimplemented : public AlphaStaticInst
{
private:
/// Have we warned on this instruction yet?
bool warned;
public:
/// Constructor
WarnUnimplemented(const char *_mnemonic, MachInst _machInst)
: AlphaStaticInst(_mnemonic, _machInst, No_OpClass), warned(false)
{
// don't call execute() (which panics) if we're on a
// speculative path
flags[IsNonSpeculative] = true;
}
%(BasicExecDeclare)s
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
output decoder {{
std::string
FailUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s (unimplemented)", mnemonic);
}
std::string
WarnUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
#ifdef SS_COMPATIBLE_DISASSEMBLY
return csprintf("%-10s", mnemonic);
#else
return csprintf("%-10s (unimplemented)", mnemonic);
#endif
}
}};
output exec {{
Fault
FailUnimplemented::execute(%(CPU_exec_context)s *xc,
Trace::InstRecord *traceData)
{
panic("attempt to execute unimplemented instruction '%s' "
"(inst 0x%08x, opcode 0x%x)", mnemonic, machInst, OPCODE);
return Unimplemented_Opcode_Fault;
}
Fault
WarnUnimplemented::execute(%(CPU_exec_context)s *xc,
Trace::InstRecord *traceData)
{
if (!warned) {
warn("instruction '%s' unimplemented\n", mnemonic);
warned = true;
}
return No_Fault;
}
}};
def format FailUnimpl() {{
iop = InstObjParams(name, 'FailUnimplemented')
decode_block = BasicDecodeWithMnemonic.subst(iop)
}};
def format WarnUnimpl() {{
iop = InstObjParams(name, 'WarnUnimplemented')
decode_block = BasicDecodeWithMnemonic.subst(iop)
}};
output header {{
/**
* Static instruction class for unknown (illegal) instructions.
* These cause simulator termination if they are executed in a
* non-speculative mode. This is a leaf class.
*/
class Unknown : public AlphaStaticInst
{
public:
/// Constructor
Unknown(MachInst _machInst)
: AlphaStaticInst("unknown", _machInst, No_OpClass)
{
// don't call execute() (which panics) if we're on a
// speculative path
flags[IsNonSpeculative] = true;
}
%(BasicExecDeclare)s
std::string generateDisassembly(Addr pc, const SymbolTable *symtab);
};
}};
////////////////////////////////////////////////////////////////////
//
// Unknown instructions
//
output decoder {{
std::string
Unknown::generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s (inst 0x%x, opcode 0x%x)",
"unknown", machInst, OPCODE);
}
}};
output exec {{
Fault
Unknown::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData)
{
panic("attempt to execute unknown instruction "
"(inst 0x%08x, opcode 0x%x)", machInst, OPCODE);
return Unimplemented_Opcode_Fault;
}
}};
def format Unknown() {{
decode_block = 'return new Unknown(machInst);\n'
}};
////////////////////////////////////////////////////////////////////
//
// Utility functions for execute methods
//
output exec {{
/// Return opa + opb, summing carry into third arg.
inline uint64_t
addc(uint64_t opa, uint64_t opb, int &carry)
{
uint64_t res = opa + opb;
if (res < opa || res < opb)
++carry;
return res;
}
/// Multiply two 64-bit values (opa * opb), returning the 128-bit
/// product in res_hi and res_lo.
inline void
mul128(uint64_t opa, uint64_t opb, uint64_t &res_hi, uint64_t &res_lo)
{
// do a 64x64 --> 128 multiply using four 32x32 --> 64 multiplies
uint64_t opa_hi = opa<63:32>;
uint64_t opa_lo = opa<31:0>;
uint64_t opb_hi = opb<63:32>;
uint64_t opb_lo = opb<31:0>;
res_lo = opa_lo * opb_lo;
// The middle partial products logically belong in bit
// positions 95 to 32. Thus the lower 32 bits of each product
// sum into the upper 32 bits of the low result, while the
// upper 32 sum into the low 32 bits of the upper result.
uint64_t partial1 = opa_hi * opb_lo;
uint64_t partial2 = opa_lo * opb_hi;
uint64_t partial1_lo = partial1<31:0> << 32;
uint64_t partial1_hi = partial1<63:32>;
uint64_t partial2_lo = partial2<31:0> << 32;
uint64_t partial2_hi = partial2<63:32>;
// Add partial1_lo and partial2_lo to res_lo, keeping track
// of any carries out
int carry_out = 0;
res_lo = addc(partial1_lo, res_lo, carry_out);
res_lo = addc(partial2_lo, res_lo, carry_out);
// Now calculate the high 64 bits...
res_hi = (opa_hi * opb_hi) + partial1_hi + partial2_hi + carry_out;
}
/// Map 8-bit S-floating exponent to 11-bit T-floating exponent.
/// See Table 2-2 of Alpha AHB.
inline int
map_s(int old_exp)
{
int hibit = old_exp<7:>;
int lobits = old_exp<6:0>;
if (hibit == 1) {
return (lobits == 0x7f) ? 0x7ff : (0x400 | lobits);
}
else {
return (lobits == 0) ? 0 : (0x380 | lobits);
}
}
/// Convert a 32-bit S-floating value to the equivalent 64-bit
/// representation to be stored in an FP reg.
inline uint64_t
s_to_t(uint32_t s_val)
{
uint64_t tmp = s_val;
return (tmp<31:> << 63 // sign bit
| (uint64_t)map_s(tmp<30:23>) << 52 // exponent
| tmp<22:0> << 29); // fraction
}
/// Convert a 64-bit T-floating value to the equivalent 32-bit
/// S-floating representation to be stored in memory.
inline int32_t
t_to_s(uint64_t t_val)
{
return (t_val<63:62> << 30 // sign bit & hi exp bit
| t_val<58:29>); // rest of exp & fraction
}
}};
////////////////////////////////////////////////////////////////////
//
// The actual decoder specification
//
decode OPCODE default Unknown::unknown() {
format LoadAddress {
0x08: lda({{ Ra = Rb + disp; }});
0x09: ldah({{ Ra = Rb + (disp << 16); }});
}
format LoadOrNop {
0x0a: ldbu({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.ub; }});
0x0c: ldwu({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uw; }});
0x0b: ldq_u({{ EA = (Rb + disp) & ~7; }}, {{ Ra = Mem.uq; }});
0x23: ldt({{ EA = Rb + disp; }}, {{ Fa = Mem.df; }});
0x2a: ldl_l({{ EA = Rb + disp; }}, {{ Ra.sl = Mem.sl; }}, LOCKED);
0x2b: ldq_l({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uq; }}, LOCKED);
0x20: copy_load({{EA = Ra;}},
{{fault = xc->copySrcTranslate(EA);}},
IsMemRef, IsLoad, IsCopy);
}
format LoadOrPrefetch {
0x28: ldl({{ EA = Rb + disp; }}, {{ Ra.sl = Mem.sl; }});
0x29: ldq({{ EA = Rb + disp; }}, {{ Ra.uq = Mem.uq; }}, EVICT_NEXT);
// IsFloating flag on lds gets the prefetch to disassemble
// using f31 instead of r31... funcitonally it's unnecessary
0x22: lds({{ EA = Rb + disp; }}, {{ Fa.uq = s_to_t(Mem.ul); }},
PF_EXCLUSIVE, IsFloating);
}
format Store {
0x0e: stb({{ EA = Rb + disp; }}, {{ Mem.ub = Ra<7:0>; }});
0x0d: stw({{ EA = Rb + disp; }}, {{ Mem.uw = Ra<15:0>; }});
0x2c: stl({{ EA = Rb + disp; }}, {{ Mem.ul = Ra<31:0>; }});
0x2d: stq({{ EA = Rb + disp; }}, {{ Mem.uq = Ra.uq; }});
0x0f: stq_u({{ EA = (Rb + disp) & ~7; }}, {{ Mem.uq = Ra.uq; }});
0x26: sts({{ EA = Rb + disp; }}, {{ Mem.ul = t_to_s(Fa.uq); }});
0x27: stt({{ EA = Rb + disp; }}, {{ Mem.df = Fa; }});
0x24: copy_store({{EA = Rb;}},
{{fault = xc->copy(EA);}},
IsMemRef, IsStore, IsCopy);
}
format StoreCond {
0x2e: stl_c({{ EA = Rb + disp; }}, {{ Mem.ul = Ra<31:0>; }},
{{
uint64_t tmp = Mem_write_result;
// see stq_c
Ra = (tmp == 0 || tmp == 1) ? tmp : Ra;
}}, LOCKED);
0x2f: stq_c({{ EA = Rb + disp; }}, {{ Mem.uq = Ra; }},
{{
uint64_t tmp = Mem_write_result;
// If the write operation returns 0 or 1, then
// this was a conventional store conditional,
// and the value indicates the success/failure
// of the operation. If another value is
// returned, then this was a Turbolaser
// mailbox access, and we don't update the
// result register at all.
Ra = (tmp == 0 || tmp == 1) ? tmp : Ra;
}}, LOCKED);
}
format IntegerOperate {
0x10: decode INTFUNC { // integer arithmetic operations
0x00: addl({{ Rc.sl = Ra.sl + Rb_or_imm.sl; }});
0x40: addlv({{
uint32_t tmp = Ra.sl + Rb_or_imm.sl;
// signed overflow occurs when operands have same sign
// and sign of result does not match.
if (Ra.sl<31:> == Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>)
fault = Integer_Overflow_Fault;
Rc.sl = tmp;
}});
0x02: s4addl({{ Rc.sl = (Ra.sl << 2) + Rb_or_imm.sl; }});
0x12: s8addl({{ Rc.sl = (Ra.sl << 3) + Rb_or_imm.sl; }});
0x20: addq({{ Rc = Ra + Rb_or_imm; }});
0x60: addqv({{
uint64_t tmp = Ra + Rb_or_imm;
// signed overflow occurs when operands have same sign
// and sign of result does not match.
if (Ra<63:> == Rb_or_imm<63:> && tmp<63:> != Ra<63:>)
fault = Integer_Overflow_Fault;
Rc = tmp;
}});
0x22: s4addq({{ Rc = (Ra << 2) + Rb_or_imm; }});
0x32: s8addq({{ Rc = (Ra << 3) + Rb_or_imm; }});
0x09: subl({{ Rc.sl = Ra.sl - Rb_or_imm.sl; }});
0x49: sublv({{
uint32_t tmp = Ra.sl - Rb_or_imm.sl;
// signed overflow detection is same as for add,
// except we need to look at the *complemented*
// sign bit of the subtrahend (Rb), i.e., if the initial
// signs are the *same* then no overflow can occur
if (Ra.sl<31:> != Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>)
fault = Integer_Overflow_Fault;
Rc.sl = tmp;
}});
0x0b: s4subl({{ Rc.sl = (Ra.sl << 2) - Rb_or_imm.sl; }});
0x1b: s8subl({{ Rc.sl = (Ra.sl << 3) - Rb_or_imm.sl; }});
0x29: subq({{ Rc = Ra - Rb_or_imm; }});
0x69: subqv({{
uint64_t tmp = Ra - Rb_or_imm;
// signed overflow detection is same as for add,
// except we need to look at the *complemented*
// sign bit of the subtrahend (Rb), i.e., if the initial
// signs are the *same* then no overflow can occur
if (Ra<63:> != Rb_or_imm<63:> && tmp<63:> != Ra<63:>)
fault = Integer_Overflow_Fault;
Rc = tmp;
}});
0x2b: s4subq({{ Rc = (Ra << 2) - Rb_or_imm; }});
0x3b: s8subq({{ Rc = (Ra << 3) - Rb_or_imm; }});
0x2d: cmpeq({{ Rc = (Ra == Rb_or_imm); }});
0x6d: cmple({{ Rc = (Ra.sq <= Rb_or_imm.sq); }});
0x4d: cmplt({{ Rc = (Ra.sq < Rb_or_imm.sq); }});
0x3d: cmpule({{ Rc = (Ra.uq <= Rb_or_imm.uq); }});
0x1d: cmpult({{ Rc = (Ra.uq < Rb_or_imm.uq); }});
0x0f: cmpbge({{
int hi = 7;
int lo = 0;
uint64_t tmp = 0;
for (int i = 0; i < 8; ++i) {
tmp |= (Ra.uq<hi:lo> >= Rb_or_imm.uq<hi:lo>) << i;
hi += 8;
lo += 8;
}
Rc = tmp;
}});
}
0x11: decode INTFUNC { // integer logical operations
0x00: and({{ Rc = Ra & Rb_or_imm; }});
0x08: bic({{ Rc = Ra & ~Rb_or_imm; }});
0x20: bis({{ Rc = Ra | Rb_or_imm; }});
0x28: ornot({{ Rc = Ra | ~Rb_or_imm; }});
0x40: xor({{ Rc = Ra ^ Rb_or_imm; }});
0x48: eqv({{ Rc = Ra ^ ~Rb_or_imm; }});
// conditional moves
0x14: cmovlbs({{ Rc = ((Ra & 1) == 1) ? Rb_or_imm : Rc; }});
0x16: cmovlbc({{ Rc = ((Ra & 1) == 0) ? Rb_or_imm : Rc; }});
0x24: cmoveq({{ Rc = (Ra == 0) ? Rb_or_imm : Rc; }});
0x26: cmovne({{ Rc = (Ra != 0) ? Rb_or_imm : Rc; }});
0x44: cmovlt({{ Rc = (Ra.sq < 0) ? Rb_or_imm : Rc; }});
0x46: cmovge({{ Rc = (Ra.sq >= 0) ? Rb_or_imm : Rc; }});
0x64: cmovle({{ Rc = (Ra.sq <= 0) ? Rb_or_imm : Rc; }});
0x66: cmovgt({{ Rc = (Ra.sq > 0) ? Rb_or_imm : Rc; }});
// For AMASK, RA must be R31.
0x61: decode RA {
31: amask({{ Rc = Rb_or_imm & ~ULL(0x17); }});
}
// For IMPLVER, RA must be R31 and the B operand
// must be the immediate value 1.
0x6c: decode RA {
31: decode IMM {
1: decode INTIMM {
// return EV5 for FULL_SYSTEM and EV6 otherwise
1: implver({{
#ifdef FULL_SYSTEM
Rc = 1;
#else
Rc = 2;
#endif
}});
}
}
}
#ifdef FULL_SYSTEM
// The mysterious 11.25...
0x25: WarnUnimpl::eleven25();
#endif
}
0x12: decode INTFUNC {
0x39: sll({{ Rc = Ra << Rb_or_imm<5:0>; }});
0x34: srl({{ Rc = Ra.uq >> Rb_or_imm<5:0>; }});
0x3c: sra({{ Rc = Ra.sq >> Rb_or_imm<5:0>; }});
0x02: mskbl({{ Rc = Ra & ~(mask( 8) << (Rb_or_imm<2:0> * 8)); }});
0x12: mskwl({{ Rc = Ra & ~(mask(16) << (Rb_or_imm<2:0> * 8)); }});
0x22: mskll({{ Rc = Ra & ~(mask(32) << (Rb_or_imm<2:0> * 8)); }});
0x32: mskql({{ Rc = Ra & ~(mask(64) << (Rb_or_imm<2:0> * 8)); }});
0x52: mskwh({{
int bv = Rb_or_imm<2:0>;
Rc = bv ? (Ra & ~(mask(16) >> (64 - 8 * bv))) : Ra;
}});
0x62: msklh({{
int bv = Rb_or_imm<2:0>;
Rc = bv ? (Ra & ~(mask(32) >> (64 - 8 * bv))) : Ra;
}});
0x72: mskqh({{
int bv = Rb_or_imm<2:0>;
Rc = bv ? (Ra & ~(mask(64) >> (64 - 8 * bv))) : Ra;
}});
0x06: extbl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))< 7:0>; }});
0x16: extwl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<15:0>; }});
0x26: extll({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<31:0>; }});
0x36: extql({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8)); }});
0x5a: extwh({{
Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<15:0>; }});
0x6a: extlh({{
Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<31:0>; }});
0x7a: extqh({{
Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>); }});
0x0b: insbl({{ Rc = Ra< 7:0> << (Rb_or_imm<2:0> * 8); }});
0x1b: inswl({{ Rc = Ra<15:0> << (Rb_or_imm<2:0> * 8); }});
0x2b: insll({{ Rc = Ra<31:0> << (Rb_or_imm<2:0> * 8); }});
0x3b: insql({{ Rc = Ra << (Rb_or_imm<2:0> * 8); }});
0x57: inswh({{
int bv = Rb_or_imm<2:0>;
Rc = bv ? (Ra.uq<15:0> >> (64 - 8 * bv)) : 0;
}});
0x67: inslh({{
int bv = Rb_or_imm<2:0>;
Rc = bv ? (Ra.uq<31:0> >> (64 - 8 * bv)) : 0;
}});
0x77: insqh({{
int bv = Rb_or_imm<2:0>;
Rc = bv ? (Ra.uq >> (64 - 8 * bv)) : 0;
}});
0x30: zap({{
uint64_t zapmask = 0;
for (int i = 0; i < 8; ++i) {
if (Rb_or_imm<i:>)
zapmask |= (mask(8) << (i * 8));
}
Rc = Ra & ~zapmask;
}});
0x31: zapnot({{
uint64_t zapmask = 0;
for (int i = 0; i < 8; ++i) {
if (!Rb_or_imm<i:>)
zapmask |= (mask(8) << (i * 8));
}
Rc = Ra & ~zapmask;
}});
}
0x13: decode INTFUNC { // integer multiplies
0x00: mull({{ Rc.sl = Ra.sl * Rb_or_imm.sl; }}, IntMultOp);
0x20: mulq({{ Rc = Ra * Rb_or_imm; }}, IntMultOp);
0x30: umulh({{
uint64_t hi, lo;
mul128(Ra, Rb_or_imm, hi, lo);
Rc = hi;
}}, IntMultOp);
0x40: mullv({{
// 32-bit multiply with trap on overflow
int64_t Rax = Ra.sl; // sign extended version of Ra.sl
int64_t Rbx = Rb_or_imm.sl;
int64_t tmp = Rax * Rbx;
// To avoid overflow, all the upper 32 bits must match
// the sign bit of the lower 32. We code this as
// checking the upper 33 bits for all 0s or all 1s.
uint64_t sign_bits = tmp<63:31>;
if (sign_bits != 0 && sign_bits != mask(33))
fault = Integer_Overflow_Fault;
Rc.sl = tmp<31:0>;
}}, IntMultOp);
0x60: mulqv({{
// 64-bit multiply with trap on overflow
uint64_t hi, lo;
mul128(Ra, Rb_or_imm, hi, lo);
// all the upper 64 bits must match the sign bit of
// the lower 64
if (!((hi == 0 && lo<63:> == 0) ||
(hi == mask(64) && lo<63:> == 1)))
fault = Integer_Overflow_Fault;
Rc = lo;
}}, IntMultOp);
}
0x1c: decode INTFUNC {
0x00: decode RA { 31: sextb({{ Rc.sb = Rb_or_imm< 7:0>; }}); }
0x01: decode RA { 31: sextw({{ Rc.sw = Rb_or_imm<15:0>; }}); }
0x32: ctlz({{
uint64_t count = 0;
uint64_t temp = Rb;
if (temp<63:32>) temp >>= 32; else count += 32;
if (temp<31:16>) temp >>= 16; else count += 16;
if (temp<15:8>) temp >>= 8; else count += 8;
if (temp<7:4>) temp >>= 4; else count += 4;
if (temp<3:2>) temp >>= 2; else count += 2;
if (temp<1:1>) temp >>= 1; else count += 1;
if ((temp<0:0>) != 0x1) count += 1;
Rc = count;
}}, IntAluOp);
0x33: cttz({{
uint64_t count = 0;
uint64_t temp = Rb;
if (!(temp<31:0>)) { temp >>= 32; count += 32; }
if (!(temp<15:0>)) { temp >>= 16; count += 16; }
if (!(temp<7:0>)) { temp >>= 8; count += 8; }
if (!(temp<3:0>)) { temp >>= 4; count += 4; }
if (!(temp<1:0>)) { temp >>= 2; count += 2; }
if (!(temp<0:0> & ULL(0x1))) count += 1;
Rc = count;
}}, IntAluOp);
format FailUnimpl {
0x30: ctpop();
0x31: perr();
0x34: unpkbw();
0x35: unpkbl();
0x36: pkwb();
0x37: pklb();
0x38: minsb8();
0x39: minsw4();
0x3a: minub8();
0x3b: minuw4();
0x3c: maxub8();
0x3d: maxuw4();
0x3e: maxsb8();
0x3f: maxsw4();
}
format BasicOperateWithNopCheck {
0x70: decode RB {
31: ftoit({{ Rc = Fa.uq; }}, FloatCvtOp);
}
0x78: decode RB {
31: ftois({{ Rc.sl = t_to_s(Fa.uq); }},
FloatCvtOp);
}
}
}
}
// Conditional branches.
format CondBranch {
0x39: beq({{ cond = (Ra == 0); }});
0x3d: bne({{ cond = (Ra != 0); }});
0x3e: bge({{ cond = (Ra.sq >= 0); }});
0x3f: bgt({{ cond = (Ra.sq > 0); }});
0x3b: ble({{ cond = (Ra.sq <= 0); }});
0x3a: blt({{ cond = (Ra.sq < 0); }});
0x38: blbc({{ cond = ((Ra & 1) == 0); }});
0x3c: blbs({{ cond = ((Ra & 1) == 1); }});
0x31: fbeq({{ cond = (Fa == 0); }});
0x35: fbne({{ cond = (Fa != 0); }});
0x36: fbge({{ cond = (Fa >= 0); }});
0x37: fbgt({{ cond = (Fa > 0); }});
0x33: fble({{ cond = (Fa <= 0); }});
0x32: fblt({{ cond = (Fa < 0); }});
}
// unconditional branches
format UncondBranch {
0x30: br();
0x34: bsr(IsCall);
}
// indirect branches
0x1a: decode JMPFUNC {
format Jump {
0: jmp();
1: jsr(IsCall);
2: ret(IsReturn);
3: jsr_coroutine(IsCall, IsReturn);
}
}
// IEEE floating point
0x14: decode FP_SHORTFUNC {
// Integer to FP register moves must have RB == 31
0x4: decode RB {
31: decode FP_FULLFUNC {
format BasicOperateWithNopCheck {
0x004: itofs({{ Fc.uq = s_to_t(Ra.ul); }}, FloatCvtOp);
0x024: itoft({{ Fc.uq = Ra.uq; }}, FloatCvtOp);
0x014: FailUnimpl::itoff(); // VAX-format conversion
}
}
}
// Square root instructions must have FA == 31
0xb: decode FA {
31: decode FP_TYPEFUNC {
format FloatingPointOperate {
#ifdef SS_COMPATIBLE_FP
0x0b: sqrts({{
if (Fb < 0.0)
fault = Arithmetic_Fault;
Fc = sqrt(Fb);
}}, FloatSqrtOp);
#else
0x0b: sqrts({{
if (Fb.sf < 0.0)
fault = Arithmetic_Fault;
Fc.sf = sqrt(Fb.sf);
}}, FloatSqrtOp);
#endif
0x2b: sqrtt({{
if (Fb < 0.0)
fault = Arithmetic_Fault;
Fc = sqrt(Fb);
}}, FloatSqrtOp);
}
}
}
// VAX-format sqrtf and sqrtg are not implemented
0xa: FailUnimpl::sqrtfg();
}
// IEEE floating point
0x16: decode FP_SHORTFUNC_TOP2 {
// The top two bits of the short function code break this space
// into four groups: binary ops, compares, reserved, and conversions.
// See Table 4-12 of AHB.
// Most of these instructions may have various trapping and
// rounding mode flags set; these are decoded in the
// FloatingPointDecode template used by the
// FloatingPointOperate format.
// add/sub/mul/div: just decode on the short function code
// and source type.
0: decode FP_TYPEFUNC {
format FloatingPointOperate {
#ifdef SS_COMPATIBLE_FP
0x00: adds({{ Fc = Fa + Fb; }});
0x01: subs({{ Fc = Fa - Fb; }});
0x02: muls({{ Fc = Fa * Fb; }}, FloatMultOp);
0x03: divs({{ Fc = Fa / Fb; }}, FloatDivOp);
#else
0x00: adds({{ Fc.sf = Fa.sf + Fb.sf; }});
0x01: subs({{ Fc.sf = Fa.sf - Fb.sf; }});
0x02: muls({{ Fc.sf = Fa.sf * Fb.sf; }}, FloatMultOp);
0x03: divs({{ Fc.sf = Fa.sf / Fb.sf; }}, FloatDivOp);
#endif
0x20: addt({{ Fc = Fa + Fb; }});
0x21: subt({{ Fc = Fa - Fb; }});
0x22: mult({{ Fc = Fa * Fb; }}, FloatMultOp);
0x23: divt({{ Fc = Fa / Fb; }}, FloatDivOp);
}
}
// Floating-point compare instructions must have the default
// rounding mode, and may use the default trapping mode or
// /SU. Both trapping modes are treated the same by M5; the
// only difference on the real hardware (as far a I can tell)
// is that without /SU you'd get an imprecise trap if you
// tried to compare a NaN with something else (instead of an
// "unordered" result).
1: decode FP_FULLFUNC {
format BasicOperateWithNopCheck {
0x0a5, 0x5a5: cmpteq({{ Fc = (Fa == Fb) ? 2.0 : 0.0; }},
FloatCmpOp);
0x0a7, 0x5a7: cmptle({{ Fc = (Fa <= Fb) ? 2.0 : 0.0; }},
FloatCmpOp);
0x0a6, 0x5a6: cmptlt({{ Fc = (Fa < Fb) ? 2.0 : 0.0; }},
FloatCmpOp);
0x0a4, 0x5a4: cmptun({{ // unordered
Fc = (!(Fa < Fb) && !(Fa == Fb) && !(Fa > Fb)) ? 2.0 : 0.0;
}}, FloatCmpOp);
}
}
// The FP-to-integer and integer-to-FP conversion insts
// require that FA be 31.
3: decode FA {
31: decode FP_TYPEFUNC {
format FloatingPointOperate {
0x2f: cvttq({{ Fc.sq = (int64_t)rint(Fb); }});
// The cvtts opcode is overloaded to be cvtst if the trap
// mode is 2 or 6 (which are not valid otherwise)
0x2c: decode FP_FULLFUNC {
format BasicOperateWithNopCheck {
// trap on denorm version "cvtst/s" is
// simulated same as cvtst
0x2ac, 0x6ac: cvtst({{ Fc = Fb.sf; }});
}
default: cvtts({{ Fc.sf = Fb; }});
}
// The trapping mode for integer-to-FP conversions
// must be /SUI or nothing; /U and /SU are not
// allowed. The full set of rounding modes are
// supported though.
0x3c: decode FP_TRAPMODE {
0,7: cvtqs({{ Fc.sf = Fb.sq; }});
}
0x3e: decode FP_TRAPMODE {
0,7: cvtqt({{ Fc = Fb.sq; }});
}
}
}
}
}
// misc FP operate
0x17: decode FP_FULLFUNC {
format BasicOperateWithNopCheck {
0x010: cvtlq({{
Fc.sl = (Fb.uq<63:62> << 30) | Fb.uq<58:29>;
}});
0x030: cvtql({{
Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29);
}});
// We treat the precise & imprecise trapping versions of
// cvtql identically.
0x130, 0x530: cvtqlv({{
// To avoid overflow, all the upper 32 bits must match
// the sign bit of the lower 32. We code this as
// checking the upper 33 bits for all 0s or all 1s.
uint64_t sign_bits = Fb.uq<63:31>;
if (sign_bits != 0 && sign_bits != mask(33))
fault = Integer_Overflow_Fault;
Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29);
}});
0x020: cpys({{ // copy sign
Fc.uq = (Fa.uq<63:> << 63) | Fb.uq<62:0>;
}});
0x021: cpysn({{ // copy sign negated
Fc.uq = (~Fa.uq<63:> << 63) | Fb.uq<62:0>;
}});
0x022: cpyse({{ // copy sign and exponent
Fc.uq = (Fa.uq<63:52> << 52) | Fb.uq<51:0>;
}});
0x02a: fcmoveq({{ Fc = (Fa == 0) ? Fb : Fc; }});
0x02b: fcmovne({{ Fc = (Fa != 0) ? Fb : Fc; }});
0x02c: fcmovlt({{ Fc = (Fa < 0) ? Fb : Fc; }});
0x02d: fcmovge({{ Fc = (Fa >= 0) ? Fb : Fc; }});
0x02e: fcmovle({{ Fc = (Fa <= 0) ? Fb : Fc; }});
0x02f: fcmovgt({{ Fc = (Fa > 0) ? Fb : Fc; }});
0x024: mt_fpcr({{ FPCR = Fa.uq; }});
0x025: mf_fpcr({{ Fa.uq = FPCR; }});
}
}
// miscellaneous mem-format ops
0x18: decode MEMFUNC {
format WarnUnimpl {
0x8000: fetch();
0xa000: fetch_m();
0xe800: ecb();
}
format MiscPrefetch {
0xf800: wh64({{ EA = Rb & ~ULL(63); }},
{{ xc->writeHint(EA, 64, memAccessFlags); }},
IsMemRef, IsDataPrefetch, IsStore, MemWriteOp,
NO_FAULT);
}
format BasicOperate {
0xc000: rpcc({{
#ifdef FULL_SYSTEM
/* Rb is a fake dependency so here is a fun way to get
* the parser to understand that.
*/
Ra = xc->readIpr(AlphaISA::IPR_CC, fault) + (Rb & 0);
#else
Ra = curTick;
#endif
}});
// All of the barrier instructions below do nothing in
// their execute() methods (hence the empty code blocks).
// All of their functionality is hard-coded in the
// pipeline based on the flags IsSerializing,
// IsMemBarrier, and IsWriteBarrier. In the current
// detailed CPU model, the execute() function only gets
// called at fetch, so there's no way to generate pipeline
// behavior at any other stage. Once we go to an
// exec-in-exec CPU model we should be able to get rid of
// these flags and implement this behavior via the
// execute() methods.
// trapb is just a barrier on integer traps, where excb is
// a barrier on integer and FP traps. "EXCB is thus a
// superset of TRAPB." (Alpha ARM, Sec 4.11.4) We treat
// them the same though.
0x0000: trapb({{ }}, IsSerializing, No_OpClass);
0x0400: excb({{ }}, IsSerializing, No_OpClass);
0x4000: mb({{ }}, IsMemBarrier, MemReadOp);
0x4400: wmb({{ }}, IsWriteBarrier, MemWriteOp);
}
#ifdef FULL_SYSTEM
format BasicOperate {
0xe000: rc({{
Ra = xc->readIntrFlag();
xc->setIntrFlag(0);
}}, IsNonSpeculative);
0xf000: rs({{
Ra = xc->readIntrFlag();
xc->setIntrFlag(1);
}}, IsNonSpeculative);
}
#else
format FailUnimpl {
0xe000: rc();
0xf000: rs();
}
#endif
}
#ifdef FULL_SYSTEM
0x00: CallPal::call_pal({{
if (!palValid ||
(palPriv
&& xc->readIpr(AlphaISA::IPR_ICM, fault) != AlphaISA::mode_kernel)) {
// invalid pal function code, or attempt to do privileged
// PAL call in non-kernel mode
fault = Unimplemented_Opcode_Fault;
}
else {
// check to see if simulator wants to do something special
// on this PAL call (including maybe suppress it)
bool dopal = xc->simPalCheck(palFunc);
if (dopal) {
AlphaISA::swap_palshadow(&xc->xcBase()->regs, true);
xc->setIpr(AlphaISA::IPR_EXC_ADDR, NPC);
NPC = xc->readIpr(AlphaISA::IPR_PAL_BASE, fault) + palOffset;
}
}
}}, IsNonSpeculative);
#else
0x00: decode PALFUNC {
format EmulatedCallPal {
0x00: halt ({{
SimExit(curTick, "halt instruction encountered");
}}, IsNonSpeculative);
0x83: callsys({{
xc->syscall();
}}, IsNonSpeculative);
// Read uniq reg into ABI return value register (r0)
0x9e: rduniq({{ R0 = Runiq; }});
// Write uniq reg with value from ABI arg register (r16)
0x9f: wruniq({{ Runiq = R16; }});
}
}
#endif
#ifdef FULL_SYSTEM
format HwLoadStore {
0x1b: decode HW_LDST_QUAD {
0: hw_ld({{ EA = (Rb + disp) & ~3; }}, {{ Ra = Mem.ul; }}, L);
1: hw_ld({{ EA = (Rb + disp) & ~7; }}, {{ Ra = Mem.uq; }}, Q);
}
0x1f: decode HW_LDST_COND {
0: decode HW_LDST_QUAD {
0: hw_st({{ EA = (Rb + disp) & ~3; }},
{{ Mem.ul = Ra<31:0>; }}, L);
1: hw_st({{ EA = (Rb + disp) & ~7; }},
{{ Mem.uq = Ra.uq; }}, Q);
}
1: FailUnimpl::hw_st_cond();
}
}
format BasicOperate {
0x1e: hw_rei({{ xc->hwrei(); }});
// M5 special opcodes use the reserved 0x01 opcode space
0x01: decode M5FUNC {
0x00: arm({{
AlphaPseudo::arm(xc->xcBase());
}}, IsNonSpeculative);
0x01: quiesce({{
AlphaPseudo::quiesce(xc->xcBase());
}}, IsNonSpeculative);
0x10: ivlb({{
AlphaPseudo::ivlb(xc->xcBase());
}}, No_OpClass, IsNonSpeculative);
0x11: ivle({{
AlphaPseudo::ivle(xc->xcBase());
}}, No_OpClass, IsNonSpeculative);
0x20: m5exit_old({{
AlphaPseudo::m5exit_old(xc->xcBase());
}}, No_OpClass, IsNonSpeculative);
0x21: m5exit({{
AlphaPseudo::m5exit(xc->xcBase());
}}, No_OpClass, IsNonSpeculative);
0x30: initparam({{ Ra = xc->xcBase()->cpu->system->init_param; }});
0x40: resetstats({{
AlphaPseudo::resetstats(xc->xcBase());
}}, IsNonSpeculative);
0x41: dumpstats({{
AlphaPseudo::dumpstats(xc->xcBase());
}}, IsNonSpeculative);
0x42: dumpresetstats({{
AlphaPseudo::dumpresetstats(xc->xcBase());
}}, IsNonSpeculative);
0x43: m5checkpoint({{
AlphaPseudo::m5checkpoint(xc->xcBase());
}}, IsNonSpeculative);
0x50: m5readfile({{
AlphaPseudo::readfile(xc->xcBase());
}}, IsNonSpeculative);
0x51: m5break({{
AlphaPseudo::debugbreak(xc->xcBase());
}}, IsNonSpeculative);
0x52: m5switchcpu({{
AlphaPseudo::switchcpu(xc->xcBase());
}}, IsNonSpeculative);
}
}
format HwMoveIPR {
0x19: hw_mfpr({{
// this instruction is only valid in PAL mode
if (!xc->inPalMode()) {
fault = Unimplemented_Opcode_Fault;
}
else {
Ra = xc->readIpr(ipr_index, fault);
}
}});
0x1d: hw_mtpr({{
// this instruction is only valid in PAL mode
if (!xc->inPalMode()) {
fault = Unimplemented_Opcode_Fault;
}
else {
xc->setIpr(ipr_index, Ra);
if (traceData) { traceData->setData(Ra); }
}
}});
}
#endif
}