| /* | |
| * Copyright (c) 2012 Google | |
| * All rights reserved. | |
| * | |
| * 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_DECODER_HH__ | |
| #define __ARCH_X86_DECODER_HH__ | |
| #include <cassert> | |
| #include <unordered_map> | |
| #include <vector> | |
| #include "arch/x86/regs/misc.hh" | |
| #include "arch/x86/types.hh" | |
| #include "base/bitfield.hh" | |
| #include "base/logging.hh" | |
| #include "base/trace.hh" | |
| #include "base/types.hh" | |
| #include "cpu/decode_cache.hh" | |
| #include "cpu/static_inst.hh" | |
| #include "debug/Decoder.hh" | |
| namespace X86ISA | |
| { | |
| class ISA; | |
| class Decoder | |
| { | |
| private: | |
| //These are defined and documented in decoder_tables.cc | |
| static const uint8_t SizeTypeToSize[3][10]; | |
| typedef const uint8_t ByteTable[256]; | |
| static ByteTable Prefixes; | |
| static ByteTable UsesModRMOneByte; | |
| static ByteTable UsesModRMTwoByte; | |
| static ByteTable UsesModRMThreeByte0F38; | |
| static ByteTable UsesModRMThreeByte0F3A; | |
| static ByteTable ImmediateTypeOneByte; | |
| static ByteTable ImmediateTypeTwoByte; | |
| static ByteTable ImmediateTypeThreeByte0F38; | |
| static ByteTable ImmediateTypeThreeByte0F3A; | |
| static ByteTable ImmediateTypeVex[10]; | |
| protected: | |
| struct InstBytes | |
| { | |
| StaticInstPtr si; | |
| std::vector<MachInst> chunks; | |
| std::vector<MachInst> masks; | |
| int lastOffset; | |
| InstBytes() : lastOffset(0) | |
| {} | |
| }; | |
| static InstBytes dummy; | |
| //The bytes to be predecoded | |
| MachInst fetchChunk; | |
| InstBytes *instBytes; | |
| int chunkIdx; | |
| //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; | |
| //Predecoding state | |
| X86Mode mode; | |
| X86SubMode submode; | |
| uint8_t altOp; | |
| uint8_t defOp; | |
| uint8_t altAddr; | |
| uint8_t defAddr; | |
| uint8_t stack; | |
| 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); | |
| } | |
| void updateOffsetState() | |
| { | |
| assert(offset <= sizeof(MachInst)); | |
| if (offset == sizeof(MachInst)) { | |
| DPRINTF(Decoder, "At the end of a chunk, idx = %d, chunks = %d.\n", | |
| chunkIdx, instBytes->chunks.size()); | |
| chunkIdx++; | |
| if (chunkIdx == instBytes->chunks.size()) { | |
| outOfBytes = true; | |
| } else { | |
| offset = 0; | |
| fetchChunk = instBytes->chunks[chunkIdx]; | |
| basePC += sizeof(MachInst); | |
| } | |
| } | |
| } | |
| void consumeByte() | |
| { | |
| offset++; | |
| updateOffsetState(); | |
| } | |
| void consumeBytes(int numBytes) | |
| { | |
| offset += numBytes; | |
| updateOffsetState(); | |
| } | |
| //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, | |
| FromCacheState, | |
| PrefixState, | |
| Vex2Of2State, | |
| Vex2Of3State, | |
| Vex3Of3State, | |
| VexOpcodeState, | |
| OneByteOpcodeState, | |
| TwoByteOpcodeState, | |
| ThreeByte0F38OpcodeState, | |
| ThreeByte0F3AOpcodeState, | |
| 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 doResetState(); | |
| State doFromCacheState(); | |
| State doPrefixState(uint8_t); | |
| State doVex2Of2State(uint8_t); | |
| State doVex2Of3State(uint8_t); | |
| State doVex3Of3State(uint8_t); | |
| State doVexOpcodeState(uint8_t); | |
| State doOneByteOpcodeState(uint8_t); | |
| State doTwoByteOpcodeState(uint8_t); | |
| State doThreeByte0F38OpcodeState(uint8_t); | |
| State doThreeByte0F3AOpcodeState(uint8_t); | |
| State doModRMState(uint8_t); | |
| State doSIBState(uint8_t); | |
| State doDisplacementState(); | |
| State doImmediateState(); | |
| //Process the actual opcode found earlier, using the supplied tables. | |
| State processOpcode(ByteTable &immTable, ByteTable &modrmTable, | |
| bool addrSizedImm = false); | |
| // Process the opcode found with VEX / XOP prefix. | |
| State processExtendedOpcode(ByteTable &immTable); | |
| protected: | |
| /// Caching for decoded instruction objects. | |
| typedef RegVal CacheKey; | |
| typedef DecodeCache::AddrMap<Decoder::InstBytes> DecodePages; | |
| DecodePages *decodePages; | |
| typedef std::unordered_map<CacheKey, DecodePages *> AddrCacheMap; | |
| AddrCacheMap addrCacheMap; | |
| DecodeCache::InstMap<ExtMachInst> *instMap; | |
| typedef std::unordered_map< | |
| CacheKey, DecodeCache::InstMap<ExtMachInst> *> InstCacheMap; | |
| static InstCacheMap instCacheMap; | |
| public: | |
| Decoder(ISA* isa = nullptr) : basePC(0), origPC(0), offset(0), | |
| outOfBytes(true), instDone(false), | |
| state(ResetState) | |
| { | |
| emi.reset(); | |
| mode = LongMode; | |
| submode = SixtyFourBitMode; | |
| emi.mode.mode = mode; | |
| emi.mode.submode = submode; | |
| altOp = 0; | |
| defOp = 0; | |
| altAddr = 0; | |
| defAddr = 0; | |
| stack = 0; | |
| instBytes = &dummy; | |
| decodePages = NULL; | |
| instMap = NULL; | |
| } | |
| void setM5Reg(HandyM5Reg m5Reg) | |
| { | |
| mode = (X86Mode)(uint64_t)m5Reg.mode; | |
| submode = (X86SubMode)(uint64_t)m5Reg.submode; | |
| emi.mode.mode = mode; | |
| emi.mode.submode = submode; | |
| altOp = m5Reg.altOp; | |
| defOp = m5Reg.defOp; | |
| altAddr = m5Reg.altAddr; | |
| defAddr = m5Reg.defAddr; | |
| stack = m5Reg.stack; | |
| AddrCacheMap::iterator amIter = addrCacheMap.find(m5Reg); | |
| if (amIter != addrCacheMap.end()) { | |
| decodePages = amIter->second; | |
| } else { | |
| decodePages = new DecodePages; | |
| addrCacheMap[m5Reg] = decodePages; | |
| } | |
| InstCacheMap::iterator imIter = instCacheMap.find(m5Reg); | |
| if (imIter != instCacheMap.end()) { | |
| instMap = imIter->second; | |
| } else { | |
| instMap = new DecodeCache::InstMap<ExtMachInst>; | |
| instCacheMap[m5Reg] = instMap; | |
| } | |
| } | |
| void takeOverFrom(Decoder *old) | |
| { | |
| mode = old->mode; | |
| submode = old->submode; | |
| emi.mode.mode = mode; | |
| emi.mode.submode = submode; | |
| altOp = old->altOp; | |
| defOp = old->defOp; | |
| altAddr = old->altAddr; | |
| defAddr = old->defAddr; | |
| stack = old->stack; | |
| } | |
| void reset() | |
| { | |
| state = ResetState; | |
| } | |
| 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); | |
| } | |
| } | |
| public: | |
| StaticInstPtr decodeInst(ExtMachInst mach_inst); | |
| /// Decode a machine instruction. | |
| /// @param mach_inst The binary instruction to decode. | |
| /// @retval A pointer to the corresponding StaticInst object. | |
| StaticInstPtr decode(ExtMachInst mach_inst, Addr addr); | |
| StaticInstPtr decode(X86ISA::PCState &nextPC); | |
| }; | |
| } // namespace X86ISA | |
| #endif // __ARCH_X86_DECODER_HH__ |