Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/AssemblyBuilderA64.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "Luau/AssemblyBuilderA64.h"
3
4
#include "BitUtils.h"
5
#include "ByteUtils.h"
6
7
#include <stdarg.h>
8
#include <stdio.h>
9
10
namespace Luau
11
{
12
namespace CodeGen
13
{
14
namespace A64
15
{
16
17
static const uint8_t codeForCondition[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
18
static_assert(sizeof(codeForCondition) / sizeof(codeForCondition[0]) == size_t(ConditionA64::Count), "all conditions have to be covered");
19
20
static const char* textForCondition[] =
21
{"b.eq", "b.ne", "b.cs", "b.cc", "b.mi", "b.pl", "b.vs", "b.vc", "b.hi", "b.ls", "b.ge", "b.lt", "b.gt", "b.le", "b.al"};
22
static_assert(sizeof(textForCondition) / sizeof(textForCondition[0]) == size_t(ConditionA64::Count), "all conditions have to be covered");
23
24
const unsigned kMaxAlign = 32;
25
26
static int getFmovImmFp64(double value)
27
{
28
uint64_t u;
29
static_assert(sizeof(u) == sizeof(value), "expected double to be 64-bit");
30
memcpy(&u, &value, sizeof(value));
31
32
// positive 0 is encodable via movi
33
if (u == 0)
34
return 256;
35
36
// early out: fmov can only encode doubles with 48 least significant zeros
37
if ((u & ((1ull << 48) - 1)) != 0)
38
return -1;
39
40
// f64 expansion is abcdfegh => aBbbbbbb bbcdefgh 00000000 00000000 00000000 00000000 00000000 00000000
41
int imm = (int(u >> 56) & 0x80) | (int(u >> 48) & 0x7f);
42
int dec = ((imm & 0x80) << 8) | ((imm & 0x40) ? 0b00111111'11000000 : 0b01000000'00000000) | (imm & 0x3f);
43
44
return dec == int(u >> 48) ? imm : -1;
45
}
46
47
static int getFmovImmFp32(float value)
48
{
49
uint32_t u;
50
static_assert(sizeof(u) == sizeof(value), "expected float to be 32-bit");
51
memcpy(&u, &value, sizeof(value));
52
53
// positive 0 is encodable via movi
54
if (u == 0)
55
return 256;
56
57
// early out: fmov can only encode float with 19 least significant zeros
58
if ((u & ((1ull << 19) - 1)) != 0)
59
return -1;
60
61
// f32 expansion is abcdfegh => aBbbbbbc defgh000 00000000 00000000
62
int imm = (int(u >> 24) & 0x80) | (int(u >> 19) & 0x7f);
63
int dec = ((imm & 0x80) << 5) | ((imm & 0x40) ? 0b00000111'11000000 : 0b00001000'00000000) | (imm & 0x3f);
64
65
return dec == int(u >> 19) ? imm : -1;
66
}
67
68
AssemblyBuilderA64::AssemblyBuilderA64(bool logText, unsigned int features)
69
: logText(logText)
70
, features(features)
71
{
72
data.resize(4096);
73
dataPos = data.size(); // data is filled backwards
74
75
code.resize(1024);
76
codePos = code.data();
77
codeEnd = code.data() + code.size();
78
}
79
80
AssemblyBuilderA64::~AssemblyBuilderA64()
81
{
82
CODEGEN_ASSERT(finalized);
83
}
84
85
void AssemblyBuilderA64::mov(RegisterA64 dst, RegisterA64 src)
86
{
87
if (dst.kind != KindA64::q)
88
{
89
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x || dst == sp);
90
CODEGEN_ASSERT(dst.kind == src.kind || (dst.kind == KindA64::x && src == sp) || (dst == sp && src.kind == KindA64::x));
91
92
if (dst == sp || src == sp)
93
placeR1("mov", dst, src, 0b00'100010'0'000000000000);
94
else
95
placeSR2("mov", dst, src, 0b01'01010);
96
}
97
else
98
{
99
CODEGEN_ASSERT(dst.kind == src.kind);
100
101
placeR1("mov", dst, src, 0b10'01110'10'1'00000'00011'1 | (src.index << 6));
102
}
103
}
104
105
void AssemblyBuilderA64::mov(RegisterA64 dst, int src)
106
{
107
if (src >= 0)
108
{
109
movz(dst, src & 0xffff);
110
if (src > 0xffff)
111
movk(dst, src >> 16, 16);
112
}
113
else
114
{
115
movn(dst, ~src & 0xffff);
116
if (src < -0x10000)
117
movk(dst, (src >> 16) & 0xffff, 16);
118
}
119
}
120
121
void AssemblyBuilderA64::movz(RegisterA64 dst, uint16_t src, int shift)
122
{
123
placeI16("movz", dst, src, 0b10'100101, shift);
124
}
125
126
void AssemblyBuilderA64::movn(RegisterA64 dst, uint16_t src, int shift)
127
{
128
placeI16("movn", dst, src, 0b00'100101, shift);
129
}
130
131
void AssemblyBuilderA64::movk(RegisterA64 dst, uint16_t src, int shift)
132
{
133
placeI16("movk", dst, src, 0b11'100101, shift);
134
}
135
136
void AssemblyBuilderA64::add(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift)
137
{
138
if (src1.kind == KindA64::x && src2.kind == KindA64::w)
139
placeER("add", dst, src1, src2, 0b00'01011, shift);
140
else
141
placeSR3("add", dst, src1, src2, 0b00'01011, shift);
142
}
143
144
void AssemblyBuilderA64::add(RegisterA64 dst, RegisterA64 src1, uint16_t src2)
145
{
146
placeI12("add", dst, src1, src2, 0b00'10001);
147
}
148
149
void AssemblyBuilderA64::sub(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift)
150
{
151
if (src1.kind == KindA64::x && src2.kind == KindA64::w)
152
placeER("sub", dst, src1, src2, 0b10'01011, shift);
153
else
154
placeSR3("sub", dst, src1, src2, 0b10'01011, shift);
155
}
156
157
void AssemblyBuilderA64::sub(RegisterA64 dst, RegisterA64 src1, uint16_t src2)
158
{
159
placeI12("sub", dst, src1, src2, 0b10'10001);
160
}
161
162
void AssemblyBuilderA64::neg(RegisterA64 dst, RegisterA64 src)
163
{
164
placeSR2("neg", dst, src, 0b10'01011);
165
}
166
167
void AssemblyBuilderA64::cmp(RegisterA64 src1, RegisterA64 src2)
168
{
169
RegisterA64 dst = src1.kind == KindA64::x ? xzr : wzr;
170
171
placeSR3("cmp", dst, src1, src2, 0b11'01011);
172
}
173
174
void AssemblyBuilderA64::cmp(RegisterA64 src1, uint16_t src2)
175
{
176
RegisterA64 dst = src1.kind == KindA64::x ? xzr : wzr;
177
178
placeI12("cmp", dst, src1, src2, 0b11'10001);
179
}
180
181
void AssemblyBuilderA64::csel(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, ConditionA64 cond)
182
{
183
CODEGEN_ASSERT(dst.kind == KindA64::x || dst.kind == KindA64::w);
184
185
placeCS("csel", dst, src1, src2, cond, 0b11010'10'0, 0b00);
186
}
187
188
void AssemblyBuilderA64::cset(RegisterA64 dst, ConditionA64 cond)
189
{
190
CODEGEN_ASSERT(dst.kind == KindA64::x || dst.kind == KindA64::w);
191
192
RegisterA64 src = dst.kind == KindA64::x ? xzr : wzr;
193
194
placeCS("cset", dst, src, src, cond, 0b11010'10'0, 0b01, /* invert= */ 1);
195
}
196
197
void AssemblyBuilderA64::and_(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift)
198
{
199
placeSR3("and", dst, src1, src2, 0b00'01010, shift);
200
}
201
202
void AssemblyBuilderA64::orr(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift)
203
{
204
placeSR3("orr", dst, src1, src2, 0b01'01010, shift);
205
}
206
207
void AssemblyBuilderA64::eor(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift)
208
{
209
placeSR3("eor", dst, src1, src2, 0b10'01010, shift);
210
}
211
212
void AssemblyBuilderA64::bic(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift)
213
{
214
placeSR3("bic", dst, src1, src2, 0b00'01010, shift, /* N= */ 1);
215
}
216
217
void AssemblyBuilderA64::tst(RegisterA64 src1, RegisterA64 src2, int shift)
218
{
219
RegisterA64 dst = src1.kind == KindA64::x ? xzr : wzr;
220
221
placeSR3("tst", dst, src1, src2, 0b11'01010, shift);
222
}
223
224
void AssemblyBuilderA64::mvn_(RegisterA64 dst, RegisterA64 src)
225
{
226
placeSR2("mvn", dst, src, 0b01'01010, 0b1);
227
}
228
229
void AssemblyBuilderA64::and_(RegisterA64 dst, RegisterA64 src1, uint32_t src2)
230
{
231
placeBM("and", dst, src1, src2, 0b00'100100);
232
}
233
234
void AssemblyBuilderA64::orr(RegisterA64 dst, RegisterA64 src1, uint32_t src2)
235
{
236
placeBM("orr", dst, src1, src2, 0b01'100100);
237
}
238
239
void AssemblyBuilderA64::eor(RegisterA64 dst, RegisterA64 src1, uint32_t src2)
240
{
241
placeBM("eor", dst, src1, src2, 0b10'100100);
242
}
243
244
void AssemblyBuilderA64::tst(RegisterA64 src1, uint32_t src2)
245
{
246
RegisterA64 dst = src1.kind == KindA64::x ? xzr : wzr;
247
248
placeBM("tst", dst, src1, src2, 0b11'100100);
249
}
250
251
void AssemblyBuilderA64::lsl(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
252
{
253
placeR3("lsl", dst, src1, src2, 0b11010110, 0b0010'00);
254
}
255
256
void AssemblyBuilderA64::lsr(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
257
{
258
placeR3("lsr", dst, src1, src2, 0b11010110, 0b0010'01);
259
}
260
261
void AssemblyBuilderA64::asr(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
262
{
263
placeR3("asr", dst, src1, src2, 0b11010110, 0b0010'10);
264
}
265
266
void AssemblyBuilderA64::ror(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
267
{
268
placeR3("ror", dst, src1, src2, 0b11010110, 0b0010'11);
269
}
270
271
void AssemblyBuilderA64::clz(RegisterA64 dst, RegisterA64 src)
272
{
273
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
274
CODEGEN_ASSERT(dst.kind == src.kind);
275
276
placeR1("clz", dst, src, 0b10'11010110'00000'00010'0);
277
}
278
279
void AssemblyBuilderA64::rbit(RegisterA64 dst, RegisterA64 src)
280
{
281
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
282
CODEGEN_ASSERT(dst.kind == src.kind);
283
284
placeR1("rbit", dst, src, 0b10'11010110'00000'0000'00);
285
}
286
287
void AssemblyBuilderA64::rev(RegisterA64 dst, RegisterA64 src)
288
{
289
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
290
CODEGEN_ASSERT(dst.kind == src.kind);
291
292
placeR1("rev", dst, src, 0b10'11010110'00000'0000'10 | int(dst.kind == KindA64::x));
293
}
294
295
void AssemblyBuilderA64::lsl(RegisterA64 dst, RegisterA64 src1, uint8_t src2)
296
{
297
int size = dst.kind == KindA64::x ? 64 : 32;
298
CODEGEN_ASSERT(src2 < size);
299
300
placeBFM("lsl", dst, src1, src2, 0b10'100110, (-src2) & (size - 1), size - 1 - src2);
301
}
302
303
void AssemblyBuilderA64::lsr(RegisterA64 dst, RegisterA64 src1, uint8_t src2)
304
{
305
int size = dst.kind == KindA64::x ? 64 : 32;
306
CODEGEN_ASSERT(src2 < size);
307
308
placeBFM("lsr", dst, src1, src2, 0b10'100110, src2, size - 1);
309
}
310
311
void AssemblyBuilderA64::asr(RegisterA64 dst, RegisterA64 src1, uint8_t src2)
312
{
313
int size = dst.kind == KindA64::x ? 64 : 32;
314
CODEGEN_ASSERT(src2 < size);
315
316
placeBFM("asr", dst, src1, src2, 0b00'100110, src2, size - 1);
317
}
318
319
void AssemblyBuilderA64::ror(RegisterA64 dst, RegisterA64 src1, uint8_t src2)
320
{
321
int size = dst.kind == KindA64::x ? 64 : 32;
322
CODEGEN_ASSERT(src2 < size);
323
324
// note: this is encoding src1 via immr which is a hack but the bit layout matches and a special archetype feels excessive
325
placeBFM("ror", dst, src1, src2, 0b00'100111, src1.index, src2);
326
}
327
328
void AssemblyBuilderA64::ubfiz(RegisterA64 dst, RegisterA64 src, uint8_t f, uint8_t w)
329
{
330
int size = dst.kind == KindA64::x ? 64 : 32;
331
CODEGEN_ASSERT(w > 0 && f + w <= size);
332
333
// f * 100 + w is only used for disassembly printout; in the future we might replace it with two separate fields for readability
334
placeBFM("ubfiz", dst, src, f * 100 + w, 0b10'100110, (-f) & (size - 1), w - 1);
335
}
336
337
void AssemblyBuilderA64::ubfx(RegisterA64 dst, RegisterA64 src, uint8_t f, uint8_t w)
338
{
339
int size = dst.kind == KindA64::x ? 64 : 32;
340
CODEGEN_ASSERT(w > 0 && f + w <= size);
341
342
// f * 100 + w is only used for disassembly printout; in the future we might replace it with two separate fields for readability
343
placeBFM("ubfx", dst, src, f * 100 + w, 0b10'100110, f, f + w - 1);
344
}
345
346
void AssemblyBuilderA64::sbfiz(RegisterA64 dst, RegisterA64 src, uint8_t f, uint8_t w)
347
{
348
int size = dst.kind == KindA64::x ? 64 : 32;
349
CODEGEN_ASSERT(w > 0 && f + w <= size);
350
351
// f * 100 + w is only used for disassembly printout; in the future we might replace it with two separate fields for readability
352
placeBFM("sbfiz", dst, src, f * 100 + w, 0b00'100110, (-f) & (size - 1), w - 1);
353
}
354
355
void AssemblyBuilderA64::sbfx(RegisterA64 dst, RegisterA64 src, uint8_t f, uint8_t w)
356
{
357
int size = dst.kind == KindA64::x ? 64 : 32;
358
CODEGEN_ASSERT(w > 0 && f + w <= size);
359
360
// f * 100 + w is only used for disassembly printout; in the future we might replace it with two separate fields for readability
361
placeBFM("sbfx", dst, src, f * 100 + w, 0b00'100110, f, f + w - 1);
362
}
363
364
void AssemblyBuilderA64::ldr(RegisterA64 dst, AddressA64 src)
365
{
366
CODEGEN_ASSERT(dst.kind == KindA64::x || dst.kind == KindA64::w || dst.kind == KindA64::s || dst.kind == KindA64::d || dst.kind == KindA64::q);
367
368
switch (dst.kind)
369
{
370
case KindA64::w:
371
placeA("ldr", dst, src, 0b10'11100001, /* sizelog= */ 2);
372
break;
373
case KindA64::x:
374
placeA("ldr", dst, src, 0b11'11100001, /* sizelog= */ 3);
375
break;
376
case KindA64::s:
377
placeA("ldr", dst, src, 0b10'11110001, /* sizelog= */ 2);
378
break;
379
case KindA64::d:
380
placeA("ldr", dst, src, 0b11'11110001, /* sizelog= */ 3);
381
break;
382
case KindA64::q:
383
placeA("ldr", dst, src, 0b00'11110011, /* sizelog= */ 4);
384
break;
385
case KindA64::none:
386
CODEGEN_ASSERT(!"Unexpected register kind");
387
}
388
}
389
390
void AssemblyBuilderA64::ldrb(RegisterA64 dst, AddressA64 src)
391
{
392
CODEGEN_ASSERT(dst.kind == KindA64::w);
393
394
placeA("ldrb", dst, src, 0b00'11100001, /* sizelog= */ 0);
395
}
396
397
void AssemblyBuilderA64::ldrh(RegisterA64 dst, AddressA64 src)
398
{
399
CODEGEN_ASSERT(dst.kind == KindA64::w);
400
401
placeA("ldrh", dst, src, 0b01'11100001, /* sizelog= */ 1);
402
}
403
404
void AssemblyBuilderA64::ldrsb(RegisterA64 dst, AddressA64 src)
405
{
406
CODEGEN_ASSERT(dst.kind == KindA64::x || dst.kind == KindA64::w);
407
408
placeA("ldrsb", dst, src, 0b00'11100010 | uint8_t(dst.kind == KindA64::w), /* sizelog= */ 0);
409
}
410
411
void AssemblyBuilderA64::ldrsh(RegisterA64 dst, AddressA64 src)
412
{
413
CODEGEN_ASSERT(dst.kind == KindA64::x || dst.kind == KindA64::w);
414
415
placeA("ldrsh", dst, src, 0b01'11100010 | uint8_t(dst.kind == KindA64::w), /* sizelog= */ 1);
416
}
417
418
void AssemblyBuilderA64::ldrsw(RegisterA64 dst, AddressA64 src)
419
{
420
CODEGEN_ASSERT(dst.kind == KindA64::x);
421
422
placeA("ldrsw", dst, src, 0b10'11100010, /* sizelog= */ 2);
423
}
424
425
void AssemblyBuilderA64::ldp(RegisterA64 dst1, RegisterA64 dst2, AddressA64 src)
426
{
427
CODEGEN_ASSERT(dst1.kind == KindA64::x || dst1.kind == KindA64::w);
428
CODEGEN_ASSERT(dst1.kind == dst2.kind);
429
430
placeP("ldp", dst1, dst2, src, 0b101'0'010'1, uint8_t(dst1.kind == KindA64::x) << 1, /* sizelog= */ dst1.kind == KindA64::x ? 3 : 2);
431
}
432
433
void AssemblyBuilderA64::str(RegisterA64 src, AddressA64 dst)
434
{
435
CODEGEN_ASSERT(src.kind == KindA64::x || src.kind == KindA64::w || src.kind == KindA64::s || src.kind == KindA64::d || src.kind == KindA64::q);
436
437
switch (src.kind)
438
{
439
case KindA64::w:
440
placeA("str", src, dst, 0b10'11100000, /* sizelog= */ 2);
441
break;
442
case KindA64::x:
443
placeA("str", src, dst, 0b11'11100000, /* sizelog= */ 3);
444
break;
445
case KindA64::s:
446
placeA("str", src, dst, 0b10'11110000, /* sizelog= */ 2);
447
break;
448
case KindA64::d:
449
placeA("str", src, dst, 0b11'11110000, /* sizelog= */ 3);
450
break;
451
case KindA64::q:
452
placeA("str", src, dst, 0b00'11110010, /* sizelog= */ 4);
453
break;
454
case KindA64::none:
455
CODEGEN_ASSERT(!"Unexpected register kind");
456
}
457
}
458
459
void AssemblyBuilderA64::strb(RegisterA64 src, AddressA64 dst)
460
{
461
CODEGEN_ASSERT(src.kind == KindA64::w);
462
463
placeA("strb", src, dst, 0b00'11100000, /* sizelog= */ 0);
464
}
465
466
void AssemblyBuilderA64::strh(RegisterA64 src, AddressA64 dst)
467
{
468
CODEGEN_ASSERT(src.kind == KindA64::w);
469
470
placeA("strh", src, dst, 0b01'11100000, /* sizelog= */ 1);
471
}
472
473
void AssemblyBuilderA64::stp(RegisterA64 src1, RegisterA64 src2, AddressA64 dst)
474
{
475
CODEGEN_ASSERT(src1.kind == KindA64::x || src1.kind == KindA64::w);
476
CODEGEN_ASSERT(src1.kind == src2.kind);
477
478
placeP("stp", src1, src2, dst, 0b101'0'010'0, uint8_t(src1.kind == KindA64::x) << 1, /* sizelog= */ src1.kind == KindA64::x ? 3 : 2);
479
}
480
481
void AssemblyBuilderA64::b(Label& label)
482
{
483
placeB("b", label, 0b0'00101);
484
}
485
486
void AssemblyBuilderA64::bl(Label& label)
487
{
488
placeB("bl", label, 0b1'00101);
489
}
490
491
void AssemblyBuilderA64::br(RegisterA64 src)
492
{
493
placeBR("br", src, 0b1101011'0'0'00'11111'0000'0'0);
494
}
495
496
void AssemblyBuilderA64::blr(RegisterA64 src)
497
{
498
placeBR("blr", src, 0b1101011'0'0'01'11111'0000'0'0);
499
}
500
501
void AssemblyBuilderA64::ret()
502
{
503
place0("ret", 0b1101011'0'0'10'11111'0000'0'0'11110'00000);
504
}
505
506
void AssemblyBuilderA64::b(ConditionA64 cond, Label& label)
507
{
508
placeBC(textForCondition[int(cond)], label, 0b0101010'0, codeForCondition[int(cond)]);
509
}
510
511
void AssemblyBuilderA64::cbz(RegisterA64 src, Label& label)
512
{
513
placeBCR("cbz", label, 0b011010'0, src);
514
}
515
516
void AssemblyBuilderA64::cbnz(RegisterA64 src, Label& label)
517
{
518
placeBCR("cbnz", label, 0b011010'1, src);
519
}
520
521
void AssemblyBuilderA64::tbz(RegisterA64 src, uint8_t bit, Label& label)
522
{
523
placeBTR("tbz", label, 0b011011'0, src, bit);
524
}
525
526
void AssemblyBuilderA64::tbnz(RegisterA64 src, uint8_t bit, Label& label)
527
{
528
placeBTR("tbnz", label, 0b011011'1, src, bit);
529
}
530
531
void AssemblyBuilderA64::adr(RegisterA64 dst, const void* ptr, size_t size)
532
{
533
size_t pos = allocateData(size, 4);
534
uint32_t location = getCodeSize();
535
536
memcpy(&data[pos], ptr, size);
537
placeADR("adr", dst, 0b10000);
538
539
patchOffset(location, -int(location) - int((data.size() - pos) / 4), Patch::Imm19);
540
}
541
542
void AssemblyBuilderA64::adr(RegisterA64 dst, uint64_t value)
543
{
544
size_t pos = allocateData(8, 8);
545
uint32_t location = getCodeSize();
546
547
writeu64(&data[pos], value);
548
placeADR("adr", dst, 0b10000);
549
550
patchOffset(location, -int(location) - int((data.size() - pos) / 4), Patch::Imm19);
551
}
552
553
void AssemblyBuilderA64::adr(RegisterA64 dst, float value)
554
{
555
size_t pos = allocateData(4, 4);
556
uint32_t location = getCodeSize();
557
558
writef32(&data[pos], value);
559
placeADR("adr", dst, 0b10000);
560
561
patchOffset(location, -int(location) - int((data.size() - pos) / 4), Patch::Imm19);
562
}
563
564
void AssemblyBuilderA64::adr(RegisterA64 dst, double value)
565
{
566
size_t pos = allocateData(8, 8);
567
uint32_t location = getCodeSize();
568
569
writef64(&data[pos], value);
570
placeADR("adr", dst, 0b10000);
571
572
patchOffset(location, -int(location) - int((data.size() - pos) / 4), Patch::Imm19);
573
}
574
575
void AssemblyBuilderA64::adr(RegisterA64 dst, Label& label)
576
{
577
placeADR("adr", dst, 0b10000, label);
578
}
579
580
void AssemblyBuilderA64::fmov(RegisterA64 dst, RegisterA64 src)
581
{
582
if (dst.kind == KindA64::d && src.kind == KindA64::d)
583
placeR1("fmov", dst, src, 0b00'11110'01'1'0000'00'10000);
584
else if (dst.kind == KindA64::d && src.kind == KindA64::x)
585
placeR1("fmov", dst, src, 0b00'11110'01'1'00'111'000000);
586
else if (dst.kind == KindA64::x && src.kind == KindA64::d)
587
placeR1("fmov", dst, src, 0b00'11110'01'1'00'110'000000);
588
else if (dst.kind == KindA64::s && src.kind == KindA64::s)
589
placeR1("fmov", dst, src, 0b00'11110'00'1'0000'00'10000);
590
else if (dst.kind == KindA64::s && src.kind == KindA64::w)
591
placeR1("fmov", dst, src, 0b00'11110'00'1'00'111'000000);
592
else
593
CODEGEN_ASSERT(!"Unsupported fmov kind");
594
}
595
596
void AssemblyBuilderA64::fmov(RegisterA64 dst, double src)
597
{
598
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::q);
599
600
int imm = getFmovImmFp64(src);
601
CODEGEN_ASSERT(imm >= 0 && imm <= 256);
602
603
// fmov can't encode 0, but movi can; movi is otherwise not useful for fp immediates because it encodes repeating patterns
604
if (dst.kind == KindA64::d)
605
{
606
if (imm == 256)
607
placeFMOV("movi", dst, src, 0b001'0111100000'000'1110'01'00000);
608
else
609
placeFMOV("fmov", dst, src, 0b000'11110'01'1'00000000'100'00000 | (imm << 8));
610
}
611
else
612
{
613
if (imm == 256)
614
placeFMOV("movi.4s", dst, src, 0b010'0111100000'000'0000'01'00000);
615
else
616
placeFMOV("fmov.4s", dst, src, 0b010'0111100000'000'1111'0'1'00000 | ((imm >> 5) << 11) | (imm & 31));
617
}
618
}
619
620
void AssemblyBuilderA64::fmov(RegisterA64 dst, float src)
621
{
622
CODEGEN_ASSERT(dst.kind == KindA64::s || dst.kind == KindA64::q);
623
624
int imm = getFmovImmFp32(src);
625
CODEGEN_ASSERT(imm >= 0 && imm <= 256);
626
627
// fmov can't encode 0, but movi can; movi is otherwise not useful for fp immediates because it encodes repeating patterns
628
if (dst.kind == KindA64::s)
629
{
630
if (imm == 256)
631
placeFMOV("movi", dst, src, 0b001'0111100000'000'1110'01'00000);
632
else
633
placeFMOV("fmov", dst, src, 0b000'11110'00'1'00000000'100'00000 | (imm << 8));
634
}
635
else
636
{
637
if (imm == 256)
638
placeFMOV("movi.4s", dst, src, 0b010'0111100000'000'0000'01'00000);
639
else
640
placeFMOV("fmov.4s", dst, src, 0b010'0111100000'000'1111'0'1'00000 | ((imm >> 5) << 11) | (imm & 31));
641
}
642
}
643
644
void AssemblyBuilderA64::fabs(RegisterA64 dst, RegisterA64 src)
645
{
646
CODEGEN_ASSERT(dst.kind == src.kind);
647
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s || dst.kind == KindA64::q);
648
649
if (dst.kind == KindA64::q)
650
placeR1("fabs", dst, src, 0b010'01110'10'1'0000'01111'10);
651
else if (dst.kind == KindA64::d)
652
placeR1("fabs", dst, src, 0b000'11110'01'1'0000'01'10000);
653
else
654
placeR1("fabs", dst, src, 0b000'11110'00'1'0000'01'10000);
655
}
656
657
void AssemblyBuilderA64::faddp(RegisterA64 dst, RegisterA64 src)
658
{
659
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s);
660
CODEGEN_ASSERT(dst.kind == src.kind);
661
662
placeR1("faddp", dst, src, 0b011'11110'0'0'11000'01101'10 | ((dst.kind == KindA64::d) << 12));
663
}
664
665
void AssemblyBuilderA64::fmla(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
666
{
667
// There is no scalar version of FMLA instruction
668
// Vector instruction is used for both cases with proper sz bit.
669
670
// Q U Sz Rm Opcode Rn Rd
671
uint32_t op = 0b0'0'0'011100'0'1'00000'110011'00000'00000;
672
const uint32_t QBit = 1 << 30;
673
const uint32_t SzBit = 1 << 22;
674
675
if (dst.kind == KindA64::d)
676
{
677
CODEGEN_ASSERT(src1.kind == KindA64::d && src2.kind == KindA64::d);
678
679
if (logText)
680
logAppend(" %-12sd%d,d%d,d%d\n", "fmla", dst.index, src1.index, src2.index);
681
682
place(dst.index | (src1.index << 5) | (src2.index << 16) | op | QBit | SzBit);
683
}
684
else if (dst.kind == KindA64::s)
685
{
686
CODEGEN_ASSERT(src1.kind == KindA64::s && src2.kind == KindA64::s);
687
688
if (logText)
689
logAppend(" %-12ss%d,s%d,s%d\n", "fmla", dst.index, src1.index, src2.index);
690
691
place(dst.index | (src1.index << 5) | (src2.index << 16) | op);
692
}
693
else
694
{
695
CODEGEN_ASSERT(dst.kind == KindA64::q && src1.kind == KindA64::q && src2.kind == KindA64::q);
696
697
if (logText)
698
logAppend(" %-12sv%d.4s,v%d.4s,v%d.4s\n", "fmla", dst.index, src1.index, src2.index);
699
700
place(dst.index | (src1.index << 5) | (src2.index << 16) | op | QBit);
701
}
702
703
commit();
704
}
705
706
void AssemblyBuilderA64::fadd(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
707
{
708
if (dst.kind == KindA64::d)
709
{
710
CODEGEN_ASSERT(src1.kind == KindA64::d && src2.kind == KindA64::d);
711
712
placeR3("fadd", dst, src1, src2, 0b11110'01'1, 0b0010'10);
713
}
714
else if (dst.kind == KindA64::s)
715
{
716
CODEGEN_ASSERT(src1.kind == KindA64::s && src2.kind == KindA64::s);
717
718
placeR3("fadd", dst, src1, src2, 0b11110'00'1, 0b0010'10);
719
}
720
else
721
{
722
CODEGEN_ASSERT(dst.kind == KindA64::q && src1.kind == KindA64::q && src2.kind == KindA64::q);
723
724
placeVR("fadd", dst, src1, src2, 0b0'01110'0'0'1, 0b11010'1);
725
}
726
}
727
728
void AssemblyBuilderA64::fdiv(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
729
{
730
if (dst.kind == KindA64::d)
731
{
732
CODEGEN_ASSERT(src1.kind == KindA64::d && src2.kind == KindA64::d);
733
734
placeR3("fdiv", dst, src1, src2, 0b11110'01'1, 0b0001'10);
735
}
736
else if (dst.kind == KindA64::s)
737
{
738
CODEGEN_ASSERT(src1.kind == KindA64::s && src2.kind == KindA64::s);
739
740
placeR3("fdiv", dst, src1, src2, 0b11110'00'1, 0b0001'10);
741
}
742
else
743
{
744
CODEGEN_ASSERT(dst.kind == KindA64::q && src1.kind == KindA64::q && src2.kind == KindA64::q);
745
746
placeVR("fdiv", dst, src1, src2, 0b1'01110'00'1, 0b11111'1);
747
}
748
}
749
750
void AssemblyBuilderA64::fmul(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
751
{
752
if (dst.kind == KindA64::d)
753
{
754
CODEGEN_ASSERT(src1.kind == KindA64::d && src2.kind == KindA64::d);
755
756
placeR3("fmul", dst, src1, src2, 0b11110'01'1, 0b0000'10);
757
}
758
else if (dst.kind == KindA64::s)
759
{
760
CODEGEN_ASSERT(src1.kind == KindA64::s && src2.kind == KindA64::s);
761
762
placeR3("fmul", dst, src1, src2, 0b11110'00'1, 0b0000'10);
763
}
764
else
765
{
766
CODEGEN_ASSERT(dst.kind == KindA64::q && src1.kind == KindA64::q && src2.kind == KindA64::q);
767
768
placeVR("fmul", dst, src1, src2, 0b1'01110'00'1, 0b11011'1);
769
}
770
}
771
772
void AssemblyBuilderA64::fneg(RegisterA64 dst, RegisterA64 src)
773
{
774
if (dst.kind == KindA64::d)
775
{
776
CODEGEN_ASSERT(src.kind == KindA64::d);
777
778
placeR1("fneg", dst, src, 0b000'11110'01'1'0000'10'10000);
779
}
780
else if (dst.kind == KindA64::s)
781
{
782
CODEGEN_ASSERT(src.kind == KindA64::s);
783
784
placeR1("fneg", dst, src, 0b000'11110'00'1'0000'10'10000);
785
}
786
else
787
{
788
CODEGEN_ASSERT(dst.kind == KindA64::q && src.kind == KindA64::q);
789
790
placeR1("fneg", dst, src, 0b011'01110'1'0'10000'01111'10);
791
}
792
}
793
794
void AssemblyBuilderA64::fsqrt(RegisterA64 dst, RegisterA64 src)
795
{
796
CODEGEN_ASSERT(dst.kind == src.kind);
797
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s);
798
799
if (dst.kind == KindA64::d)
800
placeR1("fsqrt", dst, src, 0b000'11110'01'1'0000'11'10000);
801
else
802
placeR1("fsqrt", dst, src, 0b000'11110'00'1'0000'11'10000);
803
}
804
805
void AssemblyBuilderA64::fsub(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
806
{
807
if (dst.kind == KindA64::d)
808
{
809
CODEGEN_ASSERT(src1.kind == KindA64::d && src2.kind == KindA64::d);
810
811
placeR3("fsub", dst, src1, src2, 0b11110'01'1, 0b0011'10);
812
}
813
else if (dst.kind == KindA64::s)
814
{
815
CODEGEN_ASSERT(src1.kind == KindA64::s && src2.kind == KindA64::s);
816
817
placeR3("fsub", dst, src1, src2, 0b11110'00'1, 0b0011'10);
818
}
819
else
820
{
821
CODEGEN_ASSERT(dst.kind == KindA64::q && src1.kind == KindA64::q && src2.kind == KindA64::q);
822
823
placeVR("fsub", dst, src1, src2, 0b0'01110'10'1, 0b11010'1);
824
}
825
}
826
827
void AssemblyBuilderA64::ins_4s(RegisterA64 dst, RegisterA64 src, uint8_t index)
828
{
829
CODEGEN_ASSERT(dst.kind == KindA64::q && src.kind == KindA64::w);
830
CODEGEN_ASSERT(index < 4);
831
832
if (logText)
833
logAppend(" %-12sv%d.s[%d],w%d\n", "ins", dst.index, index, src.index);
834
835
uint32_t op = 0b0'1'0'01110000'00100'0'0011'1;
836
837
place(dst.index | (src.index << 5) | (op << 10) | (index << 19));
838
commit();
839
}
840
841
void AssemblyBuilderA64::ins_4s(RegisterA64 dst, uint8_t dstIndex, RegisterA64 src, uint8_t srcIndex)
842
{
843
CODEGEN_ASSERT(dst.kind == KindA64::q && src.kind == KindA64::q);
844
CODEGEN_ASSERT(dstIndex < 4);
845
CODEGEN_ASSERT(srcIndex < 4);
846
847
if (logText)
848
logAppend(" %-12sv%d.s[%d],v%d.s[%d]\n", "ins", dst.index, dstIndex, src.index, srcIndex);
849
850
uint32_t op = 0b0'1'1'01110000'00100'0'0000'1;
851
852
place(dst.index | (src.index << 5) | (op << 10) | (dstIndex << 19) | (srcIndex << 13));
853
commit();
854
}
855
856
void AssemblyBuilderA64::dup_4s(RegisterA64 dst, RegisterA64 src, uint8_t index)
857
{
858
if (dst.kind == KindA64::s)
859
{
860
CODEGEN_ASSERT(src.kind == KindA64::q);
861
CODEGEN_ASSERT(index < 4);
862
863
if (logText)
864
logAppend(" %-12ss%d,v%d.s[%d]\n", "dup", dst.index, src.index, index);
865
866
uint32_t op = 0b01'0'11110000'00100'0'0000'1;
867
868
place(dst.index | (src.index << 5) | (op << 10) | (index << 19));
869
}
870
else
871
{
872
CODEGEN_ASSERT(src.kind == KindA64::q);
873
CODEGEN_ASSERT(index < 4);
874
875
if (logText)
876
logAppend(" %-12sv%d.4s,v%d.s[%d]\n", "dup", dst.index, src.index, index);
877
878
uint32_t op = 0b010'01110000'00100'0'0000'1;
879
880
place(dst.index | (src.index << 5) | (op << 10) | (index << 19));
881
}
882
883
commit();
884
}
885
886
void AssemblyBuilderA64::umov_4s(RegisterA64 dst, RegisterA64 src, uint8_t index)
887
{
888
CODEGEN_ASSERT(dst.kind == KindA64::w);
889
CODEGEN_ASSERT(src.kind == KindA64::q);
890
CODEGEN_ASSERT(index < 4);
891
892
if (logText)
893
logAppend(" %-12sw%d,v%d.s[%d]\n", "umov", dst.index, src.index, index);
894
895
// Q A-SIMD SzIdx Opcode Rn Rd
896
uint32_t op = 0b0'0'0'01110000'00100'001111'00000'00000;
897
898
place(dst.index | (src.index << 5) | op | (index << 19));
899
900
commit();
901
}
902
903
void AssemblyBuilderA64::fcmeq_4s(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
904
{
905
if (logText)
906
logAppend(" %-12sv%d.4s,v%d.4s,v%d.4s\n", "fcmeq", dst.index, src1.index, src2.index);
907
908
// Q U ESz Rm Opcode Rn Rd
909
uint32_t op = 0b0'1'0'01110001'00000'111001'00000'00000;
910
911
place(dst.index | (src1.index << 5) | (src2.index << 16) | op);
912
913
commit();
914
}
915
916
void AssemblyBuilderA64::fcmgt_4s(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2)
917
{
918
if (logText)
919
logAppend(" %-12sv%d.4s,v%d.4s,v%d.4s\n", "fcmgt", dst.index, src1.index, src2.index);
920
921
// Q U ESz Rm Opcode Rn Rd
922
uint32_t op = 0b0'1'1'01110101'00000'111001'00000'00000;
923
924
place(dst.index | (src1.index << 5) | (src2.index << 16) | op);
925
926
commit();
927
}
928
929
void AssemblyBuilderA64::bit(RegisterA64 dst, RegisterA64 src, RegisterA64 mask)
930
{
931
if (logText)
932
logAppend(" %-12sv%d.16b,v%d.16b,v%d.16b\n", "bit", dst.index, src.index, mask.index);
933
934
// Q U Rm Opcode Rn Rd
935
uint32_t op = 0b0'1'1'01110101'00000'000111'00000'00000;
936
937
place(dst.index | (src.index << 5) | (mask.index << 16) | op);
938
939
commit();
940
}
941
942
void AssemblyBuilderA64::bif(RegisterA64 dst, RegisterA64 src, RegisterA64 mask)
943
{
944
if (logText)
945
logAppend(" %-12sv%d.16b,v%d.16b,v%d.16b\n", "bif", dst.index, src.index, mask.index);
946
947
// Q U Rm Opcode Rn Rd
948
uint32_t op = 0b0'1'1'01110111'00000'000111'00000'00000;
949
950
place(dst.index | (src.index << 5) | (mask.index << 16) | op);
951
952
commit();
953
}
954
955
void AssemblyBuilderA64::frinta(RegisterA64 dst, RegisterA64 src)
956
{
957
CODEGEN_ASSERT(dst.kind == src.kind);
958
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s || dst.kind == KindA64::q);
959
960
if (dst.kind == KindA64::q)
961
placeR1("frinta", dst, src, 0b011'01110'00'1'0000'11000'10);
962
else if (dst.kind == KindA64::d)
963
placeR1("frinta", dst, src, 0b000'11110'01'1'001'100'10000);
964
else
965
placeR1("frinta", dst, src, 0b000'11110'00'1'001'100'10000);
966
}
967
968
void AssemblyBuilderA64::frintm(RegisterA64 dst, RegisterA64 src)
969
{
970
CODEGEN_ASSERT(dst.kind == src.kind);
971
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s || dst.kind == KindA64::q);
972
973
if (dst.kind == KindA64::q)
974
placeR1("frintm", dst, src, 0b010'01110'00'1'0000'11001'10);
975
else if (dst.kind == KindA64::d)
976
placeR1("frintm", dst, src, 0b000'11110'01'1'001'010'10000);
977
else
978
placeR1("frintm", dst, src, 0b000'11110'00'1'001'010'10000);
979
}
980
981
void AssemblyBuilderA64::frintp(RegisterA64 dst, RegisterA64 src)
982
{
983
CODEGEN_ASSERT(dst.kind == src.kind);
984
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s || dst.kind == KindA64::q);
985
986
if (dst.kind == KindA64::q)
987
placeR1("frintp", dst, src, 0b010'01110'10'1'0000'11000'10);
988
else if (dst.kind == KindA64::d)
989
placeR1("frintp", dst, src, 0b000'11110'01'1'001'001'10000);
990
else
991
placeR1("frintp", dst, src, 0b000'11110'00'1'001'001'10000);
992
}
993
994
void AssemblyBuilderA64::fcvt(RegisterA64 dst, RegisterA64 src)
995
{
996
if (dst.kind == KindA64::s && src.kind == KindA64::d)
997
placeR1("fcvt", dst, src, 0b11110'01'1'0001'00'10000);
998
else if (dst.kind == KindA64::d && src.kind == KindA64::s)
999
placeR1("fcvt", dst, src, 0b11110'00'1'0001'01'10000);
1000
else
1001
CODEGEN_ASSERT(!"Unexpected register kind");
1002
}
1003
1004
void AssemblyBuilderA64::fcvtzs(RegisterA64 dst, RegisterA64 src)
1005
{
1006
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
1007
CODEGEN_ASSERT(src.kind == KindA64::d);
1008
1009
placeR1("fcvtzs", dst, src, 0b000'11110'01'1'11'000'000000);
1010
}
1011
1012
void AssemblyBuilderA64::fcvtzu(RegisterA64 dst, RegisterA64 src)
1013
{
1014
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
1015
CODEGEN_ASSERT(src.kind == KindA64::d);
1016
1017
placeR1("fcvtzu", dst, src, 0b000'11110'01'1'11'001'000000);
1018
}
1019
1020
void AssemblyBuilderA64::scvtf(RegisterA64 dst, RegisterA64 src)
1021
{
1022
CODEGEN_ASSERT(dst.kind == KindA64::d);
1023
CODEGEN_ASSERT(src.kind == KindA64::w || src.kind == KindA64::x);
1024
1025
placeR1("scvtf", dst, src, 0b000'11110'01'1'00'010'000000);
1026
}
1027
1028
void AssemblyBuilderA64::ucvtf(RegisterA64 dst, RegisterA64 src)
1029
{
1030
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s);
1031
CODEGEN_ASSERT(src.kind == KindA64::w || src.kind == KindA64::x);
1032
1033
if (dst.kind == KindA64::d)
1034
placeR1("ucvtf", dst, src, 0b000'11110'01'1'00'011'000000);
1035
else
1036
placeR1("ucvtf", dst, src, 0b000'11110'00'1'00'011'000000);
1037
}
1038
1039
void AssemblyBuilderA64::fjcvtzs(RegisterA64 dst, RegisterA64 src)
1040
{
1041
CODEGEN_ASSERT(dst.kind == KindA64::w);
1042
CODEGEN_ASSERT(src.kind == KindA64::d);
1043
CODEGEN_ASSERT(features & Feature_JSCVT);
1044
1045
placeR1("fjcvtzs", dst, src, 0b000'11110'01'1'11'110'000000);
1046
}
1047
1048
void AssemblyBuilderA64::fcmp(RegisterA64 src1, RegisterA64 src2)
1049
{
1050
CODEGEN_ASSERT(src1.kind == src2.kind);
1051
CODEGEN_ASSERT(src1.kind == KindA64::d || src1.kind == KindA64::s);
1052
1053
if (src1.kind == KindA64::d)
1054
placeFCMP("fcmp", src1, src2, 0b11110'01'1, 0b00);
1055
else
1056
placeFCMP("fcmp", src1, src2, 0b11110'00'1, 0b00);
1057
}
1058
1059
void AssemblyBuilderA64::fcmpz(RegisterA64 src)
1060
{
1061
CODEGEN_ASSERT(src.kind == KindA64::d || src.kind == KindA64::s);
1062
1063
if (src.kind == KindA64::d)
1064
placeFCMP("fcmp", src, RegisterA64{src.kind, 0}, 0b11110'01'1, 0b01);
1065
else
1066
placeFCMP("fcmp", src, RegisterA64{src.kind, 0}, 0b11110'00'1, 0b01);
1067
}
1068
1069
void AssemblyBuilderA64::fcsel(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, ConditionA64 cond)
1070
{
1071
CODEGEN_ASSERT(dst.kind == src1.kind && src1.kind == src2.kind);
1072
CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s);
1073
1074
if (src1.kind == KindA64::d)
1075
placeCS("fcsel", dst, src1, src2, cond, 0b11110'01'1, 0b11);
1076
else
1077
placeCS("fcsel", dst, src1, src2, cond, 0b11110'00'1, 0b11);
1078
}
1079
1080
void AssemblyBuilderA64::udf()
1081
{
1082
place0("udf", 0);
1083
}
1084
1085
bool AssemblyBuilderA64::finalize()
1086
{
1087
code.resize(codePos - code.data());
1088
1089
// Resolve jump targets
1090
for (Patch fixup : pendingLabels)
1091
{
1092
// If this assertion fires, a label was used in jmp without calling setLabel
1093
uint32_t label = fixup.label;
1094
CODEGEN_ASSERT(labelLocations[label - 1] != ~0u);
1095
int value = int(labelLocations[label - 1]) - int(fixup.location);
1096
1097
patchOffset(fixup.location, value, fixup.kind);
1098
}
1099
1100
size_t dataSize = data.size() - dataPos;
1101
1102
// Shrink data
1103
if (dataSize > 0)
1104
memmove(&data[0], &data[dataPos], dataSize);
1105
1106
data.resize(dataSize);
1107
1108
finalized = true;
1109
1110
return !overflowed;
1111
}
1112
1113
Label AssemblyBuilderA64::setLabel()
1114
{
1115
Label label{nextLabel++, getCodeSize()};
1116
labelLocations.push_back(~0u);
1117
1118
if (logText)
1119
log(label);
1120
1121
return label;
1122
}
1123
1124
void AssemblyBuilderA64::setLabel(Label& label)
1125
{
1126
if (label.id == 0)
1127
{
1128
label.id = nextLabel++;
1129
labelLocations.push_back(~0u);
1130
}
1131
1132
label.location = getCodeSize();
1133
labelLocations[label.id - 1] = label.location;
1134
1135
if (logText)
1136
log(label);
1137
}
1138
1139
void AssemblyBuilderA64::logAppend(const char* fmt, ...)
1140
{
1141
char buf[256];
1142
va_list args;
1143
va_start(args, fmt);
1144
vsnprintf(buf, sizeof(buf), fmt, args);
1145
va_end(args);
1146
text.append(buf);
1147
}
1148
1149
uint32_t AssemblyBuilderA64::getCodeSize() const
1150
{
1151
return uint32_t(codePos - code.data());
1152
}
1153
1154
unsigned AssemblyBuilderA64::getInstructionCount() const
1155
{
1156
return unsigned(getCodeSize());
1157
}
1158
1159
bool AssemblyBuilderA64::isMaskSupported(uint32_t mask)
1160
{
1161
int lz = countlz(mask);
1162
int rz = countrz(mask);
1163
1164
return lz + rz > 0 && lz + rz < 32 && // must have at least one 0 and at least one 1
1165
(mask >> rz) == (1u << (32 - lz - rz)) - 1; // sequence of 1s must be contiguous
1166
}
1167
1168
bool AssemblyBuilderA64::isFmovSupportedFp64(double value)
1169
{
1170
return getFmovImmFp64(value) >= 0;
1171
}
1172
1173
bool AssemblyBuilderA64::isFmovSupportedFp32(float value)
1174
{
1175
return getFmovImmFp32(value) >= 0;
1176
}
1177
1178
void AssemblyBuilderA64::place0(const char* name, uint32_t op)
1179
{
1180
if (logText)
1181
log(name);
1182
1183
place(op);
1184
commit();
1185
}
1186
1187
void AssemblyBuilderA64::placeSR3(const char* name, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, uint8_t op, int shift, int N)
1188
{
1189
if (logText)
1190
log(name, dst, src1, src2, shift);
1191
1192
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
1193
CODEGEN_ASSERT(dst.kind == src1.kind && dst.kind == src2.kind);
1194
CODEGEN_ASSERT(shift >= -63 && shift <= 63);
1195
1196
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0;
1197
1198
place(
1199
dst.index | (src1.index << 5) | ((shift < 0 ? -shift : shift) << 10) | (src2.index << 16) | (N << 21) | (int(shift < 0) << 22) | (op << 24) |
1200
sf
1201
);
1202
commit();
1203
}
1204
1205
void AssemblyBuilderA64::placeSR2(const char* name, RegisterA64 dst, RegisterA64 src, uint8_t op, uint8_t op2)
1206
{
1207
if (logText)
1208
log(name, dst, src);
1209
1210
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
1211
CODEGEN_ASSERT(dst.kind == src.kind);
1212
1213
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0;
1214
1215
place(dst.index | (0x1f << 5) | (src.index << 16) | (op2 << 21) | (op << 24) | sf);
1216
commit();
1217
}
1218
1219
void AssemblyBuilderA64::placeR3(const char* name, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, uint8_t op, uint8_t op2)
1220
{
1221
if (logText)
1222
log(name, dst, src1, src2);
1223
1224
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x || dst.kind == KindA64::d || dst.kind == KindA64::s);
1225
CODEGEN_ASSERT(dst.kind == src1.kind && dst.kind == src2.kind);
1226
1227
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0;
1228
1229
place(dst.index | (src1.index << 5) | (op2 << 10) | (src2.index << 16) | (op << 21) | sf);
1230
commit();
1231
}
1232
1233
void AssemblyBuilderA64::placeR1(const char* name, RegisterA64 dst, RegisterA64 src, uint32_t op)
1234
{
1235
if (logText)
1236
log(name, dst, src);
1237
1238
uint32_t sf = (dst.kind == KindA64::x || src.kind == KindA64::x) ? 0x80000000 : 0;
1239
1240
place(dst.index | (src.index << 5) | (op << 10) | sf);
1241
commit();
1242
}
1243
1244
void AssemblyBuilderA64::placeI12(const char* name, RegisterA64 dst, RegisterA64 src1, int src2, uint8_t op)
1245
{
1246
if (logText)
1247
log(name, dst, src1, src2);
1248
1249
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x || dst == sp);
1250
CODEGEN_ASSERT(dst.kind == src1.kind || (dst.kind == KindA64::x && src1 == sp) || (dst == sp && src1.kind == KindA64::x));
1251
CODEGEN_ASSERT(src2 >= 0 && src2 < (1 << 12));
1252
1253
uint32_t sf = (dst.kind != KindA64::w) ? 0x80000000 : 0;
1254
1255
place(dst.index | (src1.index << 5) | (src2 << 10) | (op << 24) | sf);
1256
commit();
1257
}
1258
1259
void AssemblyBuilderA64::placeI16(const char* name, RegisterA64 dst, int src, uint8_t op, int shift)
1260
{
1261
if (logText)
1262
log(name, dst, src, shift);
1263
1264
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
1265
CODEGEN_ASSERT(src >= 0 && src <= 0xffff);
1266
CODEGEN_ASSERT(shift == 0 || shift == 16 || shift == 32 || shift == 48);
1267
1268
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0;
1269
1270
place(dst.index | (src << 5) | ((shift >> 4) << 21) | (op << 23) | sf);
1271
commit();
1272
}
1273
1274
void AssemblyBuilderA64::placeA(const char* name, RegisterA64 dst, AddressA64 src, uint16_t opsize, int sizelog)
1275
{
1276
if (logText)
1277
log(name, dst, src);
1278
1279
switch (src.kind)
1280
{
1281
case AddressKindA64::reg:
1282
place(dst.index | (src.base.index << 5) | (0b011'0'10 << 10) | (src.offset.index << 16) | (1 << 21) | (opsize << 22));
1283
break;
1284
case AddressKindA64::imm:
1285
if (unsigned(src.data >> sizelog) < 1024 && (src.data & ((1 << sizelog) - 1)) == 0)
1286
{
1287
place(dst.index | (src.base.index << 5) | ((src.data >> sizelog) << 10) | (opsize << 22) | (1 << 24));
1288
}
1289
else if (src.data >= -256 && src.data <= 255)
1290
{
1291
place(dst.index | (src.base.index << 5) | ((src.data & ((1 << 9) - 1)) << 12) | (opsize << 22));
1292
}
1293
else
1294
{
1295
overflowed = true;
1296
1297
CODEGEN_ASSERT(!"Unable to encode large immediate offset");
1298
}
1299
break;
1300
case AddressKindA64::pre:
1301
CODEGEN_ASSERT(src.data >= -256 && src.data <= 255);
1302
place(dst.index | (src.base.index << 5) | (0b11 << 10) | ((src.data & ((1 << 9) - 1)) << 12) | (opsize << 22));
1303
break;
1304
case AddressKindA64::post:
1305
CODEGEN_ASSERT(src.data >= -256 && src.data <= 255);
1306
place(dst.index | (src.base.index << 5) | (0b01 << 10) | ((src.data & ((1 << 9) - 1)) << 12) | (opsize << 22));
1307
break;
1308
}
1309
1310
commit();
1311
}
1312
1313
void AssemblyBuilderA64::placeB(const char* name, Label& label, uint8_t op)
1314
{
1315
place(op << 26);
1316
commit();
1317
1318
patchLabel(label, Patch::Imm26);
1319
1320
if (logText)
1321
log(name, label);
1322
}
1323
1324
void AssemblyBuilderA64::placeBC(const char* name, Label& label, uint8_t op, uint8_t cond)
1325
{
1326
place(cond | (op << 24));
1327
commit();
1328
1329
patchLabel(label, Patch::Imm19);
1330
1331
if (logText)
1332
log(name, label);
1333
}
1334
1335
void AssemblyBuilderA64::placeBCR(const char* name, Label& label, uint8_t op, RegisterA64 cond)
1336
{
1337
CODEGEN_ASSERT(cond.kind == KindA64::w || cond.kind == KindA64::x);
1338
1339
uint32_t sf = (cond.kind == KindA64::x) ? 0x80000000 : 0;
1340
1341
place(cond.index | (op << 24) | sf);
1342
commit();
1343
1344
patchLabel(label, Patch::Imm19);
1345
1346
if (logText)
1347
log(name, cond, label);
1348
}
1349
1350
void AssemblyBuilderA64::placeBR(const char* name, RegisterA64 src, uint32_t op)
1351
{
1352
if (logText)
1353
log(name, src);
1354
1355
CODEGEN_ASSERT(src.kind == KindA64::x);
1356
1357
place((src.index << 5) | (op << 10));
1358
commit();
1359
}
1360
1361
void AssemblyBuilderA64::placeBTR(const char* name, Label& label, uint8_t op, RegisterA64 cond, uint8_t bit)
1362
{
1363
CODEGEN_ASSERT(cond.kind == KindA64::x || cond.kind == KindA64::w);
1364
CODEGEN_ASSERT(bit < (cond.kind == KindA64::x ? 64 : 32));
1365
1366
place(cond.index | ((bit & 0x1f) << 19) | (op << 24) | ((bit >> 5) << 31));
1367
commit();
1368
1369
patchLabel(label, Patch::Imm14);
1370
1371
if (logText)
1372
log(name, cond, label, bit);
1373
}
1374
1375
void AssemblyBuilderA64::placeADR(const char* name, RegisterA64 dst, uint8_t op)
1376
{
1377
if (logText)
1378
log(name, dst);
1379
1380
CODEGEN_ASSERT(dst.kind == KindA64::x);
1381
1382
place(dst.index | (op << 24));
1383
commit();
1384
}
1385
1386
void AssemblyBuilderA64::placeADR(const char* name, RegisterA64 dst, uint8_t op, Label& label)
1387
{
1388
CODEGEN_ASSERT(dst.kind == KindA64::x);
1389
1390
place(dst.index | (op << 24));
1391
commit();
1392
1393
patchLabel(label, Patch::Imm19);
1394
1395
if (logText)
1396
log(name, dst, label);
1397
}
1398
1399
void AssemblyBuilderA64::placeP(const char* name, RegisterA64 src1, RegisterA64 src2, AddressA64 dst, uint8_t op, uint8_t opc, int sizelog)
1400
{
1401
if (logText)
1402
log(name, src1, src2, dst);
1403
1404
CODEGEN_ASSERT(dst.kind == AddressKindA64::imm);
1405
CODEGEN_ASSERT(dst.data >= -128 * (1 << sizelog) && dst.data <= 127 * (1 << sizelog));
1406
CODEGEN_ASSERT(dst.data % (1 << sizelog) == 0);
1407
1408
place(src1.index | (dst.base.index << 5) | (src2.index << 10) | (((dst.data >> sizelog) & 127) << 15) | (op << 22) | (opc << 30));
1409
commit();
1410
}
1411
1412
void AssemblyBuilderA64::placeCS(
1413
const char* name,
1414
RegisterA64 dst,
1415
RegisterA64 src1,
1416
RegisterA64 src2,
1417
ConditionA64 cond,
1418
uint8_t op,
1419
uint8_t opc,
1420
int invert
1421
)
1422
{
1423
if (logText)
1424
log(name, dst, src1, src2, cond);
1425
1426
CODEGEN_ASSERT(dst.kind == src1.kind && dst.kind == src2.kind);
1427
1428
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0;
1429
1430
place(dst.index | (src1.index << 5) | (opc << 10) | ((codeForCondition[int(cond)] ^ invert) << 12) | (src2.index << 16) | (op << 21) | sf);
1431
commit();
1432
}
1433
1434
void AssemblyBuilderA64::placeFCMP(const char* name, RegisterA64 src1, RegisterA64 src2, uint8_t op, uint8_t opc)
1435
{
1436
if (logText)
1437
{
1438
if (opc)
1439
log(name, src1, 0);
1440
else
1441
log(name, src1, src2);
1442
}
1443
1444
CODEGEN_ASSERT(src1.kind == src2.kind);
1445
1446
place((opc << 3) | (src1.index << 5) | (0b1000 << 10) | (src2.index << 16) | (op << 21));
1447
commit();
1448
}
1449
1450
void AssemblyBuilderA64::placeFMOV(const char* name, RegisterA64 dst, double src, uint32_t op)
1451
{
1452
if (logText)
1453
log(name, dst, src);
1454
1455
place(dst.index | (op << 5));
1456
commit();
1457
}
1458
1459
void AssemblyBuilderA64::placeBM(const char* name, RegisterA64 dst, RegisterA64 src1, uint32_t src2, uint8_t op)
1460
{
1461
if (logText)
1462
log(name, dst, src1, src2);
1463
1464
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
1465
CODEGEN_ASSERT(dst.kind == src1.kind);
1466
CODEGEN_ASSERT(isMaskSupported(src2));
1467
1468
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0;
1469
1470
int lz = countlz(src2);
1471
int rz = countrz(src2);
1472
1473
int imms = 31 - lz - rz; // count of 1s minus 1
1474
int immr = (32 - rz) & 31; // right rotate amount
1475
1476
place(dst.index | (src1.index << 5) | (imms << 10) | (immr << 16) | (op << 23) | sf);
1477
commit();
1478
}
1479
1480
void AssemblyBuilderA64::placeBFM(const char* name, RegisterA64 dst, RegisterA64 src1, int src2, uint8_t op, int immr, int imms)
1481
{
1482
if (logText)
1483
log(name, dst, src1, src2);
1484
1485
CODEGEN_ASSERT(dst.kind == KindA64::w || dst.kind == KindA64::x);
1486
CODEGEN_ASSERT(dst.kind == src1.kind);
1487
1488
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0;
1489
uint32_t n = (dst.kind == KindA64::x) ? 1 << 22 : 0;
1490
1491
place(dst.index | (src1.index << 5) | (imms << 10) | (immr << 16) | n | (op << 23) | sf);
1492
commit();
1493
}
1494
1495
void AssemblyBuilderA64::placeER(const char* name, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, uint8_t op, int shift)
1496
{
1497
if (logText)
1498
log(name, dst, src1, src2, shift);
1499
1500
CODEGEN_ASSERT(dst.kind == KindA64::x && src1.kind == KindA64::x);
1501
CODEGEN_ASSERT(src2.kind == KindA64::w);
1502
CODEGEN_ASSERT(shift >= 0 && shift <= 4);
1503
1504
uint32_t sf = (dst.kind == KindA64::x) ? 0x80000000 : 0; // could be useful in the future for byte->word extends
1505
int option = 0b010; // UXTW
1506
1507
place(dst.index | (src1.index << 5) | (shift << 10) | (option << 13) | (src2.index << 16) | (1 << 21) | (op << 24) | sf);
1508
commit();
1509
}
1510
1511
void AssemblyBuilderA64::placeVR(const char* name, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, uint16_t op, uint8_t op2)
1512
{
1513
if (logText)
1514
logAppend(" %-12sv%d.4s,v%d.4s,v%d.4s\n", name, dst.index, src1.index, src2.index);
1515
1516
CODEGEN_ASSERT(dst.kind == KindA64::q && dst.kind == src1.kind && dst.kind == src2.kind);
1517
1518
place(dst.index | (src1.index << 5) | (op2 << 10) | (src2.index << 16) | (op << 21) | (1 << 30));
1519
commit();
1520
}
1521
1522
void AssemblyBuilderA64::place(uint32_t word)
1523
{
1524
CODEGEN_ASSERT(codePos < codeEnd);
1525
*codePos++ = word;
1526
}
1527
1528
void AssemblyBuilderA64::patchLabel(Label& label, Patch::Kind kind)
1529
{
1530
uint32_t location = getCodeSize() - 1;
1531
1532
if (label.location == ~0u)
1533
{
1534
if (label.id == 0)
1535
{
1536
label.id = nextLabel++;
1537
labelLocations.push_back(~0u);
1538
}
1539
1540
pendingLabels.push_back({kind, label.id, location});
1541
}
1542
else
1543
{
1544
int value = int(label.location) - int(location);
1545
1546
patchOffset(location, value, kind);
1547
}
1548
}
1549
1550
void AssemblyBuilderA64::patchOffset(uint32_t location, int value, Patch::Kind kind)
1551
{
1552
int offset = (kind == Patch::Imm26) ? 0 : 5;
1553
int range = (kind == Patch::Imm19) ? (1 << 19) : (kind == Patch::Imm26) ? (1 << 26) : (1 << 14);
1554
1555
CODEGEN_ASSERT((code[location] & ((range - 1) << offset)) == 0);
1556
1557
if (value > -(range >> 1) && value < (range >> 1))
1558
code[location] |= (value & (range - 1)) << offset;
1559
else
1560
overflowed = true;
1561
}
1562
1563
void AssemblyBuilderA64::commit()
1564
{
1565
CODEGEN_ASSERT(codePos <= codeEnd);
1566
1567
if (codeEnd == codePos)
1568
extend();
1569
}
1570
1571
void AssemblyBuilderA64::extend()
1572
{
1573
uint32_t count = getCodeSize();
1574
1575
code.resize(code.size() * 2);
1576
codePos = code.data() + count;
1577
codeEnd = code.data() + code.size();
1578
}
1579
1580
size_t AssemblyBuilderA64::allocateData(size_t size, size_t align)
1581
{
1582
CODEGEN_ASSERT(align > 0 && align <= kMaxAlign && (align & (align - 1)) == 0);
1583
1584
if (dataPos < size)
1585
{
1586
size_t oldSize = data.size();
1587
data.resize(data.size() * 2);
1588
memcpy(&data[oldSize], &data[0], oldSize);
1589
memset(&data[0], 0, oldSize);
1590
dataPos += oldSize;
1591
}
1592
1593
dataPos = (dataPos - size) & ~(align - 1);
1594
1595
return dataPos;
1596
}
1597
1598
void AssemblyBuilderA64::log(const char* opcode)
1599
{
1600
logAppend(" %s\n", opcode);
1601
}
1602
1603
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift)
1604
{
1605
logAppend(" %-12s", opcode);
1606
if (dst != xzr && dst != wzr)
1607
{
1608
log(dst);
1609
text.append(",");
1610
}
1611
log(src1);
1612
text.append(",");
1613
log(src2);
1614
if (src1.kind == KindA64::x && src2.kind == KindA64::w)
1615
logAppend(" UXTW #%d", shift);
1616
else if (shift > 0)
1617
logAppend(" LSL #%d", shift);
1618
else if (shift < 0)
1619
logAppend(" LSR #%d", -shift);
1620
text.append("\n");
1621
}
1622
1623
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst, RegisterA64 src1, int src2)
1624
{
1625
logAppend(" %-12s", opcode);
1626
if (dst != xzr && dst != wzr)
1627
{
1628
log(dst);
1629
text.append(",");
1630
}
1631
log(src1);
1632
text.append(",");
1633
logAppend("#%d", src2);
1634
text.append("\n");
1635
}
1636
1637
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst, AddressA64 src)
1638
{
1639
logAppend(" %-12s", opcode);
1640
log(dst);
1641
text.append(",");
1642
log(src);
1643
text.append("\n");
1644
}
1645
1646
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst1, RegisterA64 dst2, AddressA64 src)
1647
{
1648
logAppend(" %-12s", opcode);
1649
log(dst1);
1650
text.append(",");
1651
log(dst2);
1652
text.append(",");
1653
log(src);
1654
text.append("\n");
1655
}
1656
1657
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst, RegisterA64 src)
1658
{
1659
logAppend(" %-12s", opcode);
1660
log(dst);
1661
text.append(",");
1662
log(src);
1663
text.append("\n");
1664
}
1665
1666
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst, int src, int shift)
1667
{
1668
logAppend(" %-12s", opcode);
1669
log(dst);
1670
text.append(",");
1671
logAppend("#%d", src);
1672
if (shift > 0)
1673
logAppend(" LSL #%d", shift);
1674
text.append("\n");
1675
}
1676
1677
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst, double src)
1678
{
1679
logAppend(" %-12s", opcode);
1680
log(dst);
1681
text.append(",");
1682
logAppend("#%.17g", src);
1683
text.append("\n");
1684
}
1685
1686
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 src, Label label, int imm)
1687
{
1688
logAppend(" %-12s", opcode);
1689
log(src);
1690
text.append(",");
1691
if (imm >= 0)
1692
logAppend("#%d,", imm);
1693
logAppend(".L%d\n", label.id);
1694
}
1695
1696
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 src)
1697
{
1698
logAppend(" %-12s", opcode);
1699
log(src);
1700
text.append("\n");
1701
}
1702
1703
void AssemblyBuilderA64::log(const char* opcode, Label label)
1704
{
1705
logAppend(" %-12s.L%d\n", opcode, label.id);
1706
}
1707
1708
void AssemblyBuilderA64::log(const char* opcode, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, ConditionA64 cond)
1709
{
1710
logAppend(" %-12s", opcode);
1711
log(dst);
1712
if ((src1 != wzr && src1 != xzr) || (src2 != wzr && src2 != xzr))
1713
{
1714
text.append(",");
1715
log(src1);
1716
text.append(",");
1717
log(src2);
1718
}
1719
text.append(",");
1720
text.append(textForCondition[int(cond)] + 2); // skip b.
1721
text.append("\n");
1722
}
1723
1724
void AssemblyBuilderA64::log(Label label)
1725
{
1726
logAppend(".L%d:\n", label.id);
1727
}
1728
1729
void AssemblyBuilderA64::log(RegisterA64 reg)
1730
{
1731
switch (reg.kind)
1732
{
1733
case KindA64::w:
1734
if (reg.index == 31)
1735
text.append("wzr");
1736
else
1737
logAppend("w%d", reg.index);
1738
break;
1739
1740
case KindA64::x:
1741
if (reg.index == 31)
1742
text.append("xzr");
1743
else
1744
logAppend("x%d", reg.index);
1745
break;
1746
1747
case KindA64::s:
1748
logAppend("s%d", reg.index);
1749
break;
1750
1751
case KindA64::d:
1752
logAppend("d%d", reg.index);
1753
break;
1754
1755
case KindA64::q:
1756
logAppend("q%d", reg.index);
1757
break;
1758
1759
case KindA64::none:
1760
if (reg.index == 31)
1761
text.append("sp");
1762
else
1763
CODEGEN_ASSERT(!"Unexpected register kind");
1764
break;
1765
}
1766
}
1767
1768
void AssemblyBuilderA64::log(AddressA64 addr)
1769
{
1770
switch (addr.kind)
1771
{
1772
case AddressKindA64::reg:
1773
text.append("[");
1774
log(addr.base);
1775
text.append(",");
1776
log(addr.offset);
1777
text.append("]");
1778
break;
1779
case AddressKindA64::imm:
1780
text.append("[");
1781
log(addr.base);
1782
if (addr.data != 0)
1783
logAppend(",#%d", addr.data);
1784
text.append("]");
1785
break;
1786
case AddressKindA64::pre:
1787
text.append("[");
1788
log(addr.base);
1789
if (addr.data != 0)
1790
logAppend(",#%d", addr.data);
1791
text.append("]!");
1792
break;
1793
case AddressKindA64::post:
1794
text.append("[");
1795
log(addr.base);
1796
text.append("]!");
1797
if (addr.data != 0)
1798
logAppend(",#%d", addr.data);
1799
break;
1800
}
1801
}
1802
1803
} // namespace A64
1804
} // namespace CodeGen
1805
} // namespace Luau
1806
1807