diff options
author | Gabe Black <gblack@eecs.umich.edu> | 2012-05-26 13:44:46 -0700 |
---|---|---|
committer | Gabe Black <gblack@eecs.umich.edu> | 2012-05-26 13:44:46 -0700 |
commit | 0cba96ba6a5d7a4dab2a63b14149c49dfbfbb3bc (patch) | |
tree | 1e4e1372b76ed021060d560c2ee1a474f4b22ef0 /src/arch/x86 | |
parent | eae1e97fb002b44a9d8c46df2da1ddc1d0156ce4 (diff) | |
download | gem5-0cba96ba6a5d7a4dab2a63b14149c49dfbfbb3bc.tar.xz |
CPU: Merge the predecoder and decoder.
These classes are always used together, and merging them will give the ISAs
more flexibility in how they cache things and manage the process.
--HG--
rename : src/arch/x86/predecoder_tables.cc => src/arch/x86/decoder_tables.cc
Diffstat (limited to 'src/arch/x86')
-rw-r--r-- | src/arch/x86/SConscript | 5 | ||||
-rw-r--r-- | src/arch/x86/decoder.cc | 375 | ||||
-rw-r--r-- | src/arch/x86/decoder.hh | 189 | ||||
-rw-r--r-- | src/arch/x86/decoder_tables.cc (renamed from src/arch/x86/predecoder_tables.cc) | 10 | ||||
-rw-r--r-- | src/arch/x86/emulenv.cc | 2 | ||||
-rw-r--r-- | src/arch/x86/isa/decoder/one_byte_opcodes.isa | 2 | ||||
-rw-r--r-- | src/arch/x86/predecoder.cc | 419 | ||||
-rw-r--r-- | src/arch/x86/predecoder.hh | 239 | ||||
-rw-r--r-- | src/arch/x86/types.hh | 4 |
9 files changed, 574 insertions, 671 deletions
diff --git a/src/arch/x86/SConscript b/src/arch/x86/SConscript index 27b12fe20..92b30ced1 100644 --- a/src/arch/x86/SConscript +++ b/src/arch/x86/SConscript @@ -45,6 +45,7 @@ Import('*') if env['TARGET_ISA'] == 'x86': Source('cpuid.cc') Source('decoder.cc') + Source('decoder_tables.cc') Source('emulenv.cc') Source('faults.cc') Source('insts/badmicroop.cc') @@ -63,8 +64,6 @@ if env['TARGET_ISA'] == 'x86': Source('nativetrace.cc') Source('pagetable.cc') Source('pagetable_walker.cc') - Source('predecoder.cc') - Source('predecoder_tables.cc') Source('process.cc') Source('remote_gdb.cc') Source('stacktrace.cc') @@ -83,7 +82,7 @@ if env['TARGET_ISA'] == 'x86': DebugFlag('LocalApic', "Local APIC debugging") DebugFlag('PageTableWalker', \ "Page table walker state machine debugging") - DebugFlag('Predecoder', "Predecoder debug output") + DebugFlag('Decoder', "Decoder debug output") DebugFlag('X86', "Generic X86 ISA debugging") python_files = ( diff --git a/src/arch/x86/decoder.cc b/src/arch/x86/decoder.cc index 469858301..d7199fa82 100644 --- a/src/arch/x86/decoder.cc +++ b/src/arch/x86/decoder.cc @@ -29,9 +29,384 @@ */ #include "arch/x86/decoder.hh" +#include "arch/x86/regs/misc.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "base/types.hh" +#include "cpu/thread_context.hh" +#include "debug/Decoder.hh" namespace X86ISA { +void Decoder::doReset() +{ + origPC = basePC + offset; + DPRINTF(Decoder, "Setting origPC to %#x\n", origPC); + emi.rex = 0; + emi.legacy = 0; + emi.opcode.num = 0; + emi.opcode.op = 0; + emi.opcode.prefixA = emi.opcode.prefixB = 0; + + immediateCollected = 0; + emi.immediate = 0; + emi.displacement = 0; + emi.dispSize = 0; + + emi.modRM = 0; + emi.sib = 0; + m5Reg = tc->readMiscRegNoEffect(MISCREG_M5_REG); + emi.mode.mode = m5Reg.mode; + emi.mode.submode = m5Reg.submode; +} + +void Decoder::process() +{ + //This function drives the decoder state machine. + + //Some sanity checks. You shouldn't try to process more bytes if + //there aren't any, and you shouldn't overwrite an already + //decoder ExtMachInst. + assert(!outOfBytes); + assert(!instDone); + + //While there's still something to do... + while(!instDone && !outOfBytes) + { + uint8_t nextByte = getNextByte(); + switch(state) + { + case ResetState: + doReset(); + state = PrefixState; + case PrefixState: + state = doPrefixState(nextByte); + break; + case OpcodeState: + state = doOpcodeState(nextByte); + break; + case ModRMState: + state = doModRMState(nextByte); + break; + case SIBState: + state = doSIBState(nextByte); + break; + case DisplacementState: + state = doDisplacementState(); + break; + case ImmediateState: + state = doImmediateState(); + break; + case ErrorState: + panic("Went to the error state in the decoder.\n"); + default: + panic("Unrecognized state! %d\n", state); + } + } +} + +//Either get a prefix and record it in the ExtMachInst, or send the +//state machine on to get the opcode(s). +Decoder::State Decoder::doPrefixState(uint8_t nextByte) +{ + uint8_t prefix = Prefixes[nextByte]; + State nextState = PrefixState; + // REX prefixes are only recognized in 64 bit mode. + if (prefix == RexPrefix && emi.mode.submode != SixtyFourBitMode) + prefix = 0; + if (prefix) + consumeByte(); + switch(prefix) + { + //Operand size override prefixes + case OperandSizeOverride: + DPRINTF(Decoder, "Found operand size override prefix.\n"); + emi.legacy.op = true; + break; + case AddressSizeOverride: + DPRINTF(Decoder, "Found address size override prefix.\n"); + emi.legacy.addr = true; + break; + //Segment override prefixes + case CSOverride: + case DSOverride: + case ESOverride: + case FSOverride: + case GSOverride: + case SSOverride: + DPRINTF(Decoder, "Found segment override.\n"); + emi.legacy.seg = prefix; + break; + case Lock: + DPRINTF(Decoder, "Found lock prefix.\n"); + emi.legacy.lock = true; + break; + case Rep: + DPRINTF(Decoder, "Found rep prefix.\n"); + emi.legacy.rep = true; + break; + case Repne: + DPRINTF(Decoder, "Found repne prefix.\n"); + emi.legacy.repne = true; + break; + case RexPrefix: + DPRINTF(Decoder, "Found Rex prefix %#x.\n", nextByte); + emi.rex = nextByte; + break; + case 0: + nextState = OpcodeState; + break; + default: + panic("Unrecognized prefix %#x\n", nextByte); + } + return nextState; +} + +//Load all the opcodes (currently up to 2) and then figure out +//what immediate and/or ModRM is needed. +Decoder::State Decoder::doOpcodeState(uint8_t nextByte) +{ + State nextState = ErrorState; + emi.opcode.num++; + //We can't handle 3+ byte opcodes right now + assert(emi.opcode.num < 4); + consumeByte(); + if(emi.opcode.num == 1 && nextByte == 0x0f) + { + nextState = OpcodeState; + DPRINTF(Decoder, "Found two byte opcode.\n"); + emi.opcode.prefixA = nextByte; + } + else if(emi.opcode.num == 2 && (nextByte == 0x38 || nextByte == 0x3A)) + { + nextState = OpcodeState; + DPRINTF(Decoder, "Found three byte opcode.\n"); + emi.opcode.prefixB = nextByte; + } + else + { + DPRINTF(Decoder, "Found opcode %#x.\n", nextByte); + emi.opcode.op = nextByte; + + //Figure out the effective operand size. This can be overriden to + //a fixed value at the decoder level. + int logOpSize; + if (emi.rex.w) + logOpSize = 3; // 64 bit operand size + else if (emi.legacy.op) + logOpSize = m5Reg.altOp; + else + logOpSize = m5Reg.defOp; + + //Set the actual op size + emi.opSize = 1 << logOpSize; + + //Figure out the effective address size. This can be overriden to + //a fixed value at the decoder level. + int logAddrSize; + if(emi.legacy.addr) + logAddrSize = m5Reg.altAddr; + else + logAddrSize = m5Reg.defAddr; + + //Set the actual address size + emi.addrSize = 1 << logAddrSize; + + //Figure out the effective stack width. This can be overriden to + //a fixed value at the decoder level. + emi.stackSize = 1 << m5Reg.stack; + + //Figure out how big of an immediate we'll retreive based + //on the opcode. + int immType = ImmediateType[emi.opcode.num - 1][nextByte]; + if (emi.opcode.num == 1 && nextByte >= 0xA0 && nextByte <= 0xA3) + immediateSize = SizeTypeToSize[logAddrSize - 1][immType]; + else + immediateSize = SizeTypeToSize[logOpSize - 1][immType]; + + //Determine what to expect next + if (UsesModRM[emi.opcode.num - 1][nextByte]) { + nextState = ModRMState; + } else { + if(immediateSize) { + nextState = ImmediateState; + } else { + instDone = true; + nextState = ResetState; + } + } + } + return nextState; +} + +//Get the ModRM byte and determine what displacement, if any, there is. +//Also determine whether or not to get the SIB byte, displacement, or +//immediate next. +Decoder::State Decoder::doModRMState(uint8_t nextByte) +{ + State nextState = ErrorState; + ModRM modRM; + modRM = nextByte; + DPRINTF(Decoder, "Found modrm byte %#x.\n", nextByte); + if (m5Reg.defOp == 1) { + //figure out 16 bit displacement size + if ((modRM.mod == 0 && modRM.rm == 6) || modRM.mod == 2) + displacementSize = 2; + else if (modRM.mod == 1) + displacementSize = 1; + else + displacementSize = 0; + } else { + //figure out 32/64 bit displacement size + if ((modRM.mod == 0 && modRM.rm == 5) || modRM.mod == 2) + displacementSize = 4; + else if (modRM.mod == 1) + displacementSize = 1; + else + displacementSize = 0; + } + + // The "test" instruction in group 3 needs an immediate, even though + // the other instructions with the same actual opcode don't. + if (emi.opcode.num == 1 && (modRM.reg & 0x6) == 0) { + if (emi.opcode.op == 0xF6) + immediateSize = 1; + else if (emi.opcode.op == 0xF7) + immediateSize = (emi.opSize == 8) ? 4 : emi.opSize; + } + + //If there's an SIB, get that next. + //There is no SIB in 16 bit mode. + if (modRM.rm == 4 && modRM.mod != 3) { + // && in 32/64 bit mode) + nextState = SIBState; + } else if(displacementSize) { + nextState = DisplacementState; + } else if(immediateSize) { + nextState = ImmediateState; + } else { + instDone = true; + nextState = ResetState; + } + //The ModRM byte is consumed no matter what + consumeByte(); + emi.modRM = modRM; + return nextState; +} + +//Get the SIB byte. We don't do anything with it at this point, other +//than storing it in the ExtMachInst. Determine if we need to get a +//displacement or immediate next. +Decoder::State Decoder::doSIBState(uint8_t nextByte) +{ + State nextState = ErrorState; + emi.sib = nextByte; + DPRINTF(Decoder, "Found SIB byte %#x.\n", nextByte); + consumeByte(); + if (emi.modRM.mod == 0 && emi.sib.base == 5) + displacementSize = 4; + if (displacementSize) { + nextState = DisplacementState; + } else if(immediateSize) { + nextState = ImmediateState; + } else { + instDone = true; + nextState = ResetState; + } + return nextState; +} + +//Gather up the displacement, or at least as much of it +//as we can get. +Decoder::State Decoder::doDisplacementState() +{ + State nextState = ErrorState; + + getImmediate(immediateCollected, + emi.displacement, + displacementSize); + + DPRINTF(Decoder, "Collecting %d byte displacement, got %d bytes.\n", + displacementSize, immediateCollected); + + if(displacementSize == immediateCollected) { + //Reset this for other immediates. + immediateCollected = 0; + //Sign extend the displacement + switch(displacementSize) + { + case 1: + emi.displacement = sext<8>(emi.displacement); + break; + case 2: + emi.displacement = sext<16>(emi.displacement); + break; + case 4: + emi.displacement = sext<32>(emi.displacement); + break; + default: + panic("Undefined displacement size!\n"); + } + DPRINTF(Decoder, "Collected displacement %#x.\n", + emi.displacement); + if(immediateSize) { + nextState = ImmediateState; + } else { + instDone = true; + nextState = ResetState; + } + + emi.dispSize = displacementSize; + } + else + nextState = DisplacementState; + return nextState; +} + +//Gather up the immediate, or at least as much of it +//as we can get +Decoder::State Decoder::doImmediateState() +{ + State nextState = ErrorState; + + getImmediate(immediateCollected, + emi.immediate, + immediateSize); + + DPRINTF(Decoder, "Collecting %d byte immediate, got %d bytes.\n", + immediateSize, immediateCollected); + + if(immediateSize == immediateCollected) + { + //Reset this for other immediates. + immediateCollected = 0; + + //XXX Warning! The following is an observed pattern and might + //not always be true! + + //Instructions which use 64 bit operands but 32 bit immediates + //need to have the immediate sign extended to 64 bits. + //Instructions which use true 64 bit immediates won't be + //affected, and instructions that use true 32 bit immediates + //won't notice. + switch(immediateSize) + { + case 4: + emi.immediate = sext<32>(emi.immediate); + break; + case 1: + emi.immediate = sext<8>(emi.immediate); + } + + DPRINTF(Decoder, "Collected immediate %#x.\n", + emi.immediate); + instDone = true; + nextState = ResetState; + } + else + nextState = ImmediateState; + return nextState; +} DecodeCache Decoder::defaultCache; diff --git a/src/arch/x86/decoder.hh b/src/arch/x86/decoder.hh index 769284adb..300e2238c 100644 --- a/src/arch/x86/decoder.hh +++ b/src/arch/x86/decoder.hh @@ -31,15 +31,192 @@ #ifndef __ARCH_X86_DECODER_HH__ #define __ARCH_X86_DECODER_HH__ -#include "arch/types.hh" +#include <cassert> + +#include "arch/x86/regs/misc.hh" +#include "arch/x86/types.hh" +#include "base/bitfield.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "base/types.hh" #include "cpu/decode_cache.hh" #include "cpu/static_inst_fwd.hh" +#include "debug/Decoder.hh" + +class ThreadContext; namespace X86ISA { class Decoder { + private: + //These are defined and documented in decoder_tables.cc + static const uint8_t Prefixes[256]; + static const uint8_t UsesModRM[2][256]; + static const uint8_t ImmediateType[2][256]; + static const uint8_t SizeTypeToSize[3][10]; + + protected: + ThreadContext * tc; + //The bytes to be predecoded + MachInst fetchChunk; + //The pc of the start of fetchChunk + Addr basePC; + //The pc the current instruction started at + Addr origPC; + //The offset into fetchChunk of current processing + int offset; + //The extended machine instruction being generated + ExtMachInst emi; + HandyM5Reg m5Reg; + + inline uint8_t getNextByte() + { + return ((uint8_t *)&fetchChunk)[offset]; + } + + void getImmediate(int &collected, uint64_t ¤t, int size) + { + //Figure out how many bytes we still need to get for the + //immediate. + int toGet = size - collected; + //Figure out how many bytes are left in our "buffer" + int remaining = sizeof(MachInst) - offset; + //Get as much as we need, up to the amount available. + toGet = toGet > remaining ? remaining : toGet; + + //Shift the bytes we want to be all the way to the right + uint64_t partialImm = fetchChunk >> (offset * 8); + //Mask off what we don't want + partialImm &= mask(toGet * 8); + //Shift it over to overlay with our displacement. + partialImm <<= (immediateCollected * 8); + //Put it into our displacement + current |= partialImm; + //Update how many bytes we've collected. + collected += toGet; + consumeBytes(toGet); + } + + inline void consumeByte() + { + offset++; + assert(offset <= sizeof(MachInst)); + if(offset == sizeof(MachInst)) + outOfBytes = true; + } + + inline void consumeBytes(int numBytes) + { + offset += numBytes; + assert(offset <= sizeof(MachInst)); + if(offset == sizeof(MachInst)) + outOfBytes = true; + } + + void doReset(); + + //State machine state + protected: + //Whether or not we're out of bytes + bool outOfBytes; + //Whether we've completed generating an ExtMachInst + bool instDone; + //The size of the displacement value + int displacementSize; + //The size of the immediate value + int immediateSize; + //This is how much of any immediate value we've gotten. This is used + //for both the actual immediate and the displacement. + int immediateCollected; + + enum State { + ResetState, + PrefixState, + OpcodeState, + ModRMState, + SIBState, + DisplacementState, + ImmediateState, + //We should never get to this state. Getting here is an error. + ErrorState + }; + + State state; + + //Functions to handle each of the states + State doPrefixState(uint8_t); + State doOpcodeState(uint8_t); + State doModRMState(uint8_t); + State doSIBState(uint8_t); + State doDisplacementState(); + State doImmediateState(); + + public: + Decoder(ThreadContext * _tc) : + tc(_tc), basePC(0), origPC(0), offset(0), + outOfBytes(true), instDone(false), + state(ResetState) + { + emi.mode.mode = LongMode; + emi.mode.submode = SixtyFourBitMode; + m5Reg = 0; + } + + void reset() + { + state = ResetState; + } + + ThreadContext * getTC() + { + return tc; + } + + void setTC(ThreadContext * _tc) + { + tc = _tc; + } + + void process(); + + //Use this to give data to the decoder. This should be used + //when there is control flow. + void moreBytes(const PCState &pc, Addr fetchPC, MachInst data) + { + DPRINTF(Decoder, "Getting more bytes.\n"); + basePC = fetchPC; + offset = (fetchPC >= pc.instAddr()) ? 0 : pc.instAddr() - fetchPC; + fetchChunk = data; + outOfBytes = false; + process(); + } + + bool needMoreBytes() + { + return outOfBytes; + } + + bool instReady() + { + return instDone; + } + + void + updateNPC(X86ISA::PCState &nextPC) + { + if (!nextPC.size()) { + int size = basePC + offset - origPC; + DPRINTF(Decoder, + "Calculating the instruction size: " + "basePC: %#x offset: %#x origPC: %#x size: %d\n", + basePC, offset, origPC, size); + nextPC.size(size); + nextPC.npc(nextPC.pc() + size); + } + } + protected: /// A cache of decoded instruction objects. static DecodeCache defaultCache; @@ -55,6 +232,16 @@ class Decoder { return defaultCache.decode(this, mach_inst, addr); } + + StaticInstPtr + decode(X86ISA::PCState &nextPC) + { + if (!instDone) + return NULL; + instDone = false; + updateNPC(nextPC); + return decode(emi, origPC); + } }; } // namespace X86ISA diff --git a/src/arch/x86/predecoder_tables.cc b/src/arch/x86/decoder_tables.cc index 3931e4c30..a132cb864 100644 --- a/src/arch/x86/predecoder_tables.cc +++ b/src/arch/x86/decoder_tables.cc @@ -37,7 +37,7 @@ * Authors: Gabe Black */ -#include "arch/x86/predecoder.hh" +#include "arch/x86/decoder.hh" #include "arch/x86/types.hh" namespace X86ISA @@ -58,7 +58,7 @@ namespace X86ISA //This table identifies whether a byte is a prefix, and if it is, //which prefix it is. - const uint8_t Predecoder::Prefixes[256] = + const uint8_t Decoder::Prefixes[256] = { //LSB // MSB 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F /* 0*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, @@ -80,7 +80,7 @@ namespace X86ISA }; //This table identifies whether a particular opcode uses the ModRM byte - const uint8_t Predecoder::UsesModRM[2][256] = + const uint8_t Decoder::UsesModRM[2][256] = {//For one byte instructions { //LSB // MSB 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F @@ -147,7 +147,7 @@ namespace X86ISA PO = Pointer }; - const uint8_t Predecoder::SizeTypeToSize[3][10] = + const uint8_t Decoder::SizeTypeToSize[3][10] = { // noimm byte word dword qword oword vword zword enter pointer {0, 1, 2, 4, 8, 16, 2, 2, 3, 4 }, //16 bit @@ -159,7 +159,7 @@ namespace X86ISA //number of bytes in the instruction, and the second is the meaningful //byte of the opcode. I didn't use the NI constant here for the sake //of clarity. - const uint8_t Predecoder::ImmediateType[2][256] = + const uint8_t Decoder::ImmediateType[2][256] = {//For one byte instructions { //LSB // MSB 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F diff --git a/src/arch/x86/emulenv.cc b/src/arch/x86/emulenv.cc index e6c170d0f..49df32eca 100644 --- a/src/arch/x86/emulenv.cc +++ b/src/arch/x86/emulenv.cc @@ -53,7 +53,7 @@ void EmulEnv::doModRM(const ExtMachInst & machInst) index = machInst.sib.index | (machInst.rex.x << 3); base = machInst.sib.base | (machInst.rex.b << 3); //In this special case, we don't use a base. The displacement also - //changes, but that's managed by the predecoder. + //changes, but that's managed by the decoder. if (machInst.sib.base == INTREG_RBP && machInst.modRM.mod == 0) base = NUM_INTREGS; //In -this- special case, we don't use an index. diff --git a/src/arch/x86/isa/decoder/one_byte_opcodes.isa b/src/arch/x86/isa/decoder/one_byte_opcodes.isa index 66a0c8c46..040f5d04f 100644 --- a/src/arch/x86/isa/decoder/one_byte_opcodes.isa +++ b/src/arch/x86/isa/decoder/one_byte_opcodes.isa @@ -396,7 +396,7 @@ 0x4: int3(); 0x5: decode FullSystemInt default int_Ib() { 0: decode IMMEDIATE { - // Really only the LSB matters, but the predecoder + // Really only the LSB matters, but the decoder // will sign extend it, and there's no easy way to // specify only checking the first byte. -0x80: SyscallInst::int80('xc->syscall(Rax)', diff --git a/src/arch/x86/predecoder.cc b/src/arch/x86/predecoder.cc deleted file mode 100644 index a4aa93b48..000000000 --- a/src/arch/x86/predecoder.cc +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (c) 2007-2008 The Hewlett-Packard Development Company - * All rights reserved. - * - * The license below extends only to copyright in the software and shall - * not be construed as granting a license to any other intellectual - * property including but not limited to intellectual property relating - * to a hardware implementation of the functionality of the software - * licensed hereunder. You may use the software subject to the license - * terms below provided that you ensure that this notice is replicated - * unmodified and in its entirety in all distributions of the software, - * modified or unmodified, in source code or in binary form. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer; - * redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution; - * neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Authors: Gabe Black - */ - -#include "arch/x86/regs/misc.hh" -#include "arch/x86/predecoder.hh" -#include "base/misc.hh" -#include "base/trace.hh" -#include "base/types.hh" -#include "cpu/thread_context.hh" -#include "debug/Predecoder.hh" - -namespace X86ISA -{ - void Predecoder::doReset() - { - origPC = basePC + offset; - DPRINTF(Predecoder, "Setting origPC to %#x\n", origPC); - emi.rex = 0; - emi.legacy = 0; - emi.opcode.num = 0; - emi.opcode.op = 0; - emi.opcode.prefixA = emi.opcode.prefixB = 0; - - immediateCollected = 0; - emi.immediate = 0; - emi.displacement = 0; - emi.dispSize = 0; - - emi.modRM = 0; - emi.sib = 0; - m5Reg = tc->readMiscRegNoEffect(MISCREG_M5_REG); - emi.mode.mode = m5Reg.mode; - emi.mode.submode = m5Reg.submode; - } - - void Predecoder::process() - { - //This function drives the predecoder state machine. - - //Some sanity checks. You shouldn't try to process more bytes if - //there aren't any, and you shouldn't overwrite an already - //predecoder ExtMachInst. - assert(!outOfBytes); - assert(!emiIsReady); - - //While there's still something to do... - while(!emiIsReady && !outOfBytes) - { - uint8_t nextByte = getNextByte(); - switch(state) - { - case ResetState: - doReset(); - state = PrefixState; - case PrefixState: - state = doPrefixState(nextByte); - break; - case OpcodeState: - state = doOpcodeState(nextByte); - break; - case ModRMState: - state = doModRMState(nextByte); - break; - case SIBState: - state = doSIBState(nextByte); - break; - case DisplacementState: - state = doDisplacementState(); - break; - case ImmediateState: - state = doImmediateState(); - break; - case ErrorState: - panic("Went to the error state in the predecoder.\n"); - default: - panic("Unrecognized state! %d\n", state); - } - } - } - - //Either get a prefix and record it in the ExtMachInst, or send the - //state machine on to get the opcode(s). - Predecoder::State Predecoder::doPrefixState(uint8_t nextByte) - { - uint8_t prefix = Prefixes[nextByte]; - State nextState = PrefixState; - // REX prefixes are only recognized in 64 bit mode. - if (prefix == RexPrefix && emi.mode.submode != SixtyFourBitMode) - prefix = 0; - if (prefix) - consumeByte(); - switch(prefix) - { - //Operand size override prefixes - case OperandSizeOverride: - DPRINTF(Predecoder, "Found operand size override prefix.\n"); - emi.legacy.op = true; - break; - case AddressSizeOverride: - DPRINTF(Predecoder, "Found address size override prefix.\n"); - emi.legacy.addr = true; - break; - //Segment override prefixes - case CSOverride: - case DSOverride: - case ESOverride: - case FSOverride: - case GSOverride: - case SSOverride: - DPRINTF(Predecoder, "Found segment override.\n"); - emi.legacy.seg = prefix; - break; - case Lock: - DPRINTF(Predecoder, "Found lock prefix.\n"); - emi.legacy.lock = true; - break; - case Rep: - DPRINTF(Predecoder, "Found rep prefix.\n"); - emi.legacy.rep = true; - break; - case Repne: - DPRINTF(Predecoder, "Found repne prefix.\n"); - emi.legacy.repne = true; - break; - case RexPrefix: - DPRINTF(Predecoder, "Found Rex prefix %#x.\n", nextByte); - emi.rex = nextByte; - break; - case 0: - nextState = OpcodeState; - break; - default: - panic("Unrecognized prefix %#x\n", nextByte); - } - return nextState; - } - - //Load all the opcodes (currently up to 2) and then figure out - //what immediate and/or ModRM is needed. - Predecoder::State Predecoder::doOpcodeState(uint8_t nextByte) - { - State nextState = ErrorState; - emi.opcode.num++; - //We can't handle 3+ byte opcodes right now - assert(emi.opcode.num < 4); - consumeByte(); - if(emi.opcode.num == 1 && nextByte == 0x0f) - { - nextState = OpcodeState; - DPRINTF(Predecoder, "Found two byte opcode.\n"); - emi.opcode.prefixA = nextByte; - } - else if(emi.opcode.num == 2 && (nextByte == 0x38 || nextByte == 0x3A)) - { - nextState = OpcodeState; - DPRINTF(Predecoder, "Found three byte opcode.\n"); - emi.opcode.prefixB = nextByte; - } - else - { - DPRINTF(Predecoder, "Found opcode %#x.\n", nextByte); - emi.opcode.op = nextByte; - - //Figure out the effective operand size. This can be overriden to - //a fixed value at the decoder level. - int logOpSize; - if (emi.rex.w) - logOpSize = 3; // 64 bit operand size - else if (emi.legacy.op) - logOpSize = m5Reg.altOp; - else - logOpSize = m5Reg.defOp; - - //Set the actual op size - emi.opSize = 1 << logOpSize; - - //Figure out the effective address size. This can be overriden to - //a fixed value at the decoder level. - int logAddrSize; - if(emi.legacy.addr) - logAddrSize = m5Reg.altAddr; - else - logAddrSize = m5Reg.defAddr; - - //Set the actual address size - emi.addrSize = 1 << logAddrSize; - - //Figure out the effective stack width. This can be overriden to - //a fixed value at the decoder level. - emi.stackSize = 1 << m5Reg.stack; - - //Figure out how big of an immediate we'll retreive based - //on the opcode. - int immType = ImmediateType[emi.opcode.num - 1][nextByte]; - if (emi.opcode.num == 1 && nextByte >= 0xA0 && nextByte <= 0xA3) - immediateSize = SizeTypeToSize[logAddrSize - 1][immType]; - else - immediateSize = SizeTypeToSize[logOpSize - 1][immType]; - - //Determine what to expect next - if (UsesModRM[emi.opcode.num - 1][nextByte]) { - nextState = ModRMState; - } else { - if(immediateSize) { - nextState = ImmediateState; - } else { - emiIsReady = true; - nextState = ResetState; - } - } - } - return nextState; - } - - //Get the ModRM byte and determine what displacement, if any, there is. - //Also determine whether or not to get the SIB byte, displacement, or - //immediate next. - Predecoder::State Predecoder::doModRMState(uint8_t nextByte) - { - State nextState = ErrorState; - ModRM modRM; - modRM = nextByte; - DPRINTF(Predecoder, "Found modrm byte %#x.\n", nextByte); - if (m5Reg.defOp == 1) { - //figure out 16 bit displacement size - if ((modRM.mod == 0 && modRM.rm == 6) || modRM.mod == 2) - displacementSize = 2; - else if (modRM.mod == 1) - displacementSize = 1; - else - displacementSize = 0; - } else { - //figure out 32/64 bit displacement size - if ((modRM.mod == 0 && modRM.rm == 5) || modRM.mod == 2) - displacementSize = 4; - else if (modRM.mod == 1) - displacementSize = 1; - else - displacementSize = 0; - } - - // The "test" instruction in group 3 needs an immediate, even though - // the other instructions with the same actual opcode don't. - if (emi.opcode.num == 1 && (modRM.reg & 0x6) == 0) { - if (emi.opcode.op == 0xF6) - immediateSize = 1; - else if (emi.opcode.op == 0xF7) - immediateSize = (emi.opSize == 8) ? 4 : emi.opSize; - } - - //If there's an SIB, get that next. - //There is no SIB in 16 bit mode. - if (modRM.rm == 4 && modRM.mod != 3) { - // && in 32/64 bit mode) - nextState = SIBState; - } else if(displacementSize) { - nextState = DisplacementState; - } else if(immediateSize) { - nextState = ImmediateState; - } else { - emiIsReady = true; - nextState = ResetState; - } - //The ModRM byte is consumed no matter what - consumeByte(); - emi.modRM = modRM; - return nextState; - } - - //Get the SIB byte. We don't do anything with it at this point, other - //than storing it in the ExtMachInst. Determine if we need to get a - //displacement or immediate next. - Predecoder::State Predecoder::doSIBState(uint8_t nextByte) - { - State nextState = ErrorState; - emi.sib = nextByte; - DPRINTF(Predecoder, "Found SIB byte %#x.\n", nextByte); - consumeByte(); - if (emi.modRM.mod == 0 && emi.sib.base == 5) - displacementSize = 4; - if (displacementSize) { - nextState = DisplacementState; - } else if(immediateSize) { - nextState = ImmediateState; - } else { - emiIsReady = true; - nextState = ResetState; - } - return nextState; - } - - //Gather up the displacement, or at least as much of it - //as we can get. - Predecoder::State Predecoder::doDisplacementState() - { - State nextState = ErrorState; - - getImmediate(immediateCollected, - emi.displacement, - displacementSize); - - DPRINTF(Predecoder, "Collecting %d byte displacement, got %d bytes.\n", - displacementSize, immediateCollected); - - if(displacementSize == immediateCollected) { - //Reset this for other immediates. - immediateCollected = 0; - //Sign extend the displacement - switch(displacementSize) - { - case 1: - emi.displacement = sext<8>(emi.displacement); - break; - case 2: - emi.displacement = sext<16>(emi.displacement); - break; - case 4: - emi.displacement = sext<32>(emi.displacement); - break; - default: - panic("Undefined displacement size!\n"); - } - DPRINTF(Predecoder, "Collected displacement %#x.\n", - emi.displacement); - if(immediateSize) { - nextState = ImmediateState; - } else { - emiIsReady = true; - nextState = ResetState; - } - - emi.dispSize = displacementSize; - } - else - nextState = DisplacementState; - return nextState; - } - - //Gather up the immediate, or at least as much of it - //as we can get - Predecoder::State Predecoder::doImmediateState() - { - State nextState = ErrorState; - - getImmediate(immediateCollected, - emi.immediate, - immediateSize); - - DPRINTF(Predecoder, "Collecting %d byte immediate, got %d bytes.\n", - immediateSize, immediateCollected); - - if(immediateSize == immediateCollected) - { - //Reset this for other immediates. - immediateCollected = 0; - - //XXX Warning! The following is an observed pattern and might - //not always be true! - - //Instructions which use 64 bit operands but 32 bit immediates - //need to have the immediate sign extended to 64 bits. - //Instructions which use true 64 bit immediates won't be - //affected, and instructions that use true 32 bit immediates - //won't notice. - switch(immediateSize) - { - case 4: - emi.immediate = sext<32>(emi.immediate); - break; - case 1: - emi.immediate = sext<8>(emi.immediate); - } - - DPRINTF(Predecoder, "Collected immediate %#x.\n", - emi.immediate); - emiIsReady = true; - nextState = ResetState; - } - else - nextState = ImmediateState; - return nextState; - } -} diff --git a/src/arch/x86/predecoder.hh b/src/arch/x86/predecoder.hh deleted file mode 100644 index f7c63684d..000000000 --- a/src/arch/x86/predecoder.hh +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2007 The Hewlett-Packard Development Company - * All rights reserved. - * - * The license below extends only to copyright in the software and shall - * not be construed as granting a license to any other intellectual - * property including but not limited to intellectual property relating - * to a hardware implementation of the functionality of the software - * licensed hereunder. You may use the software subject to the license - * terms below provided that you ensure that this notice is replicated - * unmodified and in its entirety in all distributions of the software, - * modified or unmodified, in source code or in binary form. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer; - * redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution; - * neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Authors: Gabe Black - */ - -#ifndef __ARCH_X86_PREDECODER_HH__ -#define __ARCH_X86_PREDECODER_HH__ - -#include <cassert> - -#include "arch/x86/regs/misc.hh" -#include "arch/x86/types.hh" -#include "base/bitfield.hh" -#include "base/misc.hh" -#include "base/trace.hh" -#include "base/types.hh" -#include "debug/Predecoder.hh" - -class ThreadContext; - -namespace X86ISA -{ - class Predecoder - { - private: - //These are defined and documented in predecoder_tables.cc - static const uint8_t Prefixes[256]; - static const uint8_t UsesModRM[2][256]; - static const uint8_t ImmediateType[2][256]; - static const uint8_t SizeTypeToSize[3][10]; - - protected: - ThreadContext * tc; - //The bytes to be predecoded - MachInst fetchChunk; - //The pc of the start of fetchChunk - Addr basePC; - //The pc the current instruction started at - Addr origPC; - //The offset into fetchChunk of current processing - int offset; - //The extended machine instruction being generated - ExtMachInst emi; - HandyM5Reg m5Reg; - - inline uint8_t getNextByte() - { - return ((uint8_t *)&fetchChunk)[offset]; - } - - void getImmediate(int &collected, uint64_t ¤t, int size) - { - //Figure out how many bytes we still need to get for the - //immediate. - int toGet = size - collected; - //Figure out how many bytes are left in our "buffer" - int remaining = sizeof(MachInst) - offset; - //Get as much as we need, up to the amount available. - toGet = toGet > remaining ? remaining : toGet; - - //Shift the bytes we want to be all the way to the right - uint64_t partialImm = fetchChunk >> (offset * 8); - //Mask off what we don't want - partialImm &= mask(toGet * 8); - //Shift it over to overlay with our displacement. - partialImm <<= (immediateCollected * 8); - //Put it into our displacement - current |= partialImm; - //Update how many bytes we've collected. - collected += toGet; - consumeBytes(toGet); - } - - inline void consumeByte() - { - offset++; - assert(offset <= sizeof(MachInst)); - if(offset == sizeof(MachInst)) - outOfBytes = true; - } - - inline void consumeBytes(int numBytes) - { - offset += numBytes; - assert(offset <= sizeof(MachInst)); - if(offset == sizeof(MachInst)) - outOfBytes = true; - } - - void doReset(); - - //State machine state - protected: - //Whether or not we're out of bytes - bool outOfBytes; - //Whether we've completed generating an ExtMachInst - bool emiIsReady; - //The size of the displacement value - int displacementSize; - //The size of the immediate value - int immediateSize; - //This is how much of any immediate value we've gotten. This is used - //for both the actual immediate and the displacement. - int immediateCollected; - - enum State { - ResetState, - PrefixState, - OpcodeState, - ModRMState, - SIBState, - DisplacementState, - ImmediateState, - //We should never get to this state. Getting here is an error. - ErrorState - }; - - State state; - - //Functions to handle each of the states - State doPrefixState(uint8_t); - State doOpcodeState(uint8_t); - State doModRMState(uint8_t); - State doSIBState(uint8_t); - State doDisplacementState(); - State doImmediateState(); - - public: - Predecoder(ThreadContext * _tc) : - tc(_tc), basePC(0), origPC(0), offset(0), - outOfBytes(true), emiIsReady(false), - state(ResetState) - { - emi.mode.mode = LongMode; - emi.mode.submode = SixtyFourBitMode; - m5Reg = 0; - } - - void reset() - { - state = ResetState; - } - - ThreadContext * getTC() - { - return tc; - } - - void setTC(ThreadContext * _tc) - { - tc = _tc; - } - - void process(); - - //Use this to give data to the predecoder. This should be used - //when there is control flow. - void moreBytes(const PCState &pc, Addr fetchPC, MachInst data) - { - DPRINTF(Predecoder, "Getting more bytes.\n"); - basePC = fetchPC; - offset = (fetchPC >= pc.instAddr()) ? 0 : pc.instAddr() - fetchPC; - fetchChunk = data; - outOfBytes = false; - process(); - } - - bool needMoreBytes() - { - return outOfBytes; - } - - bool extMachInstReady() - { - return emiIsReady; - } - - int - getInstSize() - { - int size = basePC + offset - origPC; - DPRINTF(Predecoder, - "Calculating the instruction size: " - "basePC: %#x offset: %#x origPC: %#x size: %d\n", - basePC, offset, origPC, size); - return size; - } - - //This returns a constant reference to the ExtMachInst to avoid a copy - const ExtMachInst & - getExtMachInst(X86ISA::PCState &nextPC) - { - assert(emiIsReady); - emiIsReady = false; - if (!nextPC.size()) { - Addr size = getInstSize(); - nextPC.size(size); - nextPC.npc(nextPC.pc() + size); - } - return emi; - } - }; -} - -#endif // __ARCH_X86_PREDECODER_HH__ diff --git a/src/arch/x86/types.hh b/src/arch/x86/types.hh index 6d9f600ff..a604c3efc 100644 --- a/src/arch/x86/types.hh +++ b/src/arch/x86/types.hh @@ -51,7 +51,7 @@ namespace X86ISA { - //This really determines how many bytes are passed to the predecoder. + //This really determines how many bytes are passed to the decoder. typedef uint64_t MachInst; enum Prefixes { @@ -127,7 +127,7 @@ namespace X86ISA RealMode }; - //The intermediate structure the x86 predecoder returns. + //The intermediate structure used by the x86 decoder. struct ExtMachInst { //Prefixes |