summaryrefslogtreecommitdiffstats
path: root/js/src/jit/mips64/Assembler-mips64.h
blob: 5cb9d1239a07029c63b6010405472e9820aae8a1 (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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef jit_mips64_Assembler_mips64_h
#define jit_mips64_Assembler_mips64_h

#include "jit/mips-shared/Assembler-mips-shared.h"

#include "jit/mips64/Architecture-mips64.h"

namespace js {
namespace jit {

static constexpr Register CallTempReg4 = a4;
static constexpr Register CallTempReg5 = a5;

static constexpr Register CallTempNonArgRegs[] = { t0, t1, t2, t3 };
static const uint32_t NumCallTempNonArgRegs = mozilla::ArrayLength(CallTempNonArgRegs);

class ABIArgGenerator
{
    unsigned usedArgSlots_;
    bool firstArgFloat;
    ABIArg current_;

  public:
    ABIArgGenerator();
    ABIArg next(MIRType argType);
    ABIArg& current() { return current_; }

    uint32_t stackBytesConsumedSoFar() const {
        if (usedArgSlots_ <= 8)
            return 0;

        return (usedArgSlots_ - 8) * sizeof(int64_t);
    }
};

static constexpr Register ABINonArgReg0 = t0;
static constexpr Register ABINonArgReg1 = t1;
static constexpr Register ABINonArgReg2 = t2;
static constexpr Register ABINonArgReturnReg0 = t0;
static constexpr Register ABINonArgReturnReg1 = t1;

// TLS pointer argument register for WebAssembly functions. This must not alias
// any other register used for passing function arguments or return values.
// Preserved by WebAssembly functions.
static constexpr Register WasmTlsReg = s5;

// Registers used for wasm table calls. These registers must be disjoint
// from the ABI argument registers, WasmTlsReg and each other.
static constexpr Register WasmTableCallScratchReg = ABINonArgReg0;
static constexpr Register WasmTableCallSigReg = ABINonArgReg1;
static constexpr Register WasmTableCallIndexReg = ABINonArgReg2;

static constexpr Register JSReturnReg = v1;
static constexpr Register JSReturnReg_Type = JSReturnReg;
static constexpr Register JSReturnReg_Data = JSReturnReg;
static constexpr Register64 ReturnReg64(ReturnReg);
static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatRegisters::Single };
static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegisters::Double };
static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::f23, FloatRegisters::Single };
static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::f23, FloatRegisters::Double };
static constexpr FloatRegister SecondScratchFloat32Reg = { FloatRegisters::f21, FloatRegisters::Single };
static constexpr FloatRegister SecondScratchDoubleReg = { FloatRegisters::f21, FloatRegisters::Double };

// Registers used in the GenerateFFIIonExit Disable Activation block.
// None of these may be the second scratch register (t8).
static constexpr Register WasmIonExitRegReturnData = JSReturnReg_Data;
static constexpr Register WasmIonExitRegReturnType = JSReturnReg_Type;

static constexpr FloatRegister f0  = { FloatRegisters::f0, FloatRegisters::Double };
static constexpr FloatRegister f1  = { FloatRegisters::f1, FloatRegisters::Double };
static constexpr FloatRegister f2  = { FloatRegisters::f2, FloatRegisters::Double };
static constexpr FloatRegister f3  = { FloatRegisters::f3, FloatRegisters::Double };
static constexpr FloatRegister f4  = { FloatRegisters::f4, FloatRegisters::Double };
static constexpr FloatRegister f5  = { FloatRegisters::f5, FloatRegisters::Double };
static constexpr FloatRegister f6  = { FloatRegisters::f6, FloatRegisters::Double };
static constexpr FloatRegister f7  = { FloatRegisters::f7, FloatRegisters::Double };
static constexpr FloatRegister f8  = { FloatRegisters::f8, FloatRegisters::Double };
static constexpr FloatRegister f9  = { FloatRegisters::f9, FloatRegisters::Double };
static constexpr FloatRegister f10 = { FloatRegisters::f10, FloatRegisters::Double };
static constexpr FloatRegister f11 = { FloatRegisters::f11, FloatRegisters::Double };
static constexpr FloatRegister f12 = { FloatRegisters::f12, FloatRegisters::Double };
static constexpr FloatRegister f13 = { FloatRegisters::f13, FloatRegisters::Double };
static constexpr FloatRegister f14 = { FloatRegisters::f14, FloatRegisters::Double };
static constexpr FloatRegister f15 = { FloatRegisters::f15, FloatRegisters::Double };
static constexpr FloatRegister f16 = { FloatRegisters::f16, FloatRegisters::Double };
static constexpr FloatRegister f17 = { FloatRegisters::f17, FloatRegisters::Double };
static constexpr FloatRegister f18 = { FloatRegisters::f18, FloatRegisters::Double };
static constexpr FloatRegister f19 = { FloatRegisters::f19, FloatRegisters::Double };
static constexpr FloatRegister f20 = { FloatRegisters::f20, FloatRegisters::Double };
static constexpr FloatRegister f21 = { FloatRegisters::f21, FloatRegisters::Double };
static constexpr FloatRegister f22 = { FloatRegisters::f22, FloatRegisters::Double };
static constexpr FloatRegister f23 = { FloatRegisters::f23, FloatRegisters::Double };
static constexpr FloatRegister f24 = { FloatRegisters::f24, FloatRegisters::Double };
static constexpr FloatRegister f25 = { FloatRegisters::f25, FloatRegisters::Double };
static constexpr FloatRegister f26 = { FloatRegisters::f26, FloatRegisters::Double };
static constexpr FloatRegister f27 = { FloatRegisters::f27, FloatRegisters::Double };
static constexpr FloatRegister f28 = { FloatRegisters::f28, FloatRegisters::Double };
static constexpr FloatRegister f29 = { FloatRegisters::f29, FloatRegisters::Double };
static constexpr FloatRegister f30 = { FloatRegisters::f30, FloatRegisters::Double };
static constexpr FloatRegister f31 = { FloatRegisters::f31, FloatRegisters::Double };

// MIPS64 CPUs can only load multibyte data that is "naturally"
// eight-byte-aligned, sp register should be sixteen-byte-aligned.
static constexpr uint32_t ABIStackAlignment = 16;
static constexpr uint32_t JitStackAlignment = 16;

static constexpr uint32_t JitStackValueAlignment = JitStackAlignment / sizeof(Value);
static_assert(JitStackAlignment % sizeof(Value) == 0 && JitStackValueAlignment >= 1,
  "Stack alignment should be a non-zero multiple of sizeof(Value)");

// TODO this is just a filler to prevent a build failure. The MIPS SIMD
// alignment requirements still need to be explored.
// TODO Copy the static_asserts from x64/x86 assembler files.
static constexpr uint32_t SimdMemoryAlignment = 16;

static constexpr uint32_t WasmStackAlignment = SimdMemoryAlignment;

// Does this architecture support SIMD conversions between Uint32x4 and Float32x4?
static constexpr bool SupportsUint32x4FloatConversions = false;

// Does this architecture support comparisons of unsigned integer vectors?
static constexpr bool SupportsUint8x16Compares = false;
static constexpr bool SupportsUint16x8Compares = false;
static constexpr bool SupportsUint32x4Compares = false;

static constexpr Scale ScalePointer = TimesEight;

class Assembler : public AssemblerMIPSShared
{
  public:
    Assembler()
      : AssemblerMIPSShared()
    { }

    // MacroAssemblers hold onto gcthings, so they are traced by the GC.
    void trace(JSTracer* trc);

    static uintptr_t GetPointer(uint8_t*);

    using AssemblerMIPSShared::bind;

    void bind(RepatchLabel* label);
    void Bind(uint8_t* rawCode, CodeOffset* label, const void* address);

    static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
    static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);

    void bind(InstImm* inst, uintptr_t branch, uintptr_t target);

    static uint32_t PatchWrite_NearCallSize();

    static uint64_t ExtractLoad64Value(Instruction* inst0);
    static void UpdateLoad64Value(Instruction* inst0, uint64_t value);
    static void WriteLoad64Instructions(Instruction* inst0, Register reg, uint64_t value);


    static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall);
    static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
                                        ImmPtr expectedValue);
    static void PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newValue,
                                        PatchedImmPtr expectedValue);

    static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
    static uint64_t ExtractInstructionImmediate(uint8_t* code);

    static void ToggleCall(CodeLocationLabel inst_, bool enabled);
}; // Assembler

static const uint32_t NumIntArgRegs = 8;
static const uint32_t NumFloatArgRegs = NumIntArgRegs;

static inline bool
GetIntArgReg(uint32_t usedArgSlots, Register* out)
{
    if (usedArgSlots < NumIntArgRegs) {
        *out = Register::FromCode(a0.code() + usedArgSlots);
        return true;
    }
    return false;
}

static inline bool
GetFloatArgReg(uint32_t usedArgSlots, FloatRegister* out)
{
    if (usedArgSlots < NumFloatArgRegs) {
        *out = FloatRegister::FromCode(f12.code() + usedArgSlots);
        return true;
    }
    return false;
}

// Get a register in which we plan to put a quantity that will be used as an
// integer argument. This differs from GetIntArgReg in that if we have no more
// actual argument registers to use we will fall back on using whatever
// CallTempReg* don't overlap the argument registers, and only fail once those
// run out too.
static inline bool
GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out)
{
    // NOTE: We can't properly determine which regs are used if there are
    // float arguments. If this is needed, we will have to guess.
    MOZ_ASSERT(usedFloatArgs == 0);

    if (GetIntArgReg(usedIntArgs, out))
        return true;
    // Unfortunately, we have to assume things about the point at which
    // GetIntArgReg returns false, because we need to know how many registers it
    // can allocate.
    usedIntArgs -= NumIntArgRegs;
    if (usedIntArgs >= NumCallTempNonArgRegs)
        return false;
    *out = CallTempNonArgRegs[usedIntArgs];
    return true;
}

static inline uint32_t
GetArgStackDisp(uint32_t usedArgSlots)
{
    MOZ_ASSERT(usedArgSlots >= NumIntArgRegs);
    return (usedArgSlots - NumIntArgRegs) * sizeof(int64_t);
}

} // namespace jit
} // namespace js

#endif /* jit_mips64_Assembler_mips64_h */