aboutsummaryrefslogtreecommitdiff
path: root/src/ARMJIT_x64/ARMJIT_Compiler.h
blob: b3bc8d78544eb6566249aba4cb629fb9c26cd965 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
    Copyright 2016-2022 melonDS team, RSDuck

    This file is part of melonDS.

    melonDS is free software: you can redistribute it and/or modify it under
    the terms of the GNU General Public License as published by the Free
    Software Foundation, either version 3 of the License, or (at your option)
    any later version.

    melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with melonDS. If not, see http://www.gnu.org/licenses/.
*/

#ifndef ARMJIT_X64_COMPILER_H
#define ARMJIT_X64_COMPILER_H

#include "../dolphin/x64Emitter.h"

#include "../ARMJIT.h"
#include "../ARMJIT_Internal.h"
#include "../ARMJIT_RegisterCache.h"

#ifdef JIT_PROFILING_ENABLED
#include <jitprofiling.h>
#endif

#include <unordered_map>

namespace ARMJIT
{

const Gen::X64Reg RCPU = Gen::RBP;
const Gen::X64Reg RCPSR = Gen::R15;

const Gen::X64Reg RSCRATCH = Gen::EAX;
const Gen::X64Reg RSCRATCH2 = Gen::EDX;
const Gen::X64Reg RSCRATCH3 = Gen::ECX;
const Gen::X64Reg RSCRATCH4 = Gen::R8;

struct LoadStorePatch
{
    void* PatchFunc;
    s16 Offset;
    u16 Size;
};

struct Op2
{
    Op2()
    {}

    Op2(u32 imm)
        : IsImm(true), Imm(imm)
    {}
    Op2(int reg, int op, int amount)
        : IsImm(false)
    {
        Reg.Reg = reg;
        Reg.Op = op;
        Reg.Amount = amount;
    }

    bool IsImm;
    union
    {
        struct
        {
            int Reg, Op, Amount;
        } Reg;
        u32 Imm;
    };
};

class Compiler : public Gen::XEmitter
{
public:
    Compiler();

    void Reset();

    JitBlockEntry CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[], int instrsCount, bool hasMemoryInstr);

    void LoadReg(int reg, Gen::X64Reg nativeReg);
    void SaveReg(int reg, Gen::X64Reg nativeReg);

    bool CanCompile(bool thumb, u16 kind);

    typedef void (Compiler::*CompileFunc)();

    void Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR = false);
    void Comp_JumpTo(u32 addr, bool forceNonConstantCycles = false);

    void Comp_AddCycles_C(bool forceNonConstant = false);
    void Comp_AddCycles_CI(u32 i);
    void Comp_AddCycles_CI(Gen::X64Reg i, int add);
    void Comp_AddCycles_CDI();
    void Comp_AddCycles_CD();

    enum
    {
        opSetsFlags = 1 << 0,
        opSymmetric = 1 << 1,
        opRetriveCV = 1 << 2,
        opInvertCarry = 1 << 3,
        opSyncCarry = 1 << 4,
        opInvertOp2 = 1 << 5,
    };

    void Nop() {}

    void A_Comp_Arith();
    void A_Comp_MovOp();
    void A_Comp_CmpOp();

    void A_Comp_MUL_MLA();
    void A_Comp_Mul_Long();

    void A_Comp_CLZ();
    
    void A_Comp_MemWB();
    void A_Comp_MemHalf();
    void A_Comp_LDM_STM();

    void A_Comp_BranchImm();
    void A_Comp_BranchXchangeReg();

    void A_Comp_MRS();
    void A_Comp_MSR();

    void T_Comp_ShiftImm();
    void T_Comp_AddSub_();
    void T_Comp_ALU_Imm8();
    void T_Comp_ALU();
    void T_Comp_ALU_HiReg();
    void T_Comp_MUL();

    void T_Comp_RelAddr();
    void T_Comp_AddSP();

    void T_Comp_MemReg();
    void T_Comp_MemImm();
    void T_Comp_MemRegHalf();
    void T_Comp_MemImmHalf();
    void T_Comp_LoadPCRel();
    void T_Comp_MemSPRel();
    void T_Comp_PUSH_POP();
    void T_Comp_LDMIA_STMIA();

    void T_Comp_BCOND();
    void T_Comp_B();
    void T_Comp_BranchXchangeReg();
    void T_Comp_BL_LONG_1();
    void T_Comp_BL_LONG_2();
    void T_Comp_BL_Merged();

    enum
    {
        memop_Writeback = 1 << 0,
        memop_Post = 1 << 1,
        memop_SignExtend = 1 << 2,
        memop_Store = 1 << 3,
        memop_SubtractOffset = 1 << 4
    };
    void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags);
    s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn);
    bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr);

    void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&), 
        Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
    void Comp_ArithTriOpReverse(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&),
        Gen::OpArg rd, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed, int opFlags);
    void Comp_CmpOp(int op, Gen::OpArg rn, Gen::OpArg op2, bool carryUsed);

    void Comp_MulOp(bool S, bool add, Gen::OpArg rd, Gen::OpArg rm, Gen::OpArg rs, Gen::OpArg rn);

    void Comp_RetriveFlags(bool sign, bool retriveCV, bool carryUsed);

    void Comp_SpecialBranchBehaviour(bool taken);


    Gen::OpArg Comp_RegShiftImm(int op, int amount, Gen::OpArg rm, bool S, bool& carryUsed);
    Gen::OpArg Comp_RegShiftReg(int op, Gen::OpArg rs, Gen::OpArg rm, bool S, bool& carryUsed);

    Gen::OpArg A_Comp_GetALUOp2(bool S, bool& carryUsed);

    void LoadCPSR();
    void SaveCPSR(bool flagClean = true);

    bool FlagsNZRequired()
    { return CurInstr.SetFlags & 0xC; }

    Gen::FixupBranch CheckCondition(u32 cond);

    void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true);
    void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged);

    Gen::OpArg MapReg(int reg)
    {
        if (reg == 15 && !(RegCache.LoadedRegs & (1 << 15)))
            return Gen::Imm32(R15);

        assert(RegCache.Mapping[reg] != Gen::INVALID_REG);
        return Gen::R(RegCache.Mapping[reg]);
    }

    JitBlockEntry AddEntryOffset(u32 offset)
    {
        return (JitBlockEntry)(ResetStart + offset);
    }

    u32 SubEntryOffset(JitBlockEntry entry)
    {
        return (u8*)entry - ResetStart;
    }

    void SwitchToNearCode()
    {
        FarCode = GetWritableCodePtr();
        SetCodePtr(NearCode);
    }

    void SwitchToFarCode()
    {
        NearCode = GetWritableCodePtr();
        SetCodePtr(FarCode);
    }

    bool IsJITFault(u8* addr);

    u8* RewriteMemAccess(u8* pc);

#ifdef JIT_PROFILING_ENABLED
    void CreateMethod(const char* namefmt, void* start, ...);
#endif

    u8* FarCode;
    u8* NearCode;
    u32 FarSize;
    u32 NearSize;

    u8* NearStart;
    u8* FarStart;

    void* PatchedStoreFuncs[2][2][3][16];
    void* PatchedLoadFuncs[2][2][3][2][16];

    std::unordered_map<u8*, LoadStorePatch> LoadStorePatches;

    u8* ResetStart;
    u32 CodeMemSize;

    bool Exit;
    bool IrregularCycles;

    void* ReadBanked;
    void* WriteBanked;

    bool CPSRDirty = false;

    FetchedInstr CurInstr;

    RegisterCache<Compiler, Gen::X64Reg> RegCache;

    bool Thumb;
    u32 Num;
    u32 R15;
    u32 CodeRegion;

    u32 ConstantCycles;

    ARM* CurCPU;
};

}

#endif