2 ** MIPS instruction emitter.
3 ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
7 static intptr_t get_k64val(ASMState
*as
, IRRef ref
)
10 if (ir
->o
== IR_KINT64
) {
11 return (intptr_t)ir_kint64(ir
)->u64
;
12 } else if (ir
->o
== IR_KGC
) {
13 return (intptr_t)ir_kgc(ir
);
14 } else if (ir
->o
== IR_KPTR
|| ir
->o
== IR_KKPTR
) {
15 return (intptr_t)ir_kptr(ir
);
16 } else if (LJ_SOFTFP
&& ir
->o
== IR_KNUM
) {
17 return (intptr_t)ir_knum(ir
)->u64
;
19 lj_assertA(ir
->o
== IR_KINT
|| ir
->o
== IR_KNULL
,
20 "bad 64 bit const IR op %d", ir
->o
);
21 return ir
->i
; /* Sign-extended. */
27 #define get_kval(as, ref) get_k64val(as, ref)
29 #define get_kval(as, ref) (IR((ref))->i)
32 /* -- Emit basic instructions --------------------------------------------- */
34 static void emit_dst(ASMState
*as
, MIPSIns mi
, Reg rd
, Reg rs
, Reg rt
)
36 *--as
->mcp
= mi
| MIPSF_D(rd
) | MIPSF_S(rs
) | MIPSF_T(rt
);
39 static void emit_dta(ASMState
*as
, MIPSIns mi
, Reg rd
, Reg rt
, uint32_t a
)
41 *--as
->mcp
= mi
| MIPSF_D(rd
) | MIPSF_T(rt
) | MIPSF_A(a
);
44 #define emit_ds(as, mi, rd, rs) emit_dst(as, (mi), (rd), (rs), 0)
45 #define emit_tg(as, mi, rt, rg) emit_dst(as, (mi), (rg)&31, 0, (rt))
47 static void emit_tsi(ASMState
*as
, MIPSIns mi
, Reg rt
, Reg rs
, int32_t i
)
49 *--as
->mcp
= mi
| MIPSF_T(rt
) | MIPSF_S(rs
) | (i
& 0xffff);
52 #define emit_ti(as, mi, rt, i) emit_tsi(as, (mi), (rt), 0, (i))
53 #define emit_hsi(as, mi, rh, rs, i) emit_tsi(as, (mi), (rh) & 31, (rs), (i))
55 static void emit_fgh(ASMState
*as
, MIPSIns mi
, Reg rf
, Reg rg
, Reg rh
)
57 *--as
->mcp
= mi
| MIPSF_F(rf
&31) | MIPSF_G(rg
&31) | MIPSF_H(rh
&31);
60 #define emit_fg(as, mi, rf, rg) emit_fgh(as, (mi), (rf), (rg), 0)
62 static void emit_rotr(ASMState
*as
, Reg dest
, Reg src
, Reg tmp
, uint32_t shift
)
64 if (LJ_64
|| (as
->flags
& JIT_F_MIPSXXR2
)) {
65 emit_dta(as
, MIPSI_ROTR
, dest
, src
, shift
);
67 emit_dst(as
, MIPSI_OR
, dest
, dest
, tmp
);
68 emit_dta(as
, MIPSI_SLL
, dest
, src
, (-shift
)&31);
69 emit_dta(as
, MIPSI_SRL
, tmp
, src
, shift
);
73 #if LJ_64 || LJ_HASBUFFER
74 static void emit_tsml(ASMState
*as
, MIPSIns mi
, Reg rt
, Reg rs
, uint32_t msb
,
77 *--as
->mcp
= mi
| MIPSF_T(rt
) | MIPSF_S(rs
) | MIPSF_M(msb
) | MIPSF_L(lsb
);
81 /* -- Emit loads/stores --------------------------------------------------- */
83 /* Prefer rematerialization of BASE/L from global_State over spills. */
84 #define emit_canremat(ref) ((ref) <= REF_BASE)
86 /* Try to find a one step delta relative to another constant. */
87 static int emit_kdelta1(ASMState
*as
, Reg rd
, intptr_t i
)
89 RegSet work
= ~as
->freeset
& RSET_GPR
;
91 Reg r
= rset_picktop(work
);
92 IRRef ref
= regcost_ref(as
->cost
[r
]);
93 lj_assertA(r
!= rd
, "dest reg %d not free", rd
);
95 intptr_t delta
= (intptr_t)((uintptr_t)i
-
96 (uintptr_t)(ra_iskref(ref
) ? ra_krefk(as
, ref
) : get_kval(as
, ref
)));
97 if (checki16(delta
)) {
98 emit_tsi(as
, MIPSI_AADDIU
, rd
, r
, delta
);
104 return 0; /* Failed. */
107 /* Load a 32 bit constant into a GPR. */
108 static void emit_loadi(ASMState
*as
, Reg r
, int32_t i
)
111 emit_ti(as
, MIPSI_LI
, r
, i
);
114 intptr_t jgl
= (intptr_t)(void *)J2G(as
->J
);
115 if ((uintptr_t)(i
-jgl
) < 65536) {
116 emit_tsi(as
, MIPSI_ADDIU
, r
, RID_JGL
, i
-jgl
-32768);
118 } else if (emit_kdelta1(as
, r
, i
)) {
120 } else if ((i
>> 16) == 0) {
121 emit_tsi(as
, MIPSI_ORI
, r
, RID_ZERO
, i
);
124 emit_tsi(as
, MIPSI_ORI
, r
, r
, i
);
126 emit_ti(as
, MIPSI_LUI
, r
, (i
>> 16));
131 /* Load a 64 bit constant into a GPR. */
132 static void emit_loadu64(ASMState
*as
, Reg r
, uint64_t u64
)
134 if (checki32((int64_t)u64
)) {
135 emit_loadi(as
, r
, (int32_t)u64
);
137 uint64_t delta
= u64
- (uint64_t)(void *)J2G(as
->J
);
139 emit_tsi(as
, MIPSI_DADDIU
, r
, RID_JGL
, (int32_t)(delta
-32768));
140 } else if (emit_kdelta1(as
, r
, (intptr_t)u64
)) {
143 /* TODO MIPSR6: Use DAHI & DATI. Caveat: sign-extension. */
144 if ((u64
& 0xffff)) {
145 emit_tsi(as
, MIPSI_ORI
, r
, r
, u64
& 0xffff);
147 if (((u64
>> 16) & 0xffff)) {
148 emit_dta(as
, MIPSI_DSLL
, r
, r
, 16);
149 emit_tsi(as
, MIPSI_ORI
, r
, r
, (u64
>> 16) & 0xffff);
150 emit_dta(as
, MIPSI_DSLL
, r
, r
, 16);
152 emit_dta(as
, MIPSI_DSLL32
, r
, r
, 0);
154 emit_loadi(as
, r
, (int32_t)(u64
>> 32));
156 /* TODO: There are probably more optimization opportunities. */
160 #define emit_loada(as, r, addr) emit_loadu64(as, (r), u64ptr((addr)))
162 #define emit_loada(as, r, addr) emit_loadi(as, (r), i32ptr((addr)))
165 static Reg
ra_allock(ASMState
*as
, intptr_t k
, RegSet allow
);
166 static void ra_allockreg(ASMState
*as
, intptr_t k
, Reg r
);
168 /* Get/set from constant pointer. */
169 static void emit_lsptr(ASMState
*as
, MIPSIns mi
, Reg r
, void *p
, RegSet allow
)
171 intptr_t jgl
= (intptr_t)(J2G(as
->J
));
172 intptr_t i
= (intptr_t)(p
);
174 if ((uint32_t)(i
-jgl
) < 65536) {
178 base
= ra_allock(as
, i
-(int16_t)i
, allow
);
180 emit_tsi(as
, mi
, r
, base
, i
);
184 static void emit_loadk64(ASMState
*as
, Reg r
, IRIns
*ir
)
186 const uint64_t *k
= &ir_k64(ir
)->u64
;
188 if (rset_test(RSET_FPR
, r
)) {
190 emit_tg(as
, MIPSI_DMTC1
, r64
, r
);
192 if ((uint32_t)((intptr_t)k
-(intptr_t)J2G(as
->J
)) < 65536)
193 emit_lsptr(as
, MIPSI_LD
, r64
, (void *)k
, 0);
195 emit_loadu64(as
, r64
, *k
);
198 #define emit_loadk64(as, r, ir) \
199 emit_lsptr(as, MIPSI_LDC1, ((r) & 31), (void *)&ir_knum((ir))->u64, RSET_GPR)
202 /* Get/set global_State fields. */
203 static void emit_lsglptr(ASMState
*as
, MIPSIns mi
, Reg r
, int32_t ofs
)
205 emit_tsi(as
, mi
, r
, RID_JGL
, ofs
-32768);
208 #define emit_getgl(as, r, field) \
209 emit_lsglptr(as, MIPSI_AL, (r), (int32_t)offsetof(global_State, field))
210 #define emit_setgl(as, r, field) \
211 emit_lsglptr(as, MIPSI_AS, (r), (int32_t)offsetof(global_State, field))
213 /* Trace number is determined from per-trace exit stubs. */
214 #define emit_setvmstate(as, i) UNUSED(i)
216 /* -- Emit control-flow instructions -------------------------------------- */
218 /* Label for internal jumps. */
219 typedef MCode
*MCLabel
;
221 /* Return label pointing to current PC. */
222 #define emit_label(as) ((as)->mcp)
224 static void emit_branch(ASMState
*as
, MIPSIns mi
, Reg rs
, Reg rt
, MCode
*target
)
227 ptrdiff_t delta
= target
- p
;
228 lj_assertA(((delta
+ 0x8000) >> 16) == 0, "branch target out of range");
229 *--p
= mi
| MIPSF_S(rs
) | MIPSF_T(rt
) | ((uint32_t)delta
& 0xffffu
);
233 static void emit_jmp(ASMState
*as
, MCode
*target
)
235 *--as
->mcp
= MIPSI_NOP
;
236 emit_branch(as
, MIPSI_B
, RID_ZERO
, RID_ZERO
, (target
));
239 static void emit_call(ASMState
*as
, void *target
, int needcfa
)
243 ptrdiff_t delta
= (char *)target
- (char *)p
;
244 if ((((delta
>>2) + 0x02000000) >> 26) == 0) { /* Try compact call first. */
245 *--p
= MIPSI_BALC
| (((uintptr_t)delta
>>2) & 0x03ffffffu
);
250 *--p
= MIPSI_NOP
; /* Delay slot. */
251 if ((((uintptr_t)target
^ (uintptr_t)p
) >> 28) == 0) {
252 #if !LJ_TARGET_MIPSR6
253 *--p
= (((uintptr_t)target
& 1) ? MIPSI_JALX
: MIPSI_JAL
) |
254 (((uintptr_t)target
>>2) & 0x03ffffffu
);
256 *--p
= MIPSI_JAL
| (((uintptr_t)target
>>2) & 0x03ffffffu
);
258 } else { /* Target out of range: need indirect call. */
259 *--p
= MIPSI_JALR
| MIPSF_S(RID_CFUNCADDR
);
263 if (needcfa
) ra_allockreg(as
, (intptr_t)target
, RID_CFUNCADDR
);
266 /* -- Emit generic operations --------------------------------------------- */
268 #define emit_move(as, dst, src) \
269 emit_ds(as, MIPSI_MOVE, (dst), (src))
271 /* Generic move between two regs. */
272 static void emit_movrr(ASMState
*as
, IRIns
*ir
, Reg dst
, Reg src
)
274 if (dst
< RID_MAX_GPR
)
275 emit_move(as
, dst
, src
);
277 emit_fg(as
, irt_isnum(ir
->t
) ? MIPSI_MOV_D
: MIPSI_MOV_S
, dst
, src
);
280 /* Generic load of register with base and (small) offset address. */
281 static void emit_loadofs(ASMState
*as
, IRIns
*ir
, Reg r
, Reg base
, int32_t ofs
)
284 emit_tsi(as
, irt_is64(ir
->t
) ? MIPSI_LD
: MIPSI_LW
, r
, base
, ofs
);
286 emit_tsi(as
, irt_isnum(ir
->t
) ? MIPSI_LDC1
: MIPSI_LWC1
,
287 (r
& 31), base
, ofs
);
290 /* Generic store of register with base and (small) offset address. */
291 static void emit_storeofs(ASMState
*as
, IRIns
*ir
, Reg r
, Reg base
, int32_t ofs
)
294 emit_tsi(as
, irt_is64(ir
->t
) ? MIPSI_SD
: MIPSI_SW
, r
, base
, ofs
);
296 emit_tsi(as
, irt_isnum(ir
->t
) ? MIPSI_SDC1
: MIPSI_SWC1
,
300 /* Add offset to pointer. */
301 static void emit_addptr(ASMState
*as
, Reg r
, int32_t ofs
)
304 lj_assertA(checki16(ofs
), "offset %d out of range", ofs
);
305 emit_tsi(as
, MIPSI_AADDIU
, r
, r
, ofs
);
309 #define emit_spsub(as, ofs) emit_addptr(as, RID_SP, -(ofs))