decoder.hh revision 12334:e0ab29a34764
1/*
2 * Copyright (c) 2012 Google
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Gabe Black
29 */
30
31#ifndef __ARCH_X86_DECODER_HH__
32#define __ARCH_X86_DECODER_HH__
33
34#include <cassert>
35#include <unordered_map>
36#include <vector>
37
38#include "arch/x86/regs/misc.hh"
39#include "arch/x86/types.hh"
40#include "base/bitfield.hh"
41#include "base/logging.hh"
42#include "base/trace.hh"
43#include "base/types.hh"
44#include "cpu/decode_cache.hh"
45#include "cpu/static_inst.hh"
46#include "debug/Decoder.hh"
47
48namespace X86ISA
49{
50
51class ISA;
52class Decoder
53{
54  private:
55    //These are defined and documented in decoder_tables.cc
56    static const uint8_t SizeTypeToSize[3][10];
57    typedef const uint8_t ByteTable[256];
58    static ByteTable Prefixes;
59
60    static ByteTable UsesModRMOneByte;
61    static ByteTable UsesModRMTwoByte;
62    static ByteTable UsesModRMThreeByte0F38;
63    static ByteTable UsesModRMThreeByte0F3A;
64
65    static ByteTable ImmediateTypeOneByte;
66    static ByteTable ImmediateTypeTwoByte;
67    static ByteTable ImmediateTypeThreeByte0F38;
68    static ByteTable ImmediateTypeThreeByte0F3A;
69    static ByteTable ImmediateTypeVex[10];
70
71  protected:
72    struct InstBytes
73    {
74        StaticInstPtr si;
75        std::vector<MachInst> chunks;
76        std::vector<MachInst> masks;
77        int lastOffset;
78
79        InstBytes() : lastOffset(0)
80        {}
81    };
82
83    static InstBytes dummy;
84
85    //The bytes to be predecoded
86    MachInst fetchChunk;
87    InstBytes *instBytes;
88    int chunkIdx;
89    //The pc of the start of fetchChunk
90    Addr basePC;
91    //The pc the current instruction started at
92    Addr origPC;
93    //The offset into fetchChunk of current processing
94    int offset;
95    //The extended machine instruction being generated
96    ExtMachInst emi;
97    //Predecoding state
98    X86Mode mode;
99    X86SubMode submode;
100    uint8_t altOp;
101    uint8_t defOp;
102    uint8_t altAddr;
103    uint8_t defAddr;
104    uint8_t stack;
105
106    uint8_t getNextByte()
107    {
108        return ((uint8_t *)&fetchChunk)[offset];
109    }
110
111    void getImmediate(int &collected, uint64_t &current, int size)
112    {
113        //Figure out how many bytes we still need to get for the
114        //immediate.
115        int toGet = size - collected;
116        //Figure out how many bytes are left in our "buffer"
117        int remaining = sizeof(MachInst) - offset;
118        //Get as much as we need, up to the amount available.
119        toGet = toGet > remaining ? remaining : toGet;
120
121        //Shift the bytes we want to be all the way to the right
122        uint64_t partialImm = fetchChunk >> (offset * 8);
123        //Mask off what we don't want
124        partialImm &= mask(toGet * 8);
125        //Shift it over to overlay with our displacement.
126        partialImm <<= (immediateCollected * 8);
127        //Put it into our displacement
128        current |= partialImm;
129        //Update how many bytes we've collected.
130        collected += toGet;
131        consumeBytes(toGet);
132    }
133
134    void updateOffsetState()
135    {
136        assert(offset <= sizeof(MachInst));
137        if (offset == sizeof(MachInst)) {
138            DPRINTF(Decoder, "At the end of a chunk, idx = %d, chunks = %d.\n",
139                    chunkIdx, instBytes->chunks.size());
140            chunkIdx++;
141            if (chunkIdx == instBytes->chunks.size()) {
142                outOfBytes = true;
143            } else {
144                offset = 0;
145                fetchChunk = instBytes->chunks[chunkIdx];
146                basePC += sizeof(MachInst);
147            }
148        }
149    }
150
151    void consumeByte()
152    {
153        offset++;
154        updateOffsetState();
155    }
156
157    void consumeBytes(int numBytes)
158    {
159        offset += numBytes;
160        updateOffsetState();
161    }
162
163    //State machine state
164  protected:
165    //Whether or not we're out of bytes
166    bool outOfBytes;
167    //Whether we've completed generating an ExtMachInst
168    bool instDone;
169    //The size of the displacement value
170    int displacementSize;
171    //The size of the immediate value
172    int immediateSize;
173    //This is how much of any immediate value we've gotten. This is used
174    //for both the actual immediate and the displacement.
175    int immediateCollected;
176
177    enum State {
178        ResetState,
179        FromCacheState,
180        PrefixState,
181        Vex2Of2State,
182        Vex2Of3State,
183        Vex3Of3State,
184        VexOpcodeState,
185        OneByteOpcodeState,
186        TwoByteOpcodeState,
187        ThreeByte0F38OpcodeState,
188        ThreeByte0F3AOpcodeState,
189        ModRMState,
190        SIBState,
191        DisplacementState,
192        ImmediateState,
193        //We should never get to this state. Getting here is an error.
194        ErrorState
195    };
196
197    State state;
198
199    //Functions to handle each of the states
200    State doResetState();
201    State doFromCacheState();
202    State doPrefixState(uint8_t);
203    State doVex2Of2State(uint8_t);
204    State doVex2Of3State(uint8_t);
205    State doVex3Of3State(uint8_t);
206    State doVexOpcodeState(uint8_t);
207    State doOneByteOpcodeState(uint8_t);
208    State doTwoByteOpcodeState(uint8_t);
209    State doThreeByte0F38OpcodeState(uint8_t);
210    State doThreeByte0F3AOpcodeState(uint8_t);
211    State doModRMState(uint8_t);
212    State doSIBState(uint8_t);
213    State doDisplacementState();
214    State doImmediateState();
215
216    //Process the actual opcode found earlier, using the supplied tables.
217    State processOpcode(ByteTable &immTable, ByteTable &modrmTable,
218                        bool addrSizedImm = false);
219    // Process the opcode found with VEX / XOP prefix.
220    State processExtendedOpcode(ByteTable &immTable);
221
222  protected:
223    /// Caching for decoded instruction objects.
224
225    typedef MiscReg CacheKey;
226
227    typedef DecodeCache::AddrMap<Decoder::InstBytes> DecodePages;
228    DecodePages *decodePages;
229    typedef std::unordered_map<CacheKey, DecodePages *> AddrCacheMap;
230    AddrCacheMap addrCacheMap;
231
232    DecodeCache::InstMap *instMap;
233    typedef std::unordered_map<CacheKey, DecodeCache::InstMap *> InstCacheMap;
234    static InstCacheMap instCacheMap;
235
236  public:
237    Decoder(ISA* isa = nullptr) : basePC(0), origPC(0), offset(0),
238        outOfBytes(true), instDone(false),
239        state(ResetState)
240    {
241        memset(&emi, 0, sizeof(emi));
242        mode = LongMode;
243        submode = SixtyFourBitMode;
244        emi.mode.mode = mode;
245        emi.mode.submode = submode;
246        altOp = 0;
247        defOp = 0;
248        altAddr = 0;
249        defAddr = 0;
250        stack = 0;
251        instBytes = &dummy;
252        decodePages = NULL;
253        instMap = NULL;
254    }
255
256    void setM5Reg(HandyM5Reg m5Reg)
257    {
258        mode = (X86Mode)(uint64_t)m5Reg.mode;
259        submode = (X86SubMode)(uint64_t)m5Reg.submode;
260        emi.mode.mode = mode;
261        emi.mode.submode = submode;
262        altOp = m5Reg.altOp;
263        defOp = m5Reg.defOp;
264        altAddr = m5Reg.altAddr;
265        defAddr = m5Reg.defAddr;
266        stack = m5Reg.stack;
267
268        AddrCacheMap::iterator amIter = addrCacheMap.find(m5Reg);
269        if (amIter != addrCacheMap.end()) {
270            decodePages = amIter->second;
271        } else {
272            decodePages = new DecodePages;
273            addrCacheMap[m5Reg] = decodePages;
274        }
275
276        InstCacheMap::iterator imIter = instCacheMap.find(m5Reg);
277        if (imIter != instCacheMap.end()) {
278            instMap = imIter->second;
279        } else {
280            instMap = new DecodeCache::InstMap;
281            instCacheMap[m5Reg] = instMap;
282        }
283    }
284
285    void takeOverFrom(Decoder *old)
286    {
287        mode = old->mode;
288        submode = old->submode;
289        emi.mode.mode = mode;
290        emi.mode.submode = submode;
291        altOp = old->altOp;
292        defOp = old->defOp;
293        altAddr = old->altAddr;
294        defAddr = old->defAddr;
295        stack = old->stack;
296    }
297
298    void reset()
299    {
300        state = ResetState;
301    }
302
303    void process();
304
305    //Use this to give data to the decoder. This should be used
306    //when there is control flow.
307    void moreBytes(const PCState &pc, Addr fetchPC, MachInst data)
308    {
309        DPRINTF(Decoder, "Getting more bytes.\n");
310        basePC = fetchPC;
311        offset = (fetchPC >= pc.instAddr()) ? 0 : pc.instAddr() - fetchPC;
312        fetchChunk = data;
313        outOfBytes = false;
314        process();
315    }
316
317    bool needMoreBytes()
318    {
319        return outOfBytes;
320    }
321
322    bool instReady()
323    {
324        return instDone;
325    }
326
327    void
328    updateNPC(X86ISA::PCState &nextPC)
329    {
330        if (!nextPC.size()) {
331            int size = basePC + offset - origPC;
332            DPRINTF(Decoder,
333                    "Calculating the instruction size: "
334                    "basePC: %#x offset: %#x origPC: %#x size: %d\n",
335                    basePC, offset, origPC, size);
336            nextPC.size(size);
337            nextPC.npc(nextPC.pc() + size);
338        }
339    }
340
341  public:
342    StaticInstPtr decodeInst(ExtMachInst mach_inst);
343
344    /// Decode a machine instruction.
345    /// @param mach_inst The binary instruction to decode.
346    /// @retval A pointer to the corresponding StaticInst object.
347    StaticInstPtr decode(ExtMachInst mach_inst, Addr addr);
348    StaticInstPtr decode(X86ISA::PCState &nextPC);
349};
350
351} // namespace X86ISA
352
353#endif // __ARCH_X86_DECODER_HH__
354