| /* |
| * 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. |
| */ |
| |
| #ifndef __ARCH_X86_DECODER_HH__ |
| #define __ARCH_X86_DECODER_HH__ |
| |
| #include <cassert> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "arch/generic/decoder.hh" |
| #include "arch/x86/microcode_rom.hh" |
| #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" |
| #include "params/X86Decoder.hh" |
| |
| namespace gem5 |
| { |
| |
| namespace X86ISA |
| { |
| |
| class ISA; |
| class Decoder : public InstDecoder |
| { |
| 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[2]; |
| |
| 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 X86ISAInst::MicrocodeRom microcodeRom; |
| |
| protected: |
| using MachInst = uint64_t; |
| |
| 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 = &dummy; |
| int chunkIdx; |
| // The pc of the start of fetchChunk. |
| Addr basePC = 0; |
| // The pc the current instruction started at. |
| Addr origPC = 0; |
| // The offset into fetchChunk of current processing. |
| int offset = 0; |
| // The extended machine instruction being generated. |
| ExtMachInst emi; |
| // Predecoding state. |
| X86Mode mode = LongMode; |
| X86SubMode submode = SixtyFourBitMode; |
| uint8_t altOp = 0; |
| uint8_t defOp = 0; |
| uint8_t altAddr = 0; |
| uint8_t defAddr = 0; |
| uint8_t stack = 0; |
| |
| uint8_t cpl = 0; |
| |
| 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: |
| // 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 = ResetState; |
| |
| // 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 decode_cache::AddrMap<Decoder::InstBytes> DecodePages; |
| DecodePages *decodePages = nullptr; |
| typedef std::unordered_map<CacheKey, DecodePages *> AddrCacheMap; |
| AddrCacheMap addrCacheMap; |
| |
| decode_cache::InstMap<ExtMachInst> *instMap = nullptr; |
| typedef std::unordered_map< |
| CacheKey, decode_cache::InstMap<ExtMachInst> *> InstCacheMap; |
| static InstCacheMap instCacheMap; |
| |
| 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); |
| |
| void process(); |
| |
| public: |
| Decoder(const X86DecoderParams &p) : InstDecoder(p, &fetchChunk) |
| { |
| emi.reset(); |
| emi.mode.cpl = cpl; |
| emi.mode.mode = mode; |
| emi.mode.submode = submode; |
| } |
| |
| void |
| setM5Reg(HandyM5Reg m5Reg) |
| { |
| cpl = m5Reg.cpl; |
| mode = (X86Mode)(uint64_t)m5Reg.mode; |
| submode = (X86SubMode)(uint64_t)m5Reg.submode; |
| emi.mode.cpl = cpl; |
| 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 decode_cache::InstMap<ExtMachInst>; |
| instCacheMap[m5Reg] = instMap; |
| } |
| } |
| |
| void |
| takeOverFrom(InstDecoder *old) override |
| { |
| InstDecoder::takeOverFrom(old); |
| |
| Decoder *dec = dynamic_cast<Decoder *>(old); |
| assert(dec); |
| |
| cpl = dec->cpl; |
| mode = dec->mode; |
| submode = dec->submode; |
| emi.mode.cpl = cpl; |
| emi.mode.mode = mode; |
| emi.mode.submode = submode; |
| altOp = dec->altOp; |
| defOp = dec->defOp; |
| altAddr = dec->altAddr; |
| defAddr = dec->defAddr; |
| stack = dec->stack; |
| } |
| |
| void |
| reset() override |
| { |
| InstDecoder::reset(); |
| state = ResetState; |
| } |
| |
| // Use this to give data to the decoder. This should be used |
| // when there is control flow. |
| void |
| moreBytes(const PCStateBase &pc, Addr fetchPC) override |
| { |
| DPRINTF(Decoder, "Getting more bytes.\n"); |
| basePC = fetchPC; |
| offset = (fetchPC >= pc.instAddr()) ? 0 : pc.instAddr() - fetchPC; |
| fetchChunk = letoh(fetchChunk); |
| outOfBytes = false; |
| process(); |
| } |
| |
| 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 decode(PCStateBase &next_pc) override; |
| |
| StaticInstPtr fetchRomMicroop( |
| MicroPC micropc, StaticInstPtr curMacroop) override; |
| }; |
| |
| } // namespace X86ISA |
| } // namespace gem5 |
| |
| #endif // __ARCH_X86_DECODER_HH__ |