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 RegVal 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<ExtMachInst> *instMap;
233    typedef std::unordered_map<
234            CacheKey, DecodeCache::InstMap<ExtMachInst> *> InstCacheMap;
235    static InstCacheMap instCacheMap;
236
237  public:
238    Decoder(ISA* isa = nullptr) : basePC(0), origPC(0), offset(0),
239        outOfBytes(true), instDone(false),
240        state(ResetState)
241    {
242        emi.reset();
243        mode = LongMode;
244        submode = SixtyFourBitMode;
245        emi.mode.mode = mode;
246        emi.mode.submode = submode;
247        altOp = 0;
248        defOp = 0;
249        altAddr = 0;
250        defAddr = 0;
251        stack = 0;
252        instBytes = &dummy;
253        decodePages = NULL;
254        instMap = NULL;
255    }
256
257    void setM5Reg(HandyM5Reg m5Reg)
258    {
259        mode = (X86Mode)(uint64_t)m5Reg.mode;
260        submode = (X86SubMode)(uint64_t)m5Reg.submode;
261        emi.mode.mode = mode;
262        emi.mode.submode = submode;
263        altOp = m5Reg.altOp;
264        defOp = m5Reg.defOp;
265        altAddr = m5Reg.altAddr;
266        defAddr = m5Reg.defAddr;
267        stack = m5Reg.stack;
268
269        AddrCacheMap::iterator amIter = addrCacheMap.find(m5Reg);
270        if (amIter != addrCacheMap.end()) {
271            decodePages = amIter->second;
272        } else {
273            decodePages = new DecodePages;
274            addrCacheMap[m5Reg] = decodePages;
275        }
276
277        InstCacheMap::iterator imIter = instCacheMap.find(m5Reg);
278        if (imIter != instCacheMap.end()) {
279            instMap = imIter->second;
280        } else {
281            instMap = new DecodeCache::InstMap<ExtMachInst>;
282            instCacheMap[m5Reg] = instMap;
283        }
284    }
285
286    void takeOverFrom(Decoder *old)
287    {
288        mode = old->mode;
289        submode = old->submode;
290        emi.mode.mode = mode;
291        emi.mode.submode = submode;
292        altOp = old->altOp;
293        defOp = old->defOp;
294        altAddr = old->altAddr;
295        defAddr = old->defAddr;
296        stack = old->stack;
297    }
298
299    void reset()
300    {
301        state = ResetState;
302    }
303
304    void process();
305
306    //Use this to give data to the decoder. This should be used
307    //when there is control flow.
308    void moreBytes(const PCState &pc, Addr fetchPC, MachInst data)
309    {
310        DPRINTF(Decoder, "Getting more bytes.\n");
311        basePC = fetchPC;
312        offset = (fetchPC >= pc.instAddr()) ? 0 : pc.instAddr() - fetchPC;
313        fetchChunk = data;
314        outOfBytes = false;
315        process();
316    }
317
318    bool needMoreBytes()
319    {
320        return outOfBytes;
321    }
322
323    bool instReady()
324    {
325        return instDone;
326    }
327
328    void
329    updateNPC(X86ISA::PCState &nextPC)
330    {
331        if (!nextPC.size()) {
332            int size = basePC + offset - origPC;
333            DPRINTF(Decoder,
334                    "Calculating the instruction size: "
335                    "basePC: %#x offset: %#x origPC: %#x size: %d\n",
336                    basePC, offset, origPC, size);
337            nextPC.size(size);
338            nextPC.npc(nextPC.pc() + size);
339        }
340    }
341
342  public:
343    StaticInstPtr decodeInst(ExtMachInst mach_inst);
344
345    /// Decode a machine instruction.
346    /// @param mach_inst The binary instruction to decode.
347    /// @retval A pointer to the corresponding StaticInst object.
348    StaticInstPtr decode(ExtMachInst mach_inst, Addr addr);
349    StaticInstPtr decode(X86ISA::PCState &nextPC);
350};
351
352} // namespace X86ISA
353
354#endif // __ARCH_X86_DECODER_HH__
355