gem5/arch/alpha/isa_desc
Steve Reinhardt b6ff600bca Add support for "serializing" instructions that flush
execution pipeline (Alpha trapb & excb).

Add support for write memory barriers (mostly impacts
store buffer).

Add StaticInst flag to indicate memory barriers, though
this is not modeled in the pipeline yet.

arch/alpha/isa_desc:
    Implement trapb, excb, mb, and wmb as insts with
    no execution effect (empty execute() function) but
    with flags that indicate their side effects.

    Also make sure every instruction that needs to go to
    the execute stage has a real opClass value, since we
    are now using No_OpClass to signal insts that can get
    dropped at dispatch.

    StaticInst::branchTarget() is now a const method.
cpu/static_inst.hh:
    Add flags to indicate serializing insts (trapb, excb) and
    memory and write barriers.

    Also declare some StaticInst methods as const methods.
dev/etherlink.hh:
sim/eventq.hh:
sim/serialize.cc:
sim/serialize.hh:
sim/sim_object.hh:
    Make name() return value const.

--HG--
extra : convert_revision : 39520e71469fa20e0a7446b2e06b494eec17a02c
2004-02-04 21:42:00 -08:00

2539 lines
67 KiB
C++

// -*- mode:c++ -*-
//
// Alpha ISA description file.
//
let {{
global rcs_id
rcs_id = "$Id$"
}};
#include <sstream>
#include <iostream>
#include <iomanip>
#include <math.h>
#if defined(linux)
#include <fenv.h>
#endif
#include "base/cprintf.hh"
#include "base/misc.hh"
#include "cpu/exec_context.hh"
#include "cpu/exetrace.hh"
#include "cpu/full_cpu/full_cpu.hh"
#include "cpu/full_cpu/op_class.hh"
#include "cpu/full_cpu/spec_state.hh"
#include "cpu/simple_cpu/simple_cpu.hh"
#include "cpu/static_inst.hh"
#include "sim/annotation.hh"
#include "sim/sim_exit.hh"
#ifdef FULL_SYSTEM
#include "arch/alpha/ev5.hh"
#include "arch/alpha/pseudo_inst.hh"
#endif
namespace AlphaISA;
// 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>;
let {{
global operandTypeMap
operandTypeMap = {
'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)
}
global operandTraitsMap
operandTraitsMap = {
# 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),
}
defineDerivedOperandVars()
}};
declare {{
// just temporary, while comparing with old code for debugging
// #define SS_COMPATIBLE_DISASSEMBLY
/// 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(ExecContext *xc)
{
Fault fault = No_Fault; // dummy... this ipr access should not fault
if (!ICSR_FPE(xc->readIpr(AlphaISA::IPR_ICSR, fault))) {
fault = Fen_Fault;
}
return fault;
}
#else
inline Fault checkFpEnableFault(ExecContext *xc)
{
return No_Fault;
}
#endif
/**
* 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)
{
if (reg < FP_Base_DepTag) {
ccprintf(os, "r%d", reg);
}
else {
ccprintf(os, "f%d", reg - FP_Base_DepTag);
}
}
std::string 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();
}
};
}};
def template BasicDeclare {{
/**
* Static instruction class for "%(mnemonic)s".
*/
class %(class_name)s : public %(base_class)s
{
public:
/// Constructor.
%(class_name)s(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
SimpleCPU *memAccessObj __attribute__((unused)) = cpu;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(simple_rd)s;
%(code)s;
if (fault == No_Fault) {
%(simple_wb)s;
}
return fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
DynInst *memAccessObj __attribute__((unused)) = dynInst;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(dtld_rd)s;
%(code)s;
if (fault == No_Fault) {
%(dtld_wb)s;
}
return fault;
}
};
}};
def template BasicDecode {{
return new %(class_name)s(machInst);
}};
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)
return iop.subst('BasicDeclare', 'BasicDecode')
}};
////////////////////////////////////////////////////////////////////
declare {{
/**
* 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() { }
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
return No_Fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
return No_Fault;
}
std::string 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;
}
}};
def format Nop() {{
return ('', 'return new Nop("%s", machInst);\n' % name)
}};
// 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)
return iop.subst('BasicDeclare', 'OperateNopCheckDecode')
}};
////////////////////////////////////////////////////////////////////
//
// Integer operate instructions
//
declare {{
/**
* 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)
{
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)
decls = iop.subst('BasicDeclare')
if uses_imm:
# append declaration for imm version
imm_cblk = CodeBlock(imm_code)
imm_iop = InstObjParams(name, Name + 'Imm', 'IntegerImm', imm_cblk,
opt_flags)
decls += imm_iop.subst('BasicDeclare')
# decode checks IMM bit to pick correct version
decode = iop.subst('RegOrImmDecode')
else:
# no imm version: just check for nop
decode = iop.subst('OperateNopCheckDecode')
return (decls, decode)
}};
////////////////////////////////////////////////////////////////////
//
// Floating-point instructions
//
// Note that many FP-type instructions which do not support all the
// various rounding & trapping modes use the simpler format
// BasicOperateWithNopCheck.
//
declare {{
/**
* 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(ExecContext *xc)
{
if (roundingMode == Dynamic) {
return alphaToC99RoundingMode[bits(xc->readFpcr(), 59, 58)];
}
else {
return alphaToC99RoundingMode[roundingMode];
}
}
#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)
{
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" };
}};
def template FloatingPointDeclare {{
/**
* "Fast" static instruction class for "%(mnemonic)s" (imprecise
* trapping mode, normal rounding mode).
*/
class %(class_name)sFast : public %(base_class)s
{
public:
/// Constructor.
%(class_name)sFast(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(simple_rd)s;
%(code)s;
if (fault == No_Fault) {
%(simple_wb)s;
}
return fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(dtld_rd)s;
%(code)s;
if (fault == No_Fault) {
%(dtld_wb)s;
}
return fault;
}
};
/**
* General static instruction class for "%(mnemonic)s". Supports
* all the various rounding and trapping modes.
*/
class %(class_name)sGeneral : public %(base_class)s
{
public:
/// Constructor.
%(class_name)sGeneral(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(simple_rd)s;
#if defined(linux)
fesetround(getC99RoundingMode(xc));
#endif
%(code)s;
#if defined(linux)
fesetround(FE_TONEAREST);
#endif
if (fault == No_Fault) {
%(simple_wb)s;
}
return fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(dtld_rd)s;
#if defined(linux)
fesetround(getC99RoundingMode(xc));
#endif
%(code)s;
#if defined(linux)
fesetround(FE_TONEAREST);
#endif
if (fault == No_Fault) {
%(dtld_wb)s;
}
return fault;
}
};
}};
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;
}
}};
// 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)
return iop.subst('FloatingPointDeclare', 'FloatingPointDecode')
}};
////////////////////////////////////////////////////////////////////
//
// Memory-format instructions: LoadAddress, Load, Store
//
declare {{
/**
* Base class for general Alpha memory-format instructions.
*/
class Memory : public AlphaStaticInst
{
protected:
/// Displacement for EA calculation (signed).
int32_t disp;
/// Memory request flags. See mem_req_base.hh.
unsigned memAccessFlags;
/// Constructor
Memory(const char *mnem, MachInst _machInst, OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass),
disp(MEMDISP), memAccessFlags(0)
{
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s %c%d,%d(r%d)", mnemonic,
flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB);
}
};
/**
* 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 AlphaStaticInst
{
protected:
/// Memory request flags. See mem_req_base.hh.
unsigned memAccessFlags;
/// Constructor
MemoryNoDisp(const char *mnem, MachInst _machInst, OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass),
memAccessFlags(0)
{
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s (r%d)", mnemonic, RB);
}
};
/**
* Base class for "fake" effective-address computation
* instructions returnded by eaCompInst().
*/
class EACompBase : public AlphaStaticInst
{
public:
/// Constructor
EACompBase(MachInst machInst)
: AlphaStaticInst("(eacomp)", machInst, IntALU)
{
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{ panic("attempt to execute eacomp"); }
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{ panic("attempt to execute eacomp"); }
};
/**
* Base class for "fake" memory-access instructions returnded by
* memAccInst().
*/
class MemAccBase : public AlphaStaticInst
{
public:
/// Constructor
MemAccBase(MachInst machInst, OpClass __opClass)
: AlphaStaticInst("(memacc)", machInst, __opClass)
{
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{ panic("attempt to execute memacc"); }
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{ panic("attempt to execute memacc"); }
};
}};
def format LoadAddress(code) {{
iop = InstObjParams(name, Name, 'Memory', CodeBlock(code))
return iop.subst('BasicDeclare', 'BasicDecode')
}};
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 EACompBase
{
public:
/// Constructor
EAComp(MachInst machInst)
: EACompBase(machInst)
{
%(ea_constructor)s;
}
};
/**
* "Fake" memory access instruction class for "%(mnemonic)s".
*/
class MemAcc : public MemAccBase
{
public:
/// Constructor
MemAcc(MachInst machInst)
: MemAccBase(machInst, %(op_class)s)
{
%(memacc_constructor)s;
}
};
/// Pointer to EAComp object.
StaticInstPtr<AlphaISA> eaCompPtr;
/// Pointer to MemAcc object.
StaticInstPtr<AlphaISA> memAccPtr;
public:
StaticInstPtr<AlphaISA> eaCompInst() { return eaCompPtr; }
StaticInstPtr<AlphaISA> memAccInst() { return memAccPtr; }
/// Constructor.
%(class_name)s(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s),
eaCompPtr(new EAComp(machInst)), memAccPtr(new MemAcc(machInst))
{
%(constructor)s;
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
SimpleCPU *memAccessObj = cpu;
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(simple_nonmem_rd)s;
%(ea_code)s;
if (fault == No_Fault) {
%(simple_mem_rd)s;
%(memacc_code)s;
}
if (fault == No_Fault) {
%(simple_mem_wb)s;
}
if (fault == No_Fault) {
%(postacc_code)s;
}
if (fault == No_Fault) {
%(simple_nonmem_wb)s;
}
return fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
DynInst *memAccessObj = dynInst;
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(dtld_nonmem_rd)s;
%(ea_code)s;
if (fault == No_Fault) {
%(dtld_mem_rd)s;
%(memacc_code)s;
}
if (fault == No_Fault) {
%(dtld_mem_wb)s;
}
if (fault == No_Fault) {
%(postacc_code)s;
}
if (fault == No_Fault) {
%(dtld_nonmem_wb)s;
}
return fault;
}
};
}};
def template PrefetchDeclare {{
/**
* Static instruction class for "%(mnemonic)s".
*/
class %(class_name)s : public %(base_class)s
{
public:
/// Constructor
%(class_name)s(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst, %(op_class)s)
{
%(constructor)s;
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(simple_nonmem_rd)s;
%(ea_code)s;
if (fault == No_Fault) {
cpu->prefetch(EA, memAccessFlags);
}
return No_Fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
Addr EA;
Fault fault = No_Fault;
%(fp_enable_check)s;
%(exec_decl)s;
%(dtld_nonmem_rd)s;
%(ea_code)s;
if (fault == No_Fault) {
dynInst->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 {{
global LoadStoreBase
def LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code = '',
base_class = 'Memory', flags = [],
declare_template = 'LoadStoreDeclare',
decode_template = 'BasicDecode'):
# 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', 'EVICT_NEXT', 'PF_EXCLUSIVE']
inst_flags = []
mem_flags = []
for f in flags:
if f in valid_mem_flags:
mem_flags.append(f)
else:
inst_flags.append(f)
ea_cblk = CodeBlock(ea_code)
memacc_cblk = CodeBlock(memacc_code)
postacc_cblk = CodeBlock(postacc_code)
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
mem_flags = string.join(mem_flags, '|')
if mem_flags != '':
iop.constructor += '\n\tmemAccessFlags = ' + mem_flags + ';'
return iop.subst(declare_template, decode_template)
}};
def format LoadOrNop(ea_code, memacc_code, *flags) {{
return 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
(decls, decode) = \
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', 'RdPort']
(pfdecls, pfdecode) = \
LoadStoreBase(name, Name + 'Prefetch', ea_code, '',
flags = pf_flags,
declare_template = 'PrefetchDeclare')
return (decls + pfdecls, decode)
}};
def format Store(ea_code, memacc_code, *flags) {{
return LoadStoreBase(name, Name, ea_code, memacc_code,
flags = flags)
}};
def format StoreCond(ea_code, memacc_code, postacc_code, *flags) {{
return 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) {{
return LoadStoreBase(name, Name, ea_code, memacc_code,
flags = flags, base_class = 'MemoryNoDisp')
}};
////////////////////////////////////////////////////////////////////
declare {{
/**
* 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)
{
if (!cachedDisassembly ||
pc != cachedPC || symtab != cachedSymtab)
{
if (cachedDisassembly)
delete cachedDisassembly;
cachedDisassembly =
new std::string(generateDisassembly(pc, symtab));
cachedPC = pc;
cachedSymtab = symtab;
}
return *cachedDisassembly;
}
};
/**
* 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
{
return branchPC + 4 + disp;
}
std::string 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();
}
};
/**
* 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
{
Addr NPC = xc->readPC() + 4;
uint64_t Rb = xc->readIntReg(_srcRegIdx[0]);
return (Rb & ~3) | (NPC & 1);
}
std::string 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'))
return iop.subst('BasicDeclare', 'BasicDecode')
}};
let {{
global UncondCtrlBase
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)
decls = nolink_iop.subst('BasicDeclare')
# 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)
decls += link_iop.subst('BasicDeclare')
# need to use link_iop for the decode template since it is expecting
# the shorter version of class_name (w/o "AndLink")
return (decls, nolink_iop.subst('JumpOrBranchDecode'))
}};
def format UncondBranch(*flags) {{
flags += ('IsUncondControl', 'IsDirectControl')
return UncondCtrlBase(name, Name, 'Branch', 'NPC + disp', flags)
}};
def format Jump(*flags) {{
flags += ('IsUncondControl', 'IsIndirectControl')
return UncondCtrlBase(name, Name, 'Jump', '(Rb & ~3) | (NPC & 1)', flags)
}};
declare {{
/**
* 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)
{
#ifdef SS_COMPATIBLE_DISASSEMBLY
return csprintf("%s %s", "call_pal", mnemonic);
#else
return csprintf("%-10s %s", "call_pal", mnemonic);
#endif
}
};
}};
def format EmulatedCallPal(code) {{
iop = InstObjParams(name, Name, 'EmulatedCallPal', CodeBlock(code))
return iop.subst('BasicDeclare', 'BasicDecode')
}};
declare {{
/**
* 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)
: 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 generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s %#x", "call_pal", palFunc);
}
};
}};
def format CallPal(code) {{
iop = InstObjParams(name, Name, 'CallPalBase', CodeBlock(code))
return iop.subst('BasicDeclare', 'BasicDecode')
}};
//
// hw_ld, hw_st
//
declare {{
/**
* Base class for hw_ld and hw_st.
*/
class HwLoadStore : public AlphaStaticInst
{
protected:
/// Displacement for EA calculation (signed).
int16_t disp;
/// Memory request flags. See mem_req_base.hh.
unsigned memAccessFlags;
/// Constructor
HwLoadStore(const char *mnem, MachInst _machInst, OpClass __opClass)
: AlphaStaticInst(mnem, _machInst, __opClass), 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 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) {{
return 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) {{
return LoadStoreBase(name, Name + class_ext,
ea_code, memacc_code, postacc_code,
flags = flags,
base_class = 'HwLoadStore')
}};
declare {{
/**
* 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)
{
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))
return iop.subst('BasicDeclare', 'BasicDecode')
}};
declare {{
/**
* 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)
{
}
Fault execute(SimpleCPU *cpu, ExecContext *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 execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
// don't panic if this is a misspeculated instruction
if (!xc->spec_mode)
panic("attempt to execute unimplemented instruction '%s' "
"(inst 0x%08x, opcode 0x%x)",
mnemonic, machInst, OPCODE);
return Unimplemented_Opcode_Fault;
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s (unimplemented)", mnemonic);
}
};
/**
* 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)
{
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
if (!warned) {
warn("instruction '%s' unimplemented\n", mnemonic);
warned = true;
}
return No_Fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
if (!xc->spec_mode && !warned) {
warn("instruction '%s' unimplemented\n", mnemonic);
warned = true;
}
return No_Fault;
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab)
{
#ifdef SS_COMPATIBLE_DISASSEMBLY
return csprintf("%-10s", mnemonic);
#else
return csprintf("%-10s (unimplemented)", mnemonic);
#endif
}
};
}};
def template WarnUnimplDeclare {{
/**
* Static instruction class for "%(mnemonic)s".
*/
class %(class_name)s : public %(base_class)s
{
public:
/// Constructor
%(class_name)s(MachInst machInst)
: %(base_class)s("%(mnemonic)s", machInst)
{
}
};
}};
def format FailUnimpl() {{
iop = InstObjParams(name, 'FailUnimplemented')
return ('', iop.subst('BasicDecodeWithMnemonic'))
}};
def format WarnUnimpl() {{
iop = InstObjParams(name, Name, 'WarnUnimplemented')
return iop.subst('WarnUnimplDeclare', 'BasicDecode')
}};
declare {{
/**
* 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)
{
}
Fault execute(SimpleCPU *cpu, ExecContext *xc,
Trace::InstRecord *traceData)
{
panic("attempt to execute unknown instruction "
"(inst 0x%08x, opcode 0x%x)", machInst, OPCODE);
return Unimplemented_Opcode_Fault;
}
Fault execute(FullCPU *cpu, SpecExecContext *xc, DynInst *dynInst,
Trace::InstRecord *traceData)
{
// don't panic if this is a misspeculated instruction
if (!xc->spec_mode)
panic("attempt to execute unknown instruction "
"(inst 0x%08x, opcode 0x%x)", machInst, OPCODE);
return Unimplemented_Opcode_Fault;
}
std::string generateDisassembly(Addr pc, const SymbolTable *symtab)
{
return csprintf("%-10s (inst 0x%x, opcode 0x%x)",
"unknown", machInst, OPCODE);
}
};
}};
def format Unknown() {{
return ('', 'return new Unknown(machInst);\n')
}};
declare {{
/// 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.
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
}
}};
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);
}
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; }});
}
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; }}, IntMULT);
0x20: mulq({{ Rc = Ra * Rb_or_imm; }}, IntMULT);
0x30: umulh({{
uint64_t hi, lo;
mul128(Ra, Rb_or_imm, hi, lo);
Rc = hi;
}}, IntMULT);
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>;
}}, IntMULT);
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;
}}, IntMULT);
}
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>; }}); }
format FailUnimpl {
0x30: ctpop();
0x31: perr();
0x32: ctlz();
0x33: cttz();
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; }}, FloatCVT);
}
0x78: decode RB {
31: ftois({{ Rc.sl = t_to_s(Fa.uq); }},
FloatCVT);
}
}
}
}
// 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); }}, FloatCVT);
0x024: itoft({{ Fc.uq = Ra.uq; }}, FloatCVT);
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);
}}, FloatSQRT);
#else
0x0b: sqrts({{
if (Fb.sf < 0.0)
fault = Arithmetic_Fault;
Fc.sf = sqrt(Fb.sf);
}}, FloatSQRT);
#endif
0x2b: sqrtt({{
if (Fb < 0.0)
fault = Arithmetic_Fault;
Fc = sqrt(Fb);
}}, FloatSQRT);
}
}
}
// 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; }}, FloatMULT);
0x03: divs({{ Fc = Fa / Fb; }}, FloatDIV);
#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; }}, FloatMULT);
0x03: divs({{ Fc.sf = Fa.sf / Fb.sf; }}, FloatDIV);
#endif
0x20: addt({{ Fc = Fa + Fb; }});
0x21: subt({{ Fc = Fa - Fb; }});
0x22: mult({{ Fc = Fa * Fb; }}, FloatMULT);
0x23: divt({{ Fc = Fa / Fb; }}, FloatDIV);
}
}
// 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; }},
FloatCMP);
0x0a7, 0x5a7: cmptle({{ Fc = (Fa <= Fb) ? 2.0 : 0.0; }},
FloatCMP);
0x0a6, 0x5a6: cmptlt({{ Fc = (Fa < Fb) ? 2.0 : 0.0; }},
FloatCMP);
0x0a4, 0x5a4: cmptun({{ // unordered
Fc = (!(Fa < Fb) && !(Fa == Fb) && !(Fa > Fb)) ? 2.0 : 0.0;
}}, FloatCMP);
}
}
// 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; }},
{{ memAccessObj->writeHint(EA, 64); }},
IsMemRef, IsStore, WrPort);
}
format BasicOperate {
0xc000: rpcc({{ Ra = curTick; }});
// 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);
0x4400: wmb({{ }}, IsWriteBarrier);
}
#ifdef FULL_SYSTEM
format BasicOperate {
0xe000: rc({{
Ra = xc->regs.intrflag;
if (!xc->misspeculating()) {
xc->regs.intrflag = 0;
}
}});
0xf000: rs({{
Ra = xc->regs.intrflag;
if (!xc->misspeculating()) {
xc->regs.intrflag = 1;
}
}});
}
#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 {
bool dopal = true;
if (!xc->misspeculating()) {
// check to see if simulator wants to do something special
// on this PAL call (including maybe suppress it)
dopal = xc->simPalCheck(palFunc);
Annotate::Callpal(xc, palFunc);
if (dopal) {
AlphaISA::swap_palshadow(&xc->regs, true);
xc->setIpr(AlphaISA::IPR_EXC_ADDR, NPC);
}
}
// if we're misspeculating, it's still safe (if
// unrealistic) to set NPC, as the control-flow change
// won't get committed.
if (dopal) {
NPC = xc->readIpr(AlphaISA::IPR_PAL_BASE, fault) + palOffset;
}
}
}});
#else
0x00: decode PALFUNC {
format EmulatedCallPal {
0x00: halt ({{
if (!xc->misspeculating())
SimExit(curTick, "halt instruction encountered");
}});
0x83: callsys({{
if (!xc->misspeculating())
xc->syscall();
}});
// 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({{
if (!xc->misspeculating()) {
Annotate::ARM(xc);
xc->kernelStats.arm();
}
}});
0x01: quiesce({{
if (!xc->misspeculating())
AlphaPseudo::quiesce(xc);
}});
0x10: ivlb({{
if (!xc->misspeculating()) {
Annotate::BeginInterval(xc);
xc->kernelStats.ivlb();
}
}}, No_OpClass);
0x11: ivle({{
if (!xc->misspeculating())
Annotate::EndInterval(xc);
}}, No_OpClass);
0x20: m5exit_old({{
if (!xc->misspeculating())
AlphaPseudo::m5exit_old(xc);
}}, No_OpClass);
0x21: m5exit({{
if (!xc->misspeculating())
AlphaPseudo::m5exit(xc);
}}, No_OpClass);
0x30: initparam({{ Ra = cpu->system->init_param; }});
0x40: resetstats({{
if (!xc->misspeculating())
AlphaPseudo::resetstats(xc);
}});
0x41: dumpstats({{
if (!xc->misspeculating())
AlphaPseudo::dumpstats(xc);
}});
0x42: dumpresetstats({{
if (!xc->misspeculating())
AlphaPseudo::dumpresetstats(xc);
}});
0x43: m5checkpoint({{
if (!xc->misspeculating())
AlphaPseudo::m5checkpoint(xc);
}});
}
}
format HwMoveIPR {
0x19: hw_mfpr({{
// this instruction is only valid in PAL mode
if (!PC_PAL(xc->regs.pc)) {
fault = Unimplemented_Opcode_Fault;
}
else {
Ra = xc->readIpr(ipr_index, fault);
}
}});
0x1d: hw_mtpr({{
// this instruction is only valid in PAL mode
if (!PC_PAL(xc->regs.pc)) {
fault = Unimplemented_Opcode_Fault;
}
else {
xc->setIpr(ipr_index, Ra);
if (traceData) { traceData->setData(Ra); }
}
}});
}
#endif
}