v / vlib / v2 / gen / arm64 / asm.v
635 lines · 495 sloc · 16.76 KB · 150e87c0d6179a1a72b51f3256549c41e10bfcd9
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module arm64
6
7// ARM64 Instruction Encoding Helpers
8// These functions provide type-safe instruction encoding for the ARM64 backend.
9
10// Register type for type safety
11type Reg = int
12
13// Common register constants
14const xzr = Reg(31) // Zero register
15
16const sp = Reg(31) // Stack pointer (context-dependent)
17
18const fp = Reg(29) // Frame pointer
19
20const lr = Reg(30) // Link register
21
22// === Prologue/Epilogue ===
23
24// stp fp, lr, [sp, -16]! (pre-indexed)
25fn asm_stp_fp_lr_pre() u32 {
26 return 0xA9BF7BFD
27}
28
29// mov fp, sp
30fn asm_mov_fp_sp() u32 {
31 return 0x910003FD
32}
33
34// ldp fp, lr, [sp], 16 (post-indexed)
35fn asm_ldp_fp_lr_post() u32 {
36 return 0xA8C17BFD
37}
38
39// ret
40fn asm_ret() u32 {
41 return 0xD65F03C0
42}
43
44// === Store/Load Pair ===
45
46// stp r1, r2, [sp, -16]! (callee-saved pair)
47fn asm_stp_pair_pre(r1 Reg, r2 Reg) u32 {
48 return 0xA9BF0000 | (u32(r2) << 10) | (31 << 5) | u32(r1)
49}
50
51// ldp r1, r2, [sp], 16 (restore callee-saved pair)
52fn asm_ldp_pair_post(r1 Reg, r2 Reg) u32 {
53 return 0xA8C10000 | (u32(r2) << 10) | (31 << 5) | u32(r1)
54}
55
56// === Arithmetic ===
57
58// add rd, rn, #imm12
59fn asm_add_imm(rd Reg, rn Reg, imm u32) u32 {
60 return 0x91000000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
61}
62
63// add rd, rn, #imm, lsl #12
64fn asm_add_imm_lsl12(rd Reg, rn Reg, imm u32) u32 {
65 return 0x91400000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
66}
67
68// add rd, rn, rm
69fn asm_add_reg(rd Reg, rn Reg, rm Reg) u32 {
70 return 0x8B000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
71}
72
73// add rd, rn, rm, lsl #3 (for GEP: index * 8)
74fn asm_add_reg_lsl3(rd Reg, rn Reg, rm Reg) u32 {
75 return 0x8B000C00 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
76}
77
78// sub rd, rn, #imm12
79fn asm_sub_imm(rd Reg, rn Reg, imm u32) u32 {
80 return 0xD1000000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
81}
82
83// sub rd, rn, #imm12, lsl #12
84fn asm_sub_imm_lsl12(rd Reg, rn Reg, imm u32) u32 {
85 return 0xD1400000 | (imm << 10) | (u32(rn) << 5) | u32(rd)
86}
87
88// sub rd, rn, rm
89fn asm_sub_reg(rd Reg, rn Reg, rm Reg) u32 {
90 return 0xCB000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
91}
92
93// madd rd, rn, rm, xzr (multiply: rd = rn * rm)
94fn asm_mul(rd Reg, rn Reg, rm Reg) u32 {
95 return 0x9B007C00 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
96}
97
98// sdiv rd, rn, rm
99fn asm_sdiv(rd Reg, rn Reg, rm Reg) u32 {
100 return 0x9AC00C00 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
101}
102
103// udiv rd, rn, rm
104fn asm_udiv(rd Reg, rn Reg, rm Reg) u32 {
105 return 0x9AC00800 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
106}
107
108// msub rd, rn, rm, ra (rd = ra - rn * rm)
109fn asm_msub(rd Reg, rn Reg, rm Reg, ra Reg) u32 {
110 return 0x9B008000 | (u32(rm) << 16) | (u32(ra) << 10) | (u32(rn) << 5) | u32(rd)
111}
112
113// === Logical ===
114
115// and rd, rn, rm
116fn asm_and(rd Reg, rn Reg, rm Reg) u32 {
117 return 0x8A000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
118}
119
120// and rd, rn, #1 — truncate to 1 bit (AND Xd, Xn, #1: N=1, immr=0, imms=0x00)
121fn asm_and_imm_1(rd Reg, rn Reg) u32 {
122 return 0x92400000 | (u32(rn) << 5) | u32(rd)
123}
124
125// and rd, rn, #0xFF — zero-extend u8 (AND Xd, Xn, #0xFF: N=1, immr=0, imms=0x07)
126fn asm_and_imm_0xff(rd Reg, rn Reg) u32 {
127 return 0x92401C00 | (u32(rn) << 5) | u32(rd)
128}
129
130// and rd, rn, #0xFFFF — zero-extend u16 (AND Xd, Xn, #0xFFFF: N=1, immr=0, imms=0x0F)
131fn asm_and_imm_0xffff(rd Reg, rn Reg) u32 {
132 return 0x92403C00 | (u32(rn) << 5) | u32(rd)
133}
134
135// and rd, rn, #0xFFFFFFFF — zero-extend u32 (AND Xd, Xn, #0xFFFFFFFF: N=1, immr=0, imms=0x1F)
136fn asm_and_imm_0xffffffff(rd Reg, rn Reg) u32 {
137 return 0x92407C00 | (u32(rn) << 5) | u32(rd)
138}
139
140// orr rd, rn, rm
141fn asm_orr(rd Reg, rn Reg, rm Reg) u32 {
142 return 0xAA000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
143}
144
145// eor rd, rn, rm (xor)
146fn asm_eor(rd Reg, rn Reg, rm Reg) u32 {
147 return 0xCA000000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
148}
149
150// orr rd, xzr, rm (mov rd, rm)
151fn asm_mov_reg(rd Reg, rm Reg) u32 {
152 return 0xAA0003E0 | (u32(rm) << 16) | u32(rd)
153}
154
155// === Shifts ===
156
157// lslv rd, rn, rm (logical shift left variable)
158fn asm_lslv(rd Reg, rn Reg, rm Reg) u32 {
159 return 0x9AC02000 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
160}
161
162// asrv rd, rn, rm (arithmetic shift right variable)
163fn asm_asrv(rd Reg, rn Reg, rm Reg) u32 {
164 return 0x9AC02800 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
165}
166
167// lsrv rd, rn, rm (logical shift right variable)
168fn asm_lsrv(rd Reg, rn Reg, rm Reg) u32 {
169 return 0x9AC02400 | (u32(rm) << 16) | (u32(rn) << 5) | u32(rd)
170}
171
172// lsr xd, xn, #shift (logical shift right immediate, 64-bit)
173// Alias for UBFM Xd, Xn, #shift, #63
174fn asm_lsr_imm(rd Reg, rn Reg, shift u32) u32 {
175 return 0xD340FC00 | ((shift & 0x3F) << 16) | (u32(rn) << 5) | u32(rd)
176}
177
178// ubfx xd, xn, #0, #width (unsigned bitfield extract, zero-extend lower bits)
179// Alias for UBFM Xd, Xn, #0, #(width-1)
180fn asm_ubfx_lower(rd Reg, rn Reg, width u32) u32 {
181 imms := (width - 1) & 0x3F
182 return 0xD3400000 | (imms << 10) | (u32(rn) << 5) | u32(rd)
183}
184
185// sxtw rd, rn — sign-extend 32-bit value to 64-bit (SBFM Xd, Xn, #0, #31)
186fn asm_sxtw(rd Reg, rn Reg) u32 {
187 return 0x93407C00 | (u32(rn) << 5) | u32(rd)
188}
189
190// sxth rd, rn — sign-extend 16-bit value to 64-bit (SBFM Xd, Xn, #0, #15)
191fn asm_sxth(rd Reg, rn Reg) u32 {
192 return 0x93403C00 | (u32(rn) << 5) | u32(rd)
193}
194
195// sxtb rd, rn — sign-extend 8-bit value to 64-bit (SBFM Xd, Xn, #0, #7)
196fn asm_sxtb(rd Reg, rn Reg) u32 {
197 return 0x93401C00 | (u32(rn) << 5) | u32(rd)
198}
199
200// === Compare ===
201
202// cmp rn, rm (subs xzr, rn, rm) — 64-bit
203fn asm_cmp_reg(rn Reg, rm Reg) u32 {
204 return 0xEB00001F | (u32(rm) << 16) | (u32(rn) << 5)
205}
206
207// cmp xn, #imm12 (subs xzr, xn, #imm12) — 64-bit immediate compare
208fn asm_cmp_imm(rn Reg, imm12 u32) u32 {
209 return 0xF100001F | (imm12 << 10) | (u32(rn) << 5)
210}
211
212// cmp wn, #imm12 (subs wzr, wn, #imm12) — 32-bit immediate compare
213fn asm_cmp_imm_w(rn Reg, imm12 u32) u32 {
214 return 0x7100001F | (imm12 << 10) | (u32(rn) << 5)
215}
216
217// cmp wn, wm (subs wzr, wn, wm) — 32-bit, sign-aware for i32
218fn asm_cmp_reg_w(rn Reg, rm Reg) u32 {
219 return 0x6B00001F | (u32(rm) << 16) | (u32(rn) << 5)
220}
221
222// === Conditional Set ===
223
224// cset rd, eq
225fn asm_cset_eq(rd Reg) u32 {
226 return 0x9A9F17E0 | u32(rd)
227}
228
229// cset rd, ne
230fn asm_cset_ne(rd Reg) u32 {
231 return 0x9A9F07E0 | u32(rd)
232}
233
234// cset rd, lt
235fn asm_cset_lt(rd Reg) u32 {
236 return 0x9A9FA7E0 | u32(rd)
237}
238
239// cset rd, gt
240fn asm_cset_gt(rd Reg) u32 {
241 return 0x9A9FD7E0 | u32(rd)
242}
243
244// cset rd, le
245fn asm_cset_le(rd Reg) u32 {
246 return 0x9A9FC7E0 | u32(rd)
247}
248
249// cset rd, ge
250fn asm_cset_ge(rd Reg) u32 {
251 return 0x9A9FB7E0 | u32(rd)
252}
253
254// cset rd, hi (unsigned greater than)
255fn asm_cset_hi(rd Reg) u32 {
256 return 0x9A9F97E0 | u32(rd)
257}
258
259// cset rd, hs (unsigned greater or equal)
260fn asm_cset_hs(rd Reg) u32 {
261 return 0x9A9F37E0 | u32(rd)
262}
263
264// cset rd, lo (unsigned less than)
265fn asm_cset_lo(rd Reg) u32 {
266 return 0x9A9F27E0 | u32(rd)
267}
268
269// cset rd, ls (unsigned less or equal)
270fn asm_cset_ls(rd Reg) u32 {
271 return 0x9A9F87E0 | u32(rd)
272}
273
274// === Float Compare ===
275
276// fcmp dn, dm (compare two double-precision floats, sets NZCV)
277fn asm_fcmp_d(dn Reg, dm Reg) u32 {
278 return 0x1E602000 | (u32(dm) << 16) | (u32(dn) << 5)
279}
280
281// === Memory ===
282
283// str rt, [rn] (store 64-bit)
284fn asm_str(rt Reg, rn Reg) u32 {
285 return 0xF9000000 | (u32(rn) << 5) | u32(rt)
286}
287
288// str wt, [rn] (store 32-bit)
289fn asm_str_w(rt Reg, rn Reg) u32 {
290 return 0xB9000000 | (u32(rn) << 5) | u32(rt)
291}
292
293// strh wt, [rn] (store 16-bit)
294fn asm_str_h(rt Reg, rn Reg) u32 {
295 return 0x79000000 | (u32(rn) << 5) | u32(rt)
296}
297
298// strb wt, [rn] (store 8-bit)
299fn asm_str_b(rt Reg, rn Reg) u32 {
300 return 0x39000000 | (u32(rn) << 5) | u32(rt)
301}
302
303// str rt, [rn, #imm12] (scaled by 8)
304// imm12 is the byte offset divided by 8; range 0..32760 step 8
305fn asm_str_imm(rt Reg, rn Reg, imm12 u32) u32 {
306 return 0xF9000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
307}
308
309// str wt, [rn, #imm12] (32-bit store, unsigned offset scaled by 4)
310// imm12 is the byte offset divided by 4; range 0..16380 step 4
311fn asm_str_imm_w(rt Reg, rn Reg, imm12 u32) u32 {
312 return 0xB9000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
313}
314
315// strh wt, [rn, #imm12] (16-bit store, unsigned offset scaled by 2)
316// imm12 is the byte offset divided by 2; range 0..8190 step 2
317fn asm_str_imm_h(rt Reg, rn Reg, imm12 u32) u32 {
318 return 0x79000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
319}
320
321// strb wt, [rn, #imm12] (8-bit store, unsigned offset)
322// imm12 is the byte offset; range 0..4095
323fn asm_str_imm_b(rt Reg, rn Reg, imm12 u32) u32 {
324 return 0x39000000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
325}
326
327// stp rt, rt2, [rn, #simm7] (store pair, signed offset scaled by 8)
328// simm7 is byte offset / 8; range -512..504 step 8
329fn asm_stp_offset(rt Reg, rt2 Reg, rn Reg, simm7 i32) u32 {
330 return 0xA9000000 | (u32(simm7 & 0x7F) << 15) | (u32(rt2) << 10) | (u32(rn) << 5) | u32(rt)
331}
332
333// ldp rt, rt2, [rn, #simm7] (load pair, signed offset scaled by 8)
334// simm7 is byte offset / 8; range -512..504 step 8
335fn asm_ldp_offset(rt Reg, rt2 Reg, rn Reg, simm7 i32) u32 {
336 return 0xA9400000 | (u32(simm7 & 0x7F) << 15) | (u32(rt2) << 10) | (u32(rn) << 5) | u32(rt)
337}
338
339// stur xt, [xn, #simm9] (unscaled 64-bit store)
340fn asm_stur(rt Reg, rn Reg, simm9 i32) u32 {
341 return 0xF8000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
342}
343
344// stur wt, [xn, #simm9] (unscaled 32-bit store)
345fn asm_stur_w(rt Reg, rn Reg, simm9 i32) u32 {
346 return 0xB8000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
347}
348
349// sturh wt, [xn, #simm9] (unscaled 16-bit store)
350fn asm_stur_h(rt Reg, rn Reg, simm9 i32) u32 {
351 return 0x78000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
352}
353
354// sturb wt, [xn, #simm9] (unscaled 8-bit store)
355fn asm_stur_b(rt Reg, rn Reg, simm9 i32) u32 {
356 return 0x38000000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
357}
358
359// ldr rt, [rn] (load 64-bit)
360fn asm_ldr(rt Reg, rn Reg) u32 {
361 return 0xF9400000 | (u32(rn) << 5) | u32(rt)
362}
363
364// ldr wt, [rn] (load 32-bit, zero-extend to x)
365fn asm_ldr_w(rt Reg, rn Reg) u32 {
366 return 0xB9400000 | (u32(rn) << 5) | u32(rt)
367}
368
369// ldrsw xt, [rn] (load 32-bit, sign-extend to 64-bit x)
370// Used for signed i32 loads to preserve negative values in 64-bit registers.
371fn asm_ldrsw(rt Reg, rn Reg) u32 {
372 return 0xB9800000 | (u32(rn) << 5) | u32(rt)
373}
374
375// ldrh wt, [rn] (load 16-bit, zero-extend to x)
376fn asm_ldr_h(rt Reg, rn Reg) u32 {
377 return 0x79400000 | (u32(rn) << 5) | u32(rt)
378}
379
380// ldrb wt, [rn] (load 8-bit, zero-extend to x)
381fn asm_ldr_b(rt Reg, rn Reg) u32 {
382 return 0x39400000 | (u32(rn) << 5) | u32(rt)
383}
384
385// ldr rt, [rn, #imm12] (load 64-bit with unsigned scaled offset)
386// imm12 is the byte offset divided by 8; range 0..32760 step 8
387fn asm_ldr_imm(rt Reg, rn Reg, imm12 u32) u32 {
388 return 0xF9400000 | ((imm12 & 0xFFF) << 10) | (u32(rn) << 5) | u32(rt)
389}
390
391// ldr wt, [rn, #imm12] (32-bit load, unsigned offset scaled by 4)
392// imm12 is the byte offset divided by 4; range 0..16380 step 4
393fn asm_ldr_imm_w(rt Reg, rn Reg, imm12 u32) u32 {
394 return 0xB9400000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
395}
396
397// ldrh wt, [rn, #imm12] (16-bit load, unsigned offset scaled by 2)
398// imm12 is the byte offset divided by 2; range 0..8190 step 2
399fn asm_ldr_imm_h(rt Reg, rn Reg, imm12 u32) u32 {
400 return 0x79400000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
401}
402
403// ldrb wt, [rn, #imm12] (8-bit load, unsigned offset)
404// imm12 is the byte offset; range 0..4095
405fn asm_ldr_imm_b(rt Reg, rn Reg, imm12 u32) u32 {
406 return 0x39400000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
407}
408
409// ldrsw xt, [rn, #imm12] (32-bit sign-extending load, unsigned offset scaled by 4)
410fn asm_ldrsw_imm(rt Reg, rn Reg, imm12 u32) u32 {
411 return 0xB9800000 | (imm12 << 10) | (u32(rn) << 5) | u32(rt)
412}
413
414// ldur xt, [xn, #simm9] (unscaled 64-bit load)
415fn asm_ldur(rt Reg, rn Reg, simm9 i32) u32 {
416 return 0xF8400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
417}
418
419// ldur wt, [xn, #simm9] (unscaled 32-bit load, zero-extend)
420fn asm_ldur_w(rt Reg, rn Reg, simm9 i32) u32 {
421 return 0xB8400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
422}
423
424// ldurh wt, [xn, #simm9] (unscaled 16-bit load, zero-extend)
425fn asm_ldur_h(rt Reg, rn Reg, simm9 i32) u32 {
426 return 0x78400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
427}
428
429// ldurb wt, [xn, #simm9] (unscaled 8-bit load, zero-extend)
430fn asm_ldur_b(rt Reg, rn Reg, simm9 i32) u32 {
431 return 0x38400000 | (u32(simm9 & 0x1FF) << 12) | (u32(rn) << 5) | u32(rt)
432}
433
434// === Branches ===
435
436// b #imm26 (unconditional branch)
437fn asm_b(rel26 i32) u32 {
438 return 0x14000000 | (u32(rel26) & 0x3FFFFFF)
439}
440
441// bl #imm26 (branch with link / call)
442fn asm_bl(rel26 i32) u32 {
443 return 0x94000000 | (u32(rel26) & 0x3FFFFFF)
444}
445
446// bl (placeholder for relocation)
447fn asm_bl_reloc() u32 {
448 return 0x94000000
449}
450
451// blr rn (branch with link to register)
452fn asm_blr(rn Reg) u32 {
453 return 0xD63F0000 | (u32(rn) << 5)
454}
455
456// cbnz rt, #imm19
457fn asm_cbnz(rt Reg, rel19 i32) u32 {
458 return 0xB5000000 | ((u32(rel19) & 0x7FFFF) << 5) | u32(rt)
459}
460
461// cbz rt, #imm19
462fn asm_cbz(rt Reg, rel19 i32) u32 {
463 return 0xB4000000 | ((u32(rel19) & 0x7FFFF) << 5) | u32(rt)
464}
465
466// b.cond #imm19 (conditional branch)
467fn asm_b_cond(cond u32, rel19 i32) u32 {
468 return 0x54000000 | ((u32(rel19) & 0x7FFFF) << 5) | cond
469}
470
471// Condition codes for b.cond
472const cond_eq = u32(0x0) // equal
473
474const cond_ne = u32(0x1) // not equal
475
476const cond_lt = u32(0xB) // less than (signed)
477
478const cond_gt = u32(0xC) // greater than (signed)
479
480const cond_le = u32(0xD) // less or equal (signed)
481
482const cond_ge = u32(0xA) // greater or equal (signed)
483
484// === Move Immediate ===
485
486// movz rd, #imm16 (move wide with zero)
487fn asm_movz(rd Reg, imm16 u32) u32 {
488 return 0xD2800000 | (imm16 << 5) | u32(rd)
489}
490
491// movk rd, #imm16, lsl #shift (move wide with keep)
492fn asm_movk(rd Reg, imm16 u32, shift int) u32 {
493 hw := u32(shift / 16)
494 return 0xF2800000 | (hw << 21) | (imm16 << 5) | u32(rd)
495}
496
497// movn rd, #imm16 (move wide with NOT)
498fn asm_movn(rd Reg, imm16 u32) u32 {
499 return 0x92800000 | (imm16 << 5) | u32(rd)
500}
501
502// === PC-Relative Addressing ===
503
504// adrp rd, #imm21 (load page address - placeholder for reloc)
505fn asm_adrp(rd Reg) u32 {
506 return 0x90000000 | u32(rd)
507}
508
509// add rd, rd, #imm12 (add page offset - typically follows adrp)
510fn asm_add_pageoff(rd Reg) u32 {
511 return 0x91000000 | u32(rd) | (u32(rd) << 5)
512}
513
514// ldr rd, [rd, #imm12] (load from page offset - for GOT access, typically follows adrp)
515fn asm_ldr_pageoff(rd Reg) u32 {
516 return 0xF9400000 | u32(rd) | (u32(rd) << 5)
517}
518
519// === Stack Operations ===
520
521// sub sp, sp, x10
522fn asm_sub_sp_reg(rm Reg) u32 {
523 return 0xCB000000 | (u32(rm) << 16) | (31 << 5) | 31
524}
525
526// add sp, sp, x10
527fn asm_add_sp_reg(rm Reg) u32 {
528 return 0x8B000000 | (u32(rm) << 16) | (31 << 5) | 31
529}
530
531// sub x10, x29, x10
532fn asm_sub_fp_to_reg(rd Reg, rm Reg) u32 {
533 return 0xCB0003A0 | (u32(rm) << 16) | u32(rd)
534}
535
536// === Float Operations ===
537
538// fadd dd, dn, dm
539fn asm_fadd_d(dd int, dn int, dm int) u32 {
540 return 0x1E602800 | (u32(dm) << 16) | (u32(dn) << 5) | u32(dd)
541}
542
543// fadd d0, d0, d1 (common case)
544fn asm_fadd_d0_d0_d1() u32 {
545 return 0x1E612800
546}
547
548// fsub d0, d0, d1
549fn asm_fsub_d0_d0_d1() u32 {
550 return 0x1E613800
551}
552
553// fmul d0, d0, d1
554fn asm_fmul_d0_d0_d1() u32 {
555 return 0x1E610800
556}
557
558// fdiv d0, d0, d1
559fn asm_fdiv_d0_d0_d1() u32 {
560 return 0x1E611800
561}
562
563// fdiv d2, d0, d1
564fn asm_fdiv_d2_d0_d1() u32 {
565 return 0x1E611802
566}
567
568// frintz d2, d2 (truncate to integer)
569fn asm_frintz_d2() u32 {
570 return 0x1E65C042
571}
572
573// fnmsub d0, d2, d1, d0 (d0 = d0 - d2*d1)
574fn asm_fnmsub_d0_d2_d1_d0() u32 {
575 return 0x1F618000
576}
577
578// === Float Conversions ===
579
580// fmov xd, dn (copy float bits to integer)
581fn asm_fmov_x_d(xd Reg, dn int) u32 {
582 return 0x9E660000 | (u32(dn) << 5) | u32(xd)
583}
584
585// fmov dd, xn (copy integer bits to float)
586fn asm_fmov_d_x(dd int, xn Reg) u32 {
587 return 0x9E670000 | (u32(xn) << 5) | u32(dd)
588}
589
590// fcvtzs xd, dn (float to signed int, truncate toward zero)
591fn asm_fcvtzs_x_d(xd Reg, dn int) u32 {
592 return 0x9E780000 | (u32(dn) << 5) | u32(xd)
593}
594
595// scvtf dd, xn (signed int to float)
596fn asm_scvtf_d_x(dd int, xn Reg) u32 {
597 return 0x9E620000 | (u32(xn) << 5) | u32(dd)
598}
599
600// ucvtf dd, xn (unsigned int to float)
601fn asm_ucvtf_d_x(dd int, xn Reg) u32 {
602 return 0x9E630000 | (u32(xn) << 5) | u32(dd)
603}
604
605// fcvtzu xd, dn (float to unsigned int, truncate toward zero)
606fn asm_fcvtzu_x_d(xd Reg, dn int) u32 {
607 return 0x9E790000 | (u32(dn) << 5) | u32(xd)
608}
609
610// fmov sd, wn (copy 32-bit integer to single-precision float register)
611fn asm_fmov_s_w(sd int, wn Reg) u32 {
612 return 0x1E270000 | (u32(wn) << 5) | u32(sd)
613}
614
615// fmov wd, sn (copy single-precision float to 32-bit integer register)
616fn asm_fmov_w_s(wd Reg, sn int) u32 {
617 return 0x1E260000 | (u32(sn) << 5) | u32(wd)
618}
619
620// fcvt dd, sn (convert single-precision to double-precision float)
621fn asm_fcvt_d_s(dd int, sn int) u32 {
622 return 0x1E22C000 | (u32(sn) << 5) | u32(dd)
623}
624
625// fcvt sd, dn (convert double-precision to single-precision float)
626fn asm_fcvt_s_d(sd int, dn int) u32 {
627 return 0x1E624000 | (u32(dn) << 5) | u32(sd)
628}
629
630// === Special ===
631
632// udf #0 (undefined - trap)
633fn asm_udf() u32 {
634 return 0x00000000
635}
636