Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/AssemblyBuilderX64.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/AssemblyBuilderX64.h"
3
4
#include "ByteUtils.h"
5
6
#include <stdarg.h>
7
#include <stdio.h>
8
9
namespace Luau
10
{
11
namespace CodeGen
12
{
13
namespace X64
14
{
15
16
// TODO: more assertions on operand sizes
17
18
static const uint8_t codeForCondition[] = {0x0, 0x1, 0x2, 0x3, 0x2, 0x6, 0x7, 0x3, 0x4, 0xc, 0xe, 0xf, 0xd,
19
0x3, 0x7, 0x6, 0x2, 0x5, 0xd, 0xf, 0xe, 0xc, 0x4, 0x5, 0xa, 0xb};
20
static_assert(sizeof(codeForCondition) / sizeof(codeForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered");
21
22
static const char* jccTextForCondition[] = {"jo", "jno", "jc", "jnc", "jb", "jbe", "ja", "jae", "je", "jl", "jle", "jg", "jge",
23
"jnb", "jnbe", "jna", "jnae", "jne", "jnl", "jnle", "jng", "jnge", "jz", "jnz", "jp", "jnp"};
24
static_assert(sizeof(jccTextForCondition) / sizeof(jccTextForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered");
25
26
static const char* setccTextForCondition[] = {"seto", "setno", "setc", "setnc", "setb", "setbe", "seta", "setae", "sete",
27
"setl", "setle", "setg", "setge", "setnb", "setnbe", "setna", "setnae", "setne",
28
"setnl", "setnle", "setng", "setnge", "setz", "setnz", "setp", "setnp"};
29
static_assert(sizeof(setccTextForCondition) / sizeof(setccTextForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered");
30
31
static const char* cmovTextForCondition[] = {"cmovo", "cmovno", "cmovc", "cmovnc", "cmovb", "cmovbe", "cmova", "cmovae", "cmove",
32
"cmovl", "cmovle", "cmovg", "cmovge", "cmovnb", "cmovnbe", "cmovna", "cmovnae", "cmovne",
33
"cmovnl", "cmovnle", "cmovng", "cmovnge", "cmovz", "cmovnz", "cmovp", "cmovnp"};
34
static_assert(sizeof(cmovTextForCondition) / sizeof(cmovTextForCondition[0]) == size_t(ConditionX64::Count), "all conditions have to be covered");
35
36
#define OP_PLUS_REG(op, reg) ((op) + (reg & 0x7))
37
#define OP_PLUS_CC(op, cc) ((op) + uint8_t(cc))
38
39
#define REX_W_BIT(value) (value ? 0x8 : 0x0)
40
#define REX_W(reg) REX_W_BIT((reg).size == SizeX64::qword)
41
#define REX_FORCE(reg) (((reg).size == SizeX64::byte && (reg).index >= 4) ? 0x40 : 0x00)
42
#define REX_R(reg) (((reg).index & 0x8) >> 1)
43
#define REX_X(reg) (((reg).index & 0x8) >> 2)
44
#define REX_B(reg) (((reg).index & 0x8) >> 3)
45
46
#define AVX_W(value) ((value) ? 0x80 : 0x0)
47
#define AVX_R(reg) ((~(reg).index & 0x8) << 4)
48
#define AVX_X(reg) ((~(reg).index & 0x8) << 3)
49
#define AVX_B(reg) ((~(reg).index & 0x8) << 2)
50
51
#define AVX_3_1() 0b11000100
52
#define AVX_3_2(r, x, b, m) (AVX_R(r) | AVX_X(x) | AVX_B(b) | (m))
53
#define AVX_3_3(w, v, l, p) (AVX_W(w) | ((~(v.index) & 0xf) << 3) | ((l) << 2) | (p))
54
55
#define MOD_RM(mod, reg, rm) (((mod) << 6) | (((reg) & 0x7) << 3) | ((rm) & 0x7))
56
#define SIB(scale, index, base) ((getScaleEncoding(scale) << 6) | (((index) & 0x7) << 3) | ((base) & 0x7))
57
58
const unsigned AVX_0F = 0b0001;
59
[[maybe_unused]] const unsigned AVX_0F38 = 0b0010;
60
[[maybe_unused]] const unsigned AVX_0F3A = 0b0011;
61
62
const unsigned AVX_NP = 0b00;
63
const unsigned AVX_66 = 0b01;
64
const unsigned AVX_F3 = 0b10;
65
const unsigned AVX_F2 = 0b11;
66
67
const unsigned kMaxAlign = 32;
68
const unsigned kMaxInstructionLength = 16;
69
70
const uint8_t kRoundingPrecisionInexact = 0b1000;
71
72
static ABIX64 getCurrentX64ABI()
73
{
74
#if defined(_WIN32)
75
return ABIX64::Windows;
76
#else
77
return ABIX64::SystemV;
78
#endif
79
}
80
81
AssemblyBuilderX64::AssemblyBuilderX64(bool logText, ABIX64 abi, unsigned int features)
82
: logText(logText)
83
, abi(abi)
84
, features(features)
85
, constCache32(~0u)
86
, constCache64(~0ull)
87
{
88
data.resize(4096);
89
dataPos = data.size(); // data is filled backwards
90
91
code.resize(4096);
92
codePos = code.data();
93
codeEnd = code.data() + code.size();
94
}
95
96
AssemblyBuilderX64::AssemblyBuilderX64(bool logText, unsigned int features)
97
: AssemblyBuilderX64(logText, getCurrentX64ABI(), features)
98
{
99
}
100
101
AssemblyBuilderX64::~AssemblyBuilderX64()
102
{
103
CODEGEN_ASSERT(finalized);
104
}
105
106
void AssemblyBuilderX64::add(OperandX64 lhs, OperandX64 rhs)
107
{
108
placeBinary("add", lhs, rhs, 0x80, 0x81, 0x83, 0x00, 0x01, 0x02, 0x03, 0);
109
}
110
111
void AssemblyBuilderX64::sub(OperandX64 lhs, OperandX64 rhs)
112
{
113
placeBinary("sub", lhs, rhs, 0x80, 0x81, 0x83, 0x28, 0x29, 0x2a, 0x2b, 5);
114
}
115
116
void AssemblyBuilderX64::cmp(OperandX64 lhs, OperandX64 rhs)
117
{
118
placeBinary("cmp", lhs, rhs, 0x80, 0x81, 0x83, 0x38, 0x39, 0x3a, 0x3b, 7);
119
}
120
121
void AssemblyBuilderX64::and_(OperandX64 lhs, OperandX64 rhs)
122
{
123
placeBinary("and", lhs, rhs, 0x80, 0x81, 0x83, 0x20, 0x21, 0x22, 0x23, 4);
124
}
125
126
void AssemblyBuilderX64::or_(OperandX64 lhs, OperandX64 rhs)
127
{
128
placeBinary("or", lhs, rhs, 0x80, 0x81, 0x83, 0x08, 0x09, 0x0a, 0x0b, 1);
129
}
130
131
void AssemblyBuilderX64::xor_(OperandX64 lhs, OperandX64 rhs)
132
{
133
placeBinary("xor", lhs, rhs, 0x80, 0x81, 0x83, 0x30, 0x31, 0x32, 0x33, 6);
134
}
135
136
void AssemblyBuilderX64::sal(OperandX64 lhs, OperandX64 rhs)
137
{
138
placeShift("sal", lhs, rhs, 4);
139
}
140
141
void AssemblyBuilderX64::sar(OperandX64 lhs, OperandX64 rhs)
142
{
143
placeShift("sar", lhs, rhs, 7);
144
}
145
146
void AssemblyBuilderX64::shl(OperandX64 lhs, OperandX64 rhs)
147
{
148
placeShift("shl", lhs, rhs, 4); // same as sal
149
}
150
151
void AssemblyBuilderX64::shr(OperandX64 lhs, OperandX64 rhs)
152
{
153
placeShift("shr", lhs, rhs, 5);
154
}
155
156
void AssemblyBuilderX64::rol(OperandX64 lhs, OperandX64 rhs)
157
{
158
placeShift("rol", lhs, rhs, 0);
159
}
160
161
void AssemblyBuilderX64::ror(OperandX64 lhs, OperandX64 rhs)
162
{
163
placeShift("ror", lhs, rhs, 1);
164
}
165
166
void AssemblyBuilderX64::mov(OperandX64 lhs, OperandX64 rhs)
167
{
168
if (logText)
169
log("mov", lhs, rhs);
170
171
if (lhs.cat == CategoryX64::reg && rhs.cat == CategoryX64::imm)
172
{
173
SizeX64 size = lhs.base.size;
174
175
placeRex(lhs.base);
176
177
if (size == SizeX64::byte)
178
{
179
place(OP_PLUS_REG(0xb0, lhs.base.index));
180
placeImm8(rhs.imm);
181
}
182
else if (size == SizeX64::word)
183
{
184
place(0x66);
185
place(OP_PLUS_REG(0xb8, lhs.base.index));
186
placeImm16(rhs.imm);
187
}
188
else if (size == SizeX64::dword)
189
{
190
place(OP_PLUS_REG(0xb8, lhs.base.index));
191
placeImm32(rhs.imm);
192
}
193
else
194
{
195
CODEGEN_ASSERT(size == SizeX64::qword);
196
197
place(OP_PLUS_REG(0xb8, lhs.base.index));
198
placeImm64(rhs.imm);
199
}
200
}
201
else if (lhs.cat == CategoryX64::mem && rhs.cat == CategoryX64::imm)
202
{
203
SizeX64 size = lhs.memSize;
204
205
placeRex(lhs);
206
207
if (size == SizeX64::byte)
208
{
209
place(0xc6);
210
placeModRegMem(lhs, 0, /*extraCodeBytes=*/1);
211
placeImm8(rhs.imm);
212
}
213
else if (size == SizeX64::word)
214
{
215
place(0x66);
216
place(0xc7);
217
placeModRegMem(lhs, 0, /*extraCodeBytes=*/2);
218
placeImm16(rhs.imm);
219
}
220
else
221
{
222
CODEGEN_ASSERT(size == SizeX64::dword || size == SizeX64::qword);
223
224
place(0xc7);
225
placeModRegMem(lhs, 0, /*extraCodeBytes=*/4);
226
placeImm32(rhs.imm);
227
}
228
}
229
else if (lhs.cat == CategoryX64::reg && (rhs.cat == CategoryX64::reg || rhs.cat == CategoryX64::mem))
230
{
231
placeBinaryRegAndRegMem(lhs, rhs, 0x8a, 0x8b);
232
}
233
else if (lhs.cat == CategoryX64::mem && rhs.cat == CategoryX64::reg)
234
{
235
placeBinaryRegMemAndReg(lhs, rhs, 0x88, 0x89);
236
}
237
else
238
{
239
CODEGEN_ASSERT(!"No encoding for this operand combination");
240
}
241
242
commit();
243
}
244
245
void AssemblyBuilderX64::mov64(RegisterX64 lhs, int64_t imm)
246
{
247
if (logText)
248
{
249
text.append(" mov ");
250
log(lhs);
251
logAppend(",%llXh\n", (unsigned long long)imm);
252
}
253
254
CODEGEN_ASSERT(lhs.size == SizeX64::qword);
255
256
placeRex(lhs);
257
place(OP_PLUS_REG(0xb8, lhs.index));
258
placeImm64(imm);
259
commit();
260
}
261
262
void AssemblyBuilderX64::movsx(RegisterX64 lhs, OperandX64 rhs)
263
{
264
if (logText)
265
log("movsx", lhs, rhs);
266
267
SizeX64 size = rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize;
268
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::word);
269
270
placeRex(lhs, rhs);
271
place(0x0f);
272
place(size == SizeX64::byte ? 0xbe : 0xbf);
273
placeRegAndModRegMem(lhs, rhs);
274
commit();
275
}
276
277
void AssemblyBuilderX64::movzx(RegisterX64 lhs, OperandX64 rhs)
278
{
279
if (logText)
280
log("movzx", lhs, rhs);
281
282
SizeX64 size = rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize;
283
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::word);
284
285
placeRex(lhs, rhs);
286
place(0x0f);
287
place(size == SizeX64::byte ? 0xb6 : 0xb7);
288
placeRegAndModRegMem(lhs, rhs);
289
commit();
290
}
291
292
void AssemblyBuilderX64::div(OperandX64 op)
293
{
294
placeUnaryModRegMem("div", op, 0xf6, 0xf7, 6);
295
}
296
297
void AssemblyBuilderX64::idiv(OperandX64 op)
298
{
299
placeUnaryModRegMem("idiv", op, 0xf6, 0xf7, 7);
300
}
301
302
void AssemblyBuilderX64::mul(OperandX64 op)
303
{
304
placeUnaryModRegMem("mul", op, 0xf6, 0xf7, 4);
305
}
306
307
void AssemblyBuilderX64::imul(OperandX64 op)
308
{
309
placeUnaryModRegMem("imul", op, 0xf6, 0xf7, 5);
310
}
311
312
void AssemblyBuilderX64::neg(OperandX64 op)
313
{
314
placeUnaryModRegMem("neg", op, 0xf6, 0xf7, 3);
315
}
316
317
void AssemblyBuilderX64::not_(OperandX64 op)
318
{
319
placeUnaryModRegMem("not", op, 0xf6, 0xf7, 2);
320
}
321
322
void AssemblyBuilderX64::dec(OperandX64 op)
323
{
324
placeUnaryModRegMem("dec", op, 0xfe, 0xff, 1);
325
}
326
327
void AssemblyBuilderX64::inc(OperandX64 op)
328
{
329
placeUnaryModRegMem("inc", op, 0xfe, 0xff, 0);
330
}
331
332
void AssemblyBuilderX64::imul(OperandX64 lhs, OperandX64 rhs)
333
{
334
if (logText)
335
log("imul", lhs, rhs);
336
337
placeRex(lhs.base, rhs);
338
place(0x0f);
339
place(0xaf);
340
placeRegAndModRegMem(lhs, rhs);
341
commit();
342
}
343
344
void AssemblyBuilderX64::imul(OperandX64 dst, OperandX64 lhs, int32_t rhs)
345
{
346
if (logText)
347
log("imul", dst, lhs, rhs);
348
349
placeRex(dst.base, lhs);
350
351
if (int8_t(rhs) == rhs)
352
{
353
place(0x6b);
354
placeRegAndModRegMem(dst, lhs, /*extraCodeBytes=*/1);
355
placeImm8(rhs);
356
}
357
else
358
{
359
place(0x69);
360
placeRegAndModRegMem(dst, lhs, /*extraCodeBytes=*/4);
361
placeImm32(rhs);
362
}
363
364
commit();
365
}
366
367
void AssemblyBuilderX64::test(OperandX64 lhs, OperandX64 rhs)
368
{
369
// No forms for r/m*, imm8 and reg, r/m*
370
placeBinary("test", lhs, rhs, 0xf6, 0xf7, 0xf7, 0x84, 0x85, 0x84, 0x85, 0);
371
}
372
373
void AssemblyBuilderX64::lea(OperandX64 lhs, OperandX64 rhs)
374
{
375
if (logText)
376
log("lea", lhs, rhs);
377
378
CODEGEN_ASSERT(lhs.cat == CategoryX64::reg && rhs.cat == CategoryX64::mem && rhs.memSize == SizeX64::none);
379
CODEGEN_ASSERT(rhs.base == rip || rhs.base.size == lhs.base.size);
380
CODEGEN_ASSERT(rhs.index == noreg || rhs.index.size == lhs.base.size);
381
rhs.memSize = lhs.base.size;
382
placeBinaryRegAndRegMem(lhs, rhs, 0x8d, 0x8d);
383
}
384
385
void AssemblyBuilderX64::push(OperandX64 op)
386
{
387
if (logText)
388
log("push", op);
389
390
CODEGEN_ASSERT(op.cat == CategoryX64::reg && op.base.size == SizeX64::qword);
391
placeRex(op.base);
392
place(OP_PLUS_REG(0x50, op.base.index));
393
commit();
394
}
395
396
void AssemblyBuilderX64::pop(OperandX64 op)
397
{
398
if (logText)
399
log("pop", op);
400
401
CODEGEN_ASSERT(op.cat == CategoryX64::reg && op.base.size == SizeX64::qword);
402
placeRex(op.base);
403
place(OP_PLUS_REG(0x58, op.base.index));
404
commit();
405
}
406
407
void AssemblyBuilderX64::ret()
408
{
409
if (logText)
410
log("ret");
411
412
place(0xc3);
413
commit();
414
}
415
416
void AssemblyBuilderX64::setcc(ConditionX64 cond, OperandX64 op)
417
{
418
SizeX64 size = op.cat == CategoryX64::reg ? op.base.size : op.memSize;
419
CODEGEN_ASSERT(size == SizeX64::byte);
420
421
if (logText)
422
log(setccTextForCondition[size_t(cond)], op);
423
424
placeRex(op);
425
place(0x0f);
426
place(0x90 | codeForCondition[size_t(cond)]);
427
placeModRegMem(op, 0);
428
commit();
429
}
430
431
void AssemblyBuilderX64::cmov(ConditionX64 cond, RegisterX64 lhs, OperandX64 rhs)
432
{
433
SizeX64 size = rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize;
434
CODEGEN_ASSERT(size != SizeX64::byte && size == lhs.size);
435
436
if (logText)
437
log(cmovTextForCondition[size_t(cond)], lhs, rhs);
438
placeRex(lhs, rhs);
439
place(0x0f);
440
place(0x40 | codeForCondition[size_t(cond)]);
441
placeRegAndModRegMem(lhs, rhs);
442
commit();
443
}
444
445
void AssemblyBuilderX64::jcc(ConditionX64 cond, Label& label)
446
{
447
placeJcc(jccTextForCondition[size_t(cond)], label, codeForCondition[size_t(cond)]);
448
}
449
450
void AssemblyBuilderX64::jmp(Label& label)
451
{
452
place(0xe9);
453
placeLabel(label);
454
455
if (logText)
456
log("jmp", label);
457
458
commit();
459
}
460
461
void AssemblyBuilderX64::jmp(OperandX64 op)
462
{
463
CODEGEN_ASSERT((op.cat == CategoryX64::reg ? op.base.size : op.memSize) == SizeX64::qword);
464
465
if (logText)
466
log("jmp", op);
467
468
// Indirect absolute calls always work in 64 bit width mode, so REX.W is optional
469
// While we could keep an optional prefix, in Windows x64 ABI it signals a tail call return statement to the unwinder
470
placeRexNoW(op);
471
472
place(0xff);
473
placeModRegMem(op, 4);
474
commit();
475
}
476
477
void AssemblyBuilderX64::call(Label& label)
478
{
479
place(0xe8);
480
placeLabel(label);
481
482
if (logText)
483
log("call", label);
484
485
commit();
486
}
487
488
void AssemblyBuilderX64::call(OperandX64 op)
489
{
490
CODEGEN_ASSERT((op.cat == CategoryX64::reg ? op.base.size : op.memSize) == SizeX64::qword);
491
492
if (logText)
493
log("call", op);
494
495
// Indirect absolute calls always work in 64 bit width mode, so REX.W is optional
496
placeRexNoW(op);
497
498
place(0xff);
499
placeModRegMem(op, 2);
500
commit();
501
}
502
503
void AssemblyBuilderX64::lea(RegisterX64 lhs, Label& label)
504
{
505
CODEGEN_ASSERT(lhs.size == SizeX64::qword);
506
507
placeBinaryRegAndRegMem(lhs, OperandX64(SizeX64::qword, noreg, 1, rip, 0), 0x8d, 0x8d);
508
509
codePos -= 4;
510
placeLabel(label);
511
commit();
512
513
if (logText)
514
log("lea", lhs, label);
515
}
516
517
void AssemblyBuilderX64::int3()
518
{
519
if (logText)
520
log("int3");
521
522
place(0xcc);
523
commit();
524
}
525
526
void AssemblyBuilderX64::ud2()
527
{
528
if (logText)
529
log("ud2");
530
531
place(0x0f);
532
place(0x0b);
533
}
534
535
void AssemblyBuilderX64::bsr(RegisterX64 dst, OperandX64 src)
536
{
537
if (logText)
538
log("bsr", dst, src);
539
540
CODEGEN_ASSERT(dst.size == SizeX64::dword || dst.size == SizeX64::qword);
541
542
placeRex(dst, src);
543
place(0x0f);
544
place(0xbd);
545
placeRegAndModRegMem(dst, src);
546
commit();
547
}
548
549
void AssemblyBuilderX64::bsf(RegisterX64 dst, OperandX64 src)
550
{
551
if (logText)
552
log("bsf", dst, src);
553
554
CODEGEN_ASSERT(dst.size == SizeX64::dword || dst.size == SizeX64::qword);
555
556
placeRex(dst, src);
557
place(0x0f);
558
place(0xbc);
559
placeRegAndModRegMem(dst, src);
560
commit();
561
}
562
563
void AssemblyBuilderX64::bswap(RegisterX64 dst)
564
{
565
if (logText)
566
log("bswap", dst);
567
568
CODEGEN_ASSERT(dst.size == SizeX64::dword || dst.size == SizeX64::qword);
569
570
placeRex(dst);
571
place(0x0f);
572
place(OP_PLUS_REG(0xc8, dst.index));
573
commit();
574
}
575
576
void AssemblyBuilderX64::nop(uint32_t length)
577
{
578
while (length != 0)
579
{
580
uint32_t step = length > 9 ? 9 : length;
581
length -= step;
582
583
switch (step)
584
{
585
case 1:
586
if (logText)
587
logAppend(" nop\n");
588
place(0x90);
589
break;
590
case 2:
591
if (logText)
592
logAppend(" xchg ax, ax ; %u-byte nop\n", step);
593
place(0x66);
594
place(0x90);
595
break;
596
case 3:
597
if (logText)
598
logAppend(" nop dword ptr[rax] ; %u-byte nop\n", step);
599
place(0x0f);
600
place(0x1f);
601
place(0x00);
602
break;
603
case 4:
604
if (logText)
605
logAppend(" nop dword ptr[rax] ; %u-byte nop\n", step);
606
place(0x0f);
607
place(0x1f);
608
place(0x40);
609
place(0x00);
610
break;
611
case 5:
612
if (logText)
613
logAppend(" nop dword ptr[rax+rax] ; %u-byte nop\n", step);
614
place(0x0f);
615
place(0x1f);
616
place(0x44);
617
place(0x00);
618
place(0x00);
619
break;
620
case 6:
621
if (logText)
622
logAppend(" nop word ptr[rax+rax] ; %u-byte nop\n", step);
623
place(0x66);
624
place(0x0f);
625
place(0x1f);
626
place(0x44);
627
place(0x00);
628
place(0x00);
629
break;
630
case 7:
631
if (logText)
632
logAppend(" nop dword ptr[rax] ; %u-byte nop\n", step);
633
place(0x0f);
634
place(0x1f);
635
place(0x80);
636
place(0x00);
637
place(0x00);
638
place(0x00);
639
place(0x00);
640
break;
641
case 8:
642
if (logText)
643
logAppend(" nop dword ptr[rax+rax] ; %u-byte nop\n", step);
644
place(0x0f);
645
place(0x1f);
646
place(0x84);
647
place(0x00);
648
place(0x00);
649
place(0x00);
650
place(0x00);
651
place(0x00);
652
break;
653
case 9:
654
if (logText)
655
logAppend(" nop word ptr[rax+rax] ; %u-byte nop\n", step);
656
place(0x66);
657
place(0x0f);
658
place(0x1f);
659
place(0x84);
660
place(0x00);
661
place(0x00);
662
place(0x00);
663
place(0x00);
664
place(0x00);
665
break;
666
}
667
668
commit();
669
}
670
}
671
672
void AssemblyBuilderX64::align(uint32_t alignment, AlignmentDataX64 data)
673
{
674
CODEGEN_ASSERT((alignment & (alignment - 1)) == 0);
675
676
uint32_t size = getCodeSize();
677
uint32_t pad = ((size + alignment - 1) & ~(alignment - 1)) - size;
678
679
switch (data)
680
{
681
case AlignmentDataX64::Nop:
682
if (logText)
683
logAppend("; align %u\n", alignment);
684
685
nop(pad);
686
break;
687
case AlignmentDataX64::Int3:
688
if (logText)
689
logAppend("; align %u using int3\n", alignment);
690
691
while (codePos + pad > codeEnd)
692
extend();
693
694
for (uint32_t i = 0; i < pad; ++i)
695
place(0xcc);
696
697
commit();
698
break;
699
case AlignmentDataX64::Ud2:
700
if (logText)
701
logAppend("; align %u using ud2\n", alignment);
702
703
while (codePos + pad > codeEnd)
704
extend();
705
706
uint32_t i = 0;
707
708
for (; i + 1 < pad; i += 2)
709
{
710
place(0x0f);
711
place(0x0b);
712
}
713
714
if (i < pad)
715
place(0xcc);
716
717
commit();
718
break;
719
}
720
}
721
722
void AssemblyBuilderX64::vaddpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
723
{
724
placeAvx("vaddpd", dst, src1, src2, 0x58, false, AVX_0F, AVX_66);
725
}
726
727
void AssemblyBuilderX64::vaddps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
728
{
729
placeAvx("vaddps", dst, src1, src2, 0x58, false, AVX_0F, AVX_NP);
730
}
731
732
void AssemblyBuilderX64::vaddsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
733
{
734
placeAvx("vaddsd", dst, src1, src2, 0x58, false, AVX_0F, AVX_F2);
735
}
736
737
void AssemblyBuilderX64::vaddss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
738
{
739
placeAvx("vaddss", dst, src1, src2, 0x58, false, AVX_0F, AVX_F3);
740
}
741
742
void AssemblyBuilderX64::vsubsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
743
{
744
placeAvx("vsubsd", dst, src1, src2, 0x5c, false, AVX_0F, AVX_F2);
745
}
746
747
void AssemblyBuilderX64::vsubss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
748
{
749
placeAvx("vsubss", dst, src1, src2, 0x5c, false, AVX_0F, AVX_F3);
750
}
751
752
void AssemblyBuilderX64::vsubps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
753
{
754
placeAvx("vsubps", dst, src1, src2, 0x5c, false, AVX_0F, AVX_NP);
755
}
756
757
void AssemblyBuilderX64::vmulsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
758
{
759
placeAvx("vmulsd", dst, src1, src2, 0x59, false, AVX_0F, AVX_F2);
760
}
761
762
void AssemblyBuilderX64::vmulss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
763
{
764
placeAvx("vmulss", dst, src1, src2, 0x59, false, AVX_0F, AVX_F3);
765
}
766
767
void AssemblyBuilderX64::vmulps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
768
{
769
placeAvx("vmulps", dst, src1, src2, 0x59, false, AVX_0F, AVX_NP);
770
}
771
772
void AssemblyBuilderX64::vdivsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
773
{
774
placeAvx("vdivsd", dst, src1, src2, 0x5e, false, AVX_0F, AVX_F2);
775
}
776
777
void AssemblyBuilderX64::vdivss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
778
{
779
placeAvx("vdivss", dst, src1, src2, 0x5e, false, AVX_0F, AVX_F3);
780
}
781
782
void AssemblyBuilderX64::vdivps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
783
{
784
placeAvx("vdivps", dst, src1, src2, 0x5e, false, AVX_0F, AVX_NP);
785
}
786
787
void AssemblyBuilderX64::vandps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
788
{
789
placeAvx("vandps", dst, src1, src2, 0x54, false, AVX_0F, AVX_NP);
790
}
791
792
void AssemblyBuilderX64::vandpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
793
{
794
placeAvx("vandpd", dst, src1, src2, 0x54, false, AVX_0F, AVX_66);
795
}
796
797
void AssemblyBuilderX64::vandnpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
798
{
799
placeAvx("vandnpd", dst, src1, src2, 0x55, false, AVX_0F, AVX_66);
800
}
801
802
void AssemblyBuilderX64::vxorps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
803
{
804
placeAvx("vxorps", dst, src1, src2, 0x57, false, AVX_0F, AVX_NP);
805
}
806
807
void AssemblyBuilderX64::vxorpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
808
{
809
placeAvx("vxorpd", dst, src1, src2, 0x57, false, AVX_0F, AVX_66);
810
}
811
812
void AssemblyBuilderX64::vorps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
813
{
814
placeAvx("vorps", dst, src1, src2, 0x56, false, AVX_0F, AVX_NP);
815
}
816
817
void AssemblyBuilderX64::vorpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
818
{
819
placeAvx("vorpd", dst, src1, src2, 0x56, false, AVX_0F, AVX_66);
820
}
821
822
void AssemblyBuilderX64::vucomisd(OperandX64 src1, OperandX64 src2)
823
{
824
placeAvx("vucomisd", src1, src2, 0x2e, false, AVX_0F, AVX_66);
825
}
826
827
void AssemblyBuilderX64::vucomiss(OperandX64 src1, OperandX64 src2)
828
{
829
placeAvx("vucomiss", src1, src2, 0x2e, false, AVX_0F, AVX_NP);
830
}
831
832
void AssemblyBuilderX64::vcvttsd2si(OperandX64 dst, OperandX64 src)
833
{
834
placeAvx("vcvttsd2si", dst, src, 0x2c, dst.base.size == SizeX64::qword, AVX_0F, AVX_F2);
835
}
836
837
void AssemblyBuilderX64::vcvtsi2sd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
838
{
839
placeAvx("vcvtsi2sd", dst, src1, src2, 0x2a, (src2.cat == CategoryX64::reg ? src2.base.size : src2.memSize) == SizeX64::qword, AVX_0F, AVX_F2);
840
}
841
842
void AssemblyBuilderX64::vcvtsi2ss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
843
{
844
placeAvx("vcvtsi2ss", dst, src1, src2, 0x2a, (src2.cat == CategoryX64::reg ? src2.base.size : src2.memSize) == SizeX64::qword, AVX_0F, AVX_F3);
845
}
846
847
void AssemblyBuilderX64::vcvtsd2ss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
848
{
849
if (src2.cat == CategoryX64::reg)
850
CODEGEN_ASSERT(src2.base.size == SizeX64::xmmword);
851
else
852
CODEGEN_ASSERT(src2.memSize == SizeX64::qword);
853
854
placeAvx("vcvtsd2ss", dst, src1, src2, 0x5a, (src2.cat == CategoryX64::reg ? src2.base.size : src2.memSize) == SizeX64::qword, AVX_0F, AVX_F2);
855
}
856
857
void AssemblyBuilderX64::vcvtss2sd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
858
{
859
if (src2.cat == CategoryX64::reg)
860
CODEGEN_ASSERT(src2.base.size == SizeX64::xmmword);
861
else
862
CODEGEN_ASSERT(src2.memSize == SizeX64::dword);
863
864
placeAvx("vcvtss2sd", dst, src1, src2, 0x5a, false, AVX_0F, AVX_F3);
865
}
866
867
void AssemblyBuilderX64::vroundsd(OperandX64 dst, OperandX64 src1, OperandX64 src2, RoundingModeX64 roundingMode)
868
{
869
placeAvx("vroundsd", dst, src1, src2, uint8_t(roundingMode) | kRoundingPrecisionInexact, 0x0b, false, AVX_0F3A, AVX_66);
870
}
871
872
void AssemblyBuilderX64::vroundss(OperandX64 dst, OperandX64 src1, OperandX64 src2, RoundingModeX64 roundingMode)
873
{
874
placeAvx("vroundss", dst, src1, src2, uint8_t(roundingMode) | kRoundingPrecisionInexact, 0x0a, false, AVX_0F3A, AVX_66);
875
}
876
877
void AssemblyBuilderX64::vroundps(OperandX64 dst, OperandX64 src, RoundingModeX64 roundingMode)
878
{
879
// 'placeAvx' wrapper doesn't have an overload for this archetype (opcode r/m, reg, imm8)
880
if (logText)
881
log("vroundps", dst, src, uint8_t(roundingMode) | kRoundingPrecisionInexact);
882
883
placeVex(dst, noreg, src, false, AVX_0F3A, AVX_66);
884
place(0x08);
885
placeRegAndModRegMem(dst, src, /*extraCodeBytes=*/1);
886
placeImm8(uint8_t(roundingMode) | kRoundingPrecisionInexact);
887
888
commit();
889
}
890
891
void AssemblyBuilderX64::vsqrtpd(OperandX64 dst, OperandX64 src)
892
{
893
placeAvx("vsqrtpd", dst, src, 0x51, false, AVX_0F, AVX_66);
894
}
895
896
void AssemblyBuilderX64::vsqrtps(OperandX64 dst, OperandX64 src)
897
{
898
placeAvx("vsqrtps", dst, src, 0x51, false, AVX_0F, AVX_NP);
899
}
900
901
void AssemblyBuilderX64::vsqrtsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
902
{
903
placeAvx("vsqrtsd", dst, src1, src2, 0x51, false, AVX_0F, AVX_F2);
904
}
905
906
void AssemblyBuilderX64::vsqrtss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
907
{
908
placeAvx("vsqrtss", dst, src1, src2, 0x51, false, AVX_0F, AVX_F3);
909
}
910
911
void AssemblyBuilderX64::vmovsd(OperandX64 dst, OperandX64 src)
912
{
913
placeAvx("vmovsd", dst, src, 0x10, 0x11, false, AVX_0F, AVX_F2);
914
}
915
916
void AssemblyBuilderX64::vmovsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
917
{
918
placeAvx("vmovsd", dst, src1, src2, 0x10, false, AVX_0F, AVX_F2);
919
}
920
921
void AssemblyBuilderX64::vmovss(OperandX64 dst, OperandX64 src)
922
{
923
placeAvx("vmovss", dst, src, 0x10, 0x11, false, AVX_0F, AVX_F3);
924
}
925
926
void AssemblyBuilderX64::vmovss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
927
{
928
placeAvx("vmovss", dst, src1, src2, 0x10, false, AVX_0F, AVX_F3);
929
}
930
931
void AssemblyBuilderX64::vmovapd(OperandX64 dst, OperandX64 src)
932
{
933
placeAvx("vmovapd", dst, src, 0x28, 0x29, false, AVX_0F, AVX_66);
934
}
935
936
void AssemblyBuilderX64::vmovaps(OperandX64 dst, OperandX64 src)
937
{
938
placeAvx("vmovaps", dst, src, 0x28, 0x29, false, AVX_0F, AVX_NP);
939
}
940
941
void AssemblyBuilderX64::vmovupd(OperandX64 dst, OperandX64 src)
942
{
943
placeAvx("vmovupd", dst, src, 0x10, 0x11, false, AVX_0F, AVX_66);
944
}
945
946
void AssemblyBuilderX64::vmovups(OperandX64 dst, OperandX64 src)
947
{
948
placeAvx("vmovups", dst, src, 0x10, 0x11, false, AVX_0F, AVX_NP);
949
}
950
951
void AssemblyBuilderX64::vmovq(OperandX64 dst, OperandX64 src)
952
{
953
if (dst.base.size == SizeX64::xmmword)
954
{
955
CODEGEN_ASSERT(dst.cat == CategoryX64::reg);
956
CODEGEN_ASSERT(src.base.size == SizeX64::qword);
957
placeAvx("vmovq", dst, src, 0x6e, true, AVX_0F, AVX_66);
958
}
959
else if (dst.base.size == SizeX64::qword)
960
{
961
CODEGEN_ASSERT(src.cat == CategoryX64::reg);
962
CODEGEN_ASSERT(src.base.size == SizeX64::xmmword);
963
placeAvx("vmovq", src, dst, 0x7e, true, AVX_0F, AVX_66);
964
}
965
else
966
{
967
CODEGEN_ASSERT(!"No encoding for left operand of this category");
968
}
969
}
970
971
void AssemblyBuilderX64::vmaxps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
972
{
973
placeAvx("vmaxps", dst, src1, src2, 0x5f, false, AVX_0F, AVX_NP);
974
}
975
976
void AssemblyBuilderX64::vmaxsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
977
{
978
placeAvx("vmaxsd", dst, src1, src2, 0x5f, false, AVX_0F, AVX_F2);
979
}
980
981
void AssemblyBuilderX64::vmaxss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
982
{
983
placeAvx("vmaxss", dst, src1, src2, 0x5f, false, AVX_0F, AVX_F3);
984
}
985
986
void AssemblyBuilderX64::vminps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
987
{
988
placeAvx("vminps", dst, src1, src2, 0x5d, false, AVX_0F, AVX_NP);
989
}
990
991
void AssemblyBuilderX64::vminsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
992
{
993
placeAvx("vminsd", dst, src1, src2, 0x5d, false, AVX_0F, AVX_F2);
994
}
995
996
void AssemblyBuilderX64::vminss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
997
{
998
placeAvx("vminss", dst, src1, src2, 0x5d, false, AVX_0F, AVX_F3);
999
}
1000
1001
void AssemblyBuilderX64::vcmpeqsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
1002
{
1003
placeAvx("vcmpeqsd", dst, src1, src2, 0x00, 0xc2, false, AVX_0F, AVX_F2);
1004
}
1005
1006
void AssemblyBuilderX64::vcmpltsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
1007
{
1008
placeAvx("vcmpltsd", dst, src1, src2, 0x01, 0xc2, false, AVX_0F, AVX_F2);
1009
}
1010
1011
void AssemblyBuilderX64::vcmpltss(OperandX64 dst, OperandX64 src1, OperandX64 src2)
1012
{
1013
placeAvx("vcmpltss", dst, src1, src2, 0x01, 0xc2, false, AVX_0F, AVX_F3);
1014
}
1015
1016
void AssemblyBuilderX64::vcmpeqps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
1017
{
1018
placeAvx("vcmpeqps", dst, src1, src2, 0x00, 0xc2, false, AVX_0F, AVX_NP);
1019
}
1020
1021
void AssemblyBuilderX64::vblendvps(RegisterX64 dst, RegisterX64 src1, OperandX64 src2, RegisterX64 mask)
1022
{
1023
// bits [7:4] of imm8 are used to select register for operand 4
1024
placeAvx("vblendvps", dst, src1, src2, mask.index << 4, 0x4a, false, AVX_0F3A, AVX_66);
1025
}
1026
1027
void AssemblyBuilderX64::vblendvpd(RegisterX64 dst, RegisterX64 src1, OperandX64 src2, RegisterX64 mask)
1028
{
1029
// bits [7:4] of imm8 are used to select register for operand 4
1030
placeAvx("vblendvpd", dst, src1, src2, mask.index << 4, 0x4b, false, AVX_0F3A, AVX_66);
1031
}
1032
1033
void AssemblyBuilderX64::vpshufps(RegisterX64 dst, RegisterX64 src1, OperandX64 src2, uint8_t shuffle)
1034
{
1035
placeAvx("vpshufps", dst, src1, src2, shuffle, 0xc6, false, AVX_0F, AVX_NP);
1036
}
1037
1038
void AssemblyBuilderX64::vpinsrd(RegisterX64 dst, RegisterX64 src1, OperandX64 src2, uint8_t offset)
1039
{
1040
placeAvx("vpinsrd", dst, src1, src2, offset, 0x22, false, AVX_0F3A, AVX_66);
1041
}
1042
1043
void AssemblyBuilderX64::vpextrd(RegisterX64 dst, RegisterX64 src, uint8_t offset)
1044
{
1045
// 'placeAvx' wrapper doesn't have an overload for this archetype (opcode r/m, reg, imm8)
1046
if (logText)
1047
log("vpextrd", dst, src, offset);
1048
1049
placeVex(src, noreg, dst, false, AVX_0F3A, AVX_66);
1050
place(0x16);
1051
placeRegAndModRegMem(src, dst, /*extraCodeBytes=*/1);
1052
placeImm8(offset);
1053
1054
commit();
1055
}
1056
1057
void AssemblyBuilderX64::vdpps(OperandX64 dst, OperandX64 src1, OperandX64 src2, uint8_t mask)
1058
{
1059
placeAvx("vdpps", dst, src1, src2, mask, 0x40, false, AVX_0F3A, AVX_66);
1060
}
1061
1062
void AssemblyBuilderX64::vfmadd213ps(OperandX64 dst, OperandX64 src1, OperandX64 src2)
1063
{
1064
placeAvx("vfmadd213ps", dst, src1, src2, 0xA8, false, AVX_0F38, AVX_66);
1065
}
1066
1067
void AssemblyBuilderX64::vfmadd213pd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
1068
{
1069
placeAvx("vfmadd213pd", dst, src1, src2, 0xA8, true, AVX_0F38, AVX_66);
1070
}
1071
1072
bool AssemblyBuilderX64::finalize()
1073
{
1074
code.resize(codePos - code.data());
1075
1076
// Resolve jump targets
1077
for (Label fixup : pendingLabels)
1078
{
1079
// If this assertion fires, a label was used in jmp without calling setLabel
1080
CODEGEN_ASSERT(labelLocations[fixup.id - 1] != ~0u);
1081
uint32_t value = labelLocations[fixup.id - 1] - (fixup.location + 4);
1082
writeu32(&code[fixup.location], value);
1083
}
1084
1085
size_t dataSize = data.size() - dataPos;
1086
1087
// Shrink data
1088
if (dataSize > 0)
1089
memmove(&data[0], &data[dataPos], dataSize);
1090
1091
data.resize(dataSize);
1092
1093
finalized = true;
1094
1095
return true;
1096
}
1097
1098
Label AssemblyBuilderX64::setLabel()
1099
{
1100
Label label{nextLabel++, getCodeSize()};
1101
labelLocations.push_back(~0u);
1102
1103
if (logText)
1104
log(label);
1105
1106
return label;
1107
}
1108
1109
void AssemblyBuilderX64::setLabel(Label& label)
1110
{
1111
if (label.id == 0)
1112
{
1113
label.id = nextLabel++;
1114
labelLocations.push_back(~0u);
1115
}
1116
1117
label.location = getCodeSize();
1118
labelLocations[label.id - 1] = label.location;
1119
1120
if (logText)
1121
log(label);
1122
}
1123
1124
OperandX64 AssemblyBuilderX64::i32(int32_t value)
1125
{
1126
uint32_t as32BitKey = value;
1127
1128
if (as32BitKey != ~0u)
1129
{
1130
if (int32_t* prev = constCache32.find(as32BitKey))
1131
return OperandX64(SizeX64::dword, noreg, 1, rip, *prev);
1132
}
1133
1134
size_t pos = allocateData(4, 4);
1135
writeu32(&data[pos], value);
1136
int32_t offset = int32_t(pos - data.size());
1137
1138
if (as32BitKey != ~0u)
1139
constCache32[as32BitKey] = offset;
1140
1141
return OperandX64(SizeX64::dword, noreg, 1, rip, offset);
1142
}
1143
1144
OperandX64 AssemblyBuilderX64::i64(int64_t value)
1145
{
1146
uint64_t as64BitKey = value;
1147
1148
if (as64BitKey != ~0ull)
1149
{
1150
if (int32_t* prev = constCache64.find(as64BitKey))
1151
return OperandX64(SizeX64::qword, noreg, 1, rip, *prev);
1152
}
1153
1154
size_t pos = allocateData(8, 8);
1155
writeu64(&data[pos], value);
1156
int32_t offset = int32_t(pos - data.size());
1157
1158
if (as64BitKey != ~0ull)
1159
constCache64[as64BitKey] = offset;
1160
1161
return OperandX64(SizeX64::qword, noreg, 1, rip, offset);
1162
}
1163
1164
OperandX64 AssemblyBuilderX64::f32(float value)
1165
{
1166
uint32_t as32BitKey;
1167
static_assert(sizeof(as32BitKey) == sizeof(value), "Expecting float to be 32-bit");
1168
memcpy(&as32BitKey, &value, sizeof(value));
1169
1170
if (as32BitKey != ~0u)
1171
{
1172
if (int32_t* prev = constCache32.find(as32BitKey))
1173
return OperandX64(SizeX64::dword, noreg, 1, rip, *prev);
1174
}
1175
1176
size_t pos = allocateData(4, 4);
1177
writef32(&data[pos], value);
1178
int32_t offset = int32_t(pos - data.size());
1179
1180
if (as32BitKey != ~0u)
1181
constCache32[as32BitKey] = offset;
1182
1183
return OperandX64(SizeX64::dword, noreg, 1, rip, offset);
1184
}
1185
1186
OperandX64 AssemblyBuilderX64::f64(double value)
1187
{
1188
uint64_t as64BitKey;
1189
static_assert(sizeof(as64BitKey) == sizeof(value), "Expecting double to be 64-bit");
1190
memcpy(&as64BitKey, &value, sizeof(value));
1191
1192
if (as64BitKey != ~0ull)
1193
{
1194
if (int32_t* prev = constCache64.find(as64BitKey))
1195
return OperandX64(SizeX64::qword, noreg, 1, rip, *prev);
1196
}
1197
1198
size_t pos = allocateData(8, 8);
1199
writef64(&data[pos], value);
1200
int32_t offset = int32_t(pos - data.size());
1201
1202
if (as64BitKey != ~0ull)
1203
constCache64[as64BitKey] = offset;
1204
1205
return OperandX64(SizeX64::qword, noreg, 1, rip, offset);
1206
}
1207
1208
OperandX64 AssemblyBuilderX64::u32x4(uint32_t x, uint32_t y, uint32_t z, uint32_t w)
1209
{
1210
size_t pos = allocateData(16, 16);
1211
writeu32(&data[pos], x);
1212
writeu32(&data[pos + 4], y);
1213
writeu32(&data[pos + 8], z);
1214
writeu32(&data[pos + 12], w);
1215
return OperandX64(SizeX64::xmmword, noreg, 1, rip, int32_t(pos - data.size()));
1216
}
1217
1218
OperandX64 AssemblyBuilderX64::f32x4(float x, float y, float z, float w)
1219
{
1220
size_t pos = allocateData(16, 16);
1221
writef32(&data[pos], x);
1222
writef32(&data[pos + 4], y);
1223
writef32(&data[pos + 8], z);
1224
writef32(&data[pos + 12], w);
1225
return OperandX64(SizeX64::xmmword, noreg, 1, rip, int32_t(pos - data.size()));
1226
}
1227
1228
OperandX64 AssemblyBuilderX64::f64x2(double x, double y)
1229
{
1230
size_t pos = allocateData(16, 16);
1231
writef64(&data[pos], x);
1232
writef64(&data[pos + 8], y);
1233
return OperandX64(SizeX64::xmmword, noreg, 1, rip, int32_t(pos - data.size()));
1234
}
1235
1236
OperandX64 AssemblyBuilderX64::bytes(const void* ptr, size_t size, size_t align)
1237
{
1238
size_t pos = allocateData(size, align);
1239
memcpy(&data[pos], ptr, size);
1240
return OperandX64(SizeX64::none, noreg, 1, rip, int32_t(pos - data.size()));
1241
}
1242
1243
void AssemblyBuilderX64::logAppend(const char* fmt, ...)
1244
{
1245
char buf[256];
1246
va_list args;
1247
va_start(args, fmt);
1248
vsnprintf(buf, sizeof(buf), fmt, args);
1249
va_end(args);
1250
text.append(buf);
1251
}
1252
1253
uint32_t AssemblyBuilderX64::getCodeSize() const
1254
{
1255
return uint32_t(codePos - code.data());
1256
}
1257
1258
unsigned AssemblyBuilderX64::getInstructionCount() const
1259
{
1260
return instructionCount;
1261
}
1262
1263
void AssemblyBuilderX64::placeBinary(
1264
const char* name,
1265
OperandX64 lhs,
1266
OperandX64 rhs,
1267
uint8_t codeimm8,
1268
uint8_t codeimm,
1269
uint8_t codeimmImm8,
1270
uint8_t code8rev,
1271
uint8_t coderev,
1272
uint8_t code8,
1273
uint8_t code,
1274
uint8_t opreg
1275
)
1276
{
1277
if (logText)
1278
log(name, lhs, rhs);
1279
1280
if ((lhs.cat == CategoryX64::reg || lhs.cat == CategoryX64::mem) && rhs.cat == CategoryX64::imm)
1281
placeBinaryRegMemAndImm(lhs, rhs, codeimm8, codeimm, codeimmImm8, opreg);
1282
else if (lhs.cat == CategoryX64::reg && (rhs.cat == CategoryX64::reg || rhs.cat == CategoryX64::mem))
1283
placeBinaryRegAndRegMem(lhs, rhs, code8, code);
1284
else if (lhs.cat == CategoryX64::mem && rhs.cat == CategoryX64::reg)
1285
placeBinaryRegMemAndReg(lhs, rhs, code8rev, coderev);
1286
else
1287
CODEGEN_ASSERT(!"No encoding for this operand combination");
1288
}
1289
1290
void AssemblyBuilderX64::placeBinaryRegMemAndImm(OperandX64 lhs, OperandX64 rhs, uint8_t code8, uint8_t code, uint8_t codeImm8, uint8_t opreg)
1291
{
1292
CODEGEN_ASSERT(lhs.cat == CategoryX64::reg || lhs.cat == CategoryX64::mem);
1293
CODEGEN_ASSERT(rhs.cat == CategoryX64::imm);
1294
1295
SizeX64 size = lhs.cat == CategoryX64::reg ? lhs.base.size : lhs.memSize;
1296
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::dword || size == SizeX64::qword);
1297
1298
placeRex(lhs);
1299
1300
if (size == SizeX64::byte)
1301
{
1302
place(code8);
1303
placeModRegMem(lhs, opreg, /*extraCodeBytes=*/1);
1304
placeImm8(rhs.imm);
1305
}
1306
else
1307
{
1308
CODEGEN_ASSERT(size == SizeX64::dword || size == SizeX64::qword);
1309
1310
if (int8_t(rhs.imm) == rhs.imm && code != codeImm8)
1311
{
1312
place(codeImm8);
1313
placeModRegMem(lhs, opreg, /*extraCodeBytes=*/1);
1314
placeImm8(rhs.imm);
1315
}
1316
else
1317
{
1318
place(code);
1319
placeModRegMem(lhs, opreg, /*extraCodeBytes=*/4);
1320
placeImm32(rhs.imm);
1321
}
1322
}
1323
1324
commit();
1325
}
1326
1327
void AssemblyBuilderX64::placeBinaryRegAndRegMem(OperandX64 lhs, OperandX64 rhs, uint8_t code8, uint8_t code)
1328
{
1329
CODEGEN_ASSERT(lhs.cat == CategoryX64::reg && (rhs.cat == CategoryX64::reg || rhs.cat == CategoryX64::mem));
1330
CODEGEN_ASSERT(lhs.base.size == (rhs.cat == CategoryX64::reg ? rhs.base.size : rhs.memSize));
1331
1332
SizeX64 size = lhs.base.size;
1333
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::word || size == SizeX64::dword || size == SizeX64::qword);
1334
1335
if (size == SizeX64::word)
1336
place(0x66);
1337
1338
placeRex(lhs.base, rhs);
1339
place(size == SizeX64::byte ? code8 : code);
1340
placeRegAndModRegMem(lhs, rhs);
1341
1342
commit();
1343
}
1344
1345
void AssemblyBuilderX64::placeBinaryRegMemAndReg(OperandX64 lhs, OperandX64 rhs, uint8_t code8, uint8_t code)
1346
{
1347
// In two operand instructions, first operand is always a register, but data flow direction is reversed
1348
placeBinaryRegAndRegMem(rhs, lhs, code8, code);
1349
}
1350
1351
void AssemblyBuilderX64::placeUnaryModRegMem(const char* name, OperandX64 op, uint8_t code8, uint8_t code, uint8_t opreg)
1352
{
1353
if (logText)
1354
log(name, op);
1355
1356
CODEGEN_ASSERT(op.cat == CategoryX64::reg || op.cat == CategoryX64::mem);
1357
1358
SizeX64 size = op.cat == CategoryX64::reg ? op.base.size : op.memSize;
1359
CODEGEN_ASSERT(size == SizeX64::byte || size == SizeX64::dword || size == SizeX64::qword);
1360
1361
placeRex(op);
1362
place(size == SizeX64::byte ? code8 : code);
1363
placeModRegMem(op, opreg);
1364
1365
commit();
1366
}
1367
1368
void AssemblyBuilderX64::placeShift(const char* name, OperandX64 lhs, OperandX64 rhs, uint8_t opreg)
1369
{
1370
if (logText)
1371
log(name, lhs, rhs);
1372
1373
CODEGEN_ASSERT(lhs.cat == CategoryX64::reg || lhs.cat == CategoryX64::mem);
1374
CODEGEN_ASSERT(rhs.cat == CategoryX64::imm || (rhs.cat == CategoryX64::reg && rhs.base == cl));
1375
1376
SizeX64 size = lhs.base.size;
1377
1378
placeRex(lhs.base);
1379
1380
if (rhs.cat == CategoryX64::imm && rhs.imm == 1)
1381
{
1382
place(size == SizeX64::byte ? 0xd0 : 0xd1);
1383
placeModRegMem(lhs, opreg);
1384
}
1385
else if (rhs.cat == CategoryX64::imm)
1386
{
1387
CODEGEN_ASSERT(int8_t(rhs.imm) == rhs.imm);
1388
1389
place(size == SizeX64::byte ? 0xc0 : 0xc1);
1390
placeModRegMem(lhs, opreg, /*extraCodeBytes=*/1);
1391
placeImm8(rhs.imm);
1392
}
1393
else
1394
{
1395
place(size == SizeX64::byte ? 0xd2 : 0xd3);
1396
placeModRegMem(lhs, opreg);
1397
}
1398
1399
commit();
1400
}
1401
1402
void AssemblyBuilderX64::placeJcc(const char* name, Label& label, uint8_t cc)
1403
{
1404
place(0x0f);
1405
place(OP_PLUS_CC(0x80, cc));
1406
placeLabel(label);
1407
1408
if (logText)
1409
log(name, label);
1410
1411
commit();
1412
}
1413
1414
void AssemblyBuilderX64::placeAvx(const char* name, OperandX64 dst, OperandX64 src, uint8_t code, bool setW, uint8_t mode, uint8_t prefix)
1415
{
1416
CODEGEN_ASSERT(dst.cat == CategoryX64::reg);
1417
CODEGEN_ASSERT(src.cat == CategoryX64::reg || src.cat == CategoryX64::mem);
1418
1419
if (logText)
1420
log(name, dst, src);
1421
1422
placeVex(dst, noreg, src, setW, mode, prefix);
1423
place(code);
1424
placeRegAndModRegMem(dst, src);
1425
1426
commit();
1427
}
1428
1429
void AssemblyBuilderX64::placeAvx(
1430
const char* name,
1431
OperandX64 dst,
1432
OperandX64 src,
1433
uint8_t code,
1434
uint8_t coderev,
1435
bool setW,
1436
uint8_t mode,
1437
uint8_t prefix
1438
)
1439
{
1440
CODEGEN_ASSERT(
1441
(dst.cat == CategoryX64::mem && src.cat == CategoryX64::reg) || (dst.cat == CategoryX64::reg && src.cat == CategoryX64::mem) ||
1442
(dst.cat == CategoryX64::reg && src.cat == CategoryX64::reg)
1443
);
1444
1445
if (logText)
1446
log(name, dst, src);
1447
1448
if (dst.cat == CategoryX64::mem)
1449
{
1450
placeVex(src, noreg, dst, setW, mode, prefix);
1451
place(coderev);
1452
placeRegAndModRegMem(src, dst);
1453
}
1454
else
1455
{
1456
placeVex(dst, noreg, src, setW, mode, prefix);
1457
place(code);
1458
placeRegAndModRegMem(dst, src);
1459
}
1460
1461
commit();
1462
}
1463
1464
void AssemblyBuilderX64::placeAvx(
1465
const char* name,
1466
OperandX64 dst,
1467
OperandX64 src1,
1468
OperandX64 src2,
1469
uint8_t code,
1470
bool setW,
1471
uint8_t mode,
1472
uint8_t prefix
1473
)
1474
{
1475
CODEGEN_ASSERT(dst.cat == CategoryX64::reg);
1476
CODEGEN_ASSERT(src1.cat == CategoryX64::reg);
1477
CODEGEN_ASSERT(src2.cat == CategoryX64::reg || src2.cat == CategoryX64::mem);
1478
1479
if (logText)
1480
log(name, dst, src1, src2);
1481
1482
placeVex(dst, src1, src2, setW, mode, prefix);
1483
place(code);
1484
placeRegAndModRegMem(dst, src2);
1485
1486
commit();
1487
}
1488
1489
void AssemblyBuilderX64::
1490
placeAvx(const char* name, OperandX64 dst, OperandX64 src1, OperandX64 src2, uint8_t imm8, uint8_t code, bool setW, uint8_t mode, uint8_t prefix)
1491
{
1492
CODEGEN_ASSERT(dst.cat == CategoryX64::reg);
1493
CODEGEN_ASSERT(src1.cat == CategoryX64::reg);
1494
CODEGEN_ASSERT(src2.cat == CategoryX64::reg || src2.cat == CategoryX64::mem);
1495
1496
if (logText)
1497
{
1498
if (src1.base == noreg)
1499
log(name, src2, dst, imm8);
1500
else
1501
log(name, dst, src1, src2, imm8);
1502
}
1503
1504
placeVex(dst, src1, src2, setW, mode, prefix);
1505
place(code);
1506
placeRegAndModRegMem(dst, src2, /*extraCodeBytes=*/1);
1507
placeImm8(imm8);
1508
1509
commit();
1510
}
1511
1512
void AssemblyBuilderX64::placeRex(RegisterX64 op)
1513
{
1514
uint8_t code = REX_W(op) | REX_B(op) | REX_FORCE(op);
1515
1516
if (code != 0)
1517
place(code | 0x40);
1518
}
1519
1520
void AssemblyBuilderX64::placeRex(OperandX64 op)
1521
{
1522
uint8_t code = 0;
1523
1524
if (op.cat == CategoryX64::reg)
1525
code = REX_W(op.base) | REX_B(op.base) | REX_FORCE(op.base);
1526
else if (op.cat == CategoryX64::mem)
1527
code = REX_W_BIT(op.memSize == SizeX64::qword) | REX_X(op.index) | REX_B(op.base);
1528
else
1529
CODEGEN_ASSERT(!"No encoding for left operand of this category");
1530
1531
if (code != 0)
1532
place(code | 0x40);
1533
}
1534
1535
void AssemblyBuilderX64::placeRexNoW(OperandX64 op)
1536
{
1537
uint8_t code = 0;
1538
1539
if (op.cat == CategoryX64::reg)
1540
code = REX_B(op.base);
1541
else if (op.cat == CategoryX64::mem)
1542
code = REX_X(op.index) | REX_B(op.base);
1543
else
1544
CODEGEN_ASSERT(!"No encoding for left operand of this category");
1545
1546
if (code != 0)
1547
place(code | 0x40);
1548
}
1549
1550
void AssemblyBuilderX64::placeRex(RegisterX64 lhs, OperandX64 rhs)
1551
{
1552
uint8_t code = REX_W(lhs) | REX_FORCE(lhs);
1553
1554
if (rhs.cat == CategoryX64::imm)
1555
code |= REX_B(lhs);
1556
else
1557
code |= REX_R(lhs) | REX_X(rhs.index) | REX_B(rhs.base) | REX_FORCE(lhs) | REX_FORCE(rhs.base);
1558
1559
if (code != 0)
1560
place(code | 0x40);
1561
}
1562
1563
void AssemblyBuilderX64::placeVex(OperandX64 dst, OperandX64 src1, OperandX64 src2, bool setW, uint8_t mode, uint8_t prefix)
1564
{
1565
CODEGEN_ASSERT(dst.cat == CategoryX64::reg);
1566
CODEGEN_ASSERT(src1.cat == CategoryX64::reg);
1567
CODEGEN_ASSERT(src2.cat == CategoryX64::reg || src2.cat == CategoryX64::mem);
1568
1569
place(AVX_3_1());
1570
place(AVX_3_2(dst.base, src2.index, src2.base, mode));
1571
place(AVX_3_3(setW, src1.base, dst.base.size == SizeX64::ymmword, prefix));
1572
}
1573
1574
static uint8_t getScaleEncoding(uint8_t scale)
1575
{
1576
static const uint8_t scales[9] = {0xff, 0, 1, 0xff, 2, 0xff, 0xff, 0xff, 3};
1577
1578
CODEGEN_ASSERT(scale < 9 && scales[scale] != 0xff);
1579
return scales[scale];
1580
}
1581
1582
void AssemblyBuilderX64::placeRegAndModRegMem(OperandX64 lhs, OperandX64 rhs, int32_t extraCodeBytes)
1583
{
1584
CODEGEN_ASSERT(lhs.cat == CategoryX64::reg);
1585
1586
placeModRegMem(rhs, lhs.base.index, extraCodeBytes);
1587
}
1588
1589
void AssemblyBuilderX64::placeModRegMem(OperandX64 rhs, uint8_t regop, int32_t extraCodeBytes)
1590
{
1591
if (rhs.cat == CategoryX64::reg)
1592
{
1593
place(MOD_RM(0b11, regop, rhs.base.index));
1594
}
1595
else if (rhs.cat == CategoryX64::mem)
1596
{
1597
RegisterX64 index = rhs.index;
1598
RegisterX64 base = rhs.base;
1599
1600
uint8_t mod = 0b00;
1601
1602
if (rhs.imm != 0)
1603
{
1604
if (int8_t(rhs.imm) == rhs.imm)
1605
mod = 0b01;
1606
else
1607
mod = 0b10;
1608
}
1609
else
1610
{
1611
// r13/bp-based addressing requires a displacement
1612
if ((base.index & 0x7) == 0b101)
1613
mod = 0b01;
1614
}
1615
1616
if (index != noreg && base != noreg)
1617
{
1618
place(MOD_RM(mod, regop, 0b100));
1619
place(SIB(rhs.scale, index.index, base.index));
1620
1621
if (mod != 0b00)
1622
placeImm8Or32(rhs.imm);
1623
}
1624
else if (index != noreg && rhs.scale != 1)
1625
{
1626
place(MOD_RM(0b00, regop, 0b100));
1627
place(SIB(rhs.scale, index.index, 0b101));
1628
placeImm32(rhs.imm);
1629
}
1630
else if ((base.index & 0x7) == 0b100) // r12/sp-based addressing requires SIB
1631
{
1632
CODEGEN_ASSERT(rhs.scale == 1);
1633
CODEGEN_ASSERT(index == noreg);
1634
1635
place(MOD_RM(mod, regop, 0b100));
1636
place(SIB(rhs.scale, 0b100, base.index));
1637
1638
if (rhs.imm != 0)
1639
placeImm8Or32(rhs.imm);
1640
}
1641
else if (base == rip)
1642
{
1643
place(MOD_RM(0b00, regop, 0b101));
1644
1645
// As a reminder: we do (getCodeSize() + 4) here to calculate the offset of the end of the current instruction we are placing.
1646
// Since we have already placed all of the instruction bytes for this instruction, we add +4 to account for the imm32 displacement.
1647
// Some instructions, however, are encoded such that an additional imm8 byte, or imm32 bytes, is placed after the ModRM byte, thus,
1648
// we need to account for that case here as well.
1649
placeImm32(-int32_t(getCodeSize() + 4 + extraCodeBytes) + rhs.imm);
1650
}
1651
else if (base != noreg)
1652
{
1653
place(MOD_RM(mod, regop, base.index));
1654
1655
if (mod != 0b00)
1656
placeImm8Or32(rhs.imm);
1657
}
1658
else
1659
{
1660
place(MOD_RM(0b00, regop, 0b100));
1661
place(SIB(1, 0b100, 0b101));
1662
placeImm32(rhs.imm);
1663
}
1664
}
1665
else
1666
{
1667
CODEGEN_ASSERT(!"No encoding for right operand of this category");
1668
}
1669
}
1670
1671
void AssemblyBuilderX64::placeImm8Or32(int32_t imm)
1672
{
1673
int8_t imm8 = int8_t(imm);
1674
1675
if (imm8 == imm)
1676
place(imm8);
1677
else
1678
placeImm32(imm);
1679
}
1680
1681
void AssemblyBuilderX64::placeImm8(int32_t imm)
1682
{
1683
int8_t imm8 = int8_t(imm);
1684
1685
place(imm8);
1686
}
1687
1688
void AssemblyBuilderX64::placeImm16(int16_t imm)
1689
{
1690
uint8_t* pos = codePos;
1691
CODEGEN_ASSERT(pos + sizeof(imm) < codeEnd);
1692
codePos = writeu16(pos, imm);
1693
}
1694
1695
void AssemblyBuilderX64::placeImm32(int32_t imm)
1696
{
1697
uint8_t* pos = codePos;
1698
CODEGEN_ASSERT(pos + sizeof(imm) < codeEnd);
1699
codePos = writeu32(pos, imm);
1700
}
1701
1702
void AssemblyBuilderX64::placeImm64(int64_t imm)
1703
{
1704
uint8_t* pos = codePos;
1705
CODEGEN_ASSERT(pos + sizeof(imm) < codeEnd);
1706
codePos = writeu64(pos, imm);
1707
}
1708
1709
void AssemblyBuilderX64::placeLabel(Label& label)
1710
{
1711
if (label.location == ~0u)
1712
{
1713
if (label.id == 0)
1714
{
1715
label.id = nextLabel++;
1716
labelLocations.push_back(~0u);
1717
}
1718
1719
pendingLabels.push_back({label.id, getCodeSize()});
1720
placeImm32(0);
1721
}
1722
else
1723
{
1724
placeImm32(int32_t(label.location - (4 + getCodeSize())));
1725
}
1726
}
1727
1728
void AssemblyBuilderX64::place(uint8_t byte)
1729
{
1730
CODEGEN_ASSERT(codePos < codeEnd);
1731
*codePos++ = byte;
1732
}
1733
1734
void AssemblyBuilderX64::commit()
1735
{
1736
CODEGEN_ASSERT(codePos <= codeEnd);
1737
1738
++instructionCount;
1739
1740
if (unsigned(codeEnd - codePos) < kMaxInstructionLength)
1741
extend();
1742
}
1743
1744
void AssemblyBuilderX64::extend()
1745
{
1746
uint32_t count = getCodeSize();
1747
1748
code.resize(code.size() * 2);
1749
codePos = code.data() + count;
1750
codeEnd = code.data() + code.size();
1751
}
1752
1753
size_t AssemblyBuilderX64::allocateData(size_t size, size_t align)
1754
{
1755
CODEGEN_ASSERT(align > 0 && align <= kMaxAlign && (align & (align - 1)) == 0);
1756
1757
if (dataPos < size)
1758
{
1759
size_t oldSize = data.size();
1760
data.resize(data.size() * 2);
1761
memcpy(&data[oldSize], &data[0], oldSize);
1762
memset(&data[0], 0, oldSize);
1763
dataPos += oldSize;
1764
}
1765
1766
dataPos = (dataPos - size) & ~(align - 1);
1767
1768
return dataPos;
1769
}
1770
1771
void AssemblyBuilderX64::log(const char* opcode)
1772
{
1773
logAppend(" %s\n", opcode);
1774
}
1775
1776
void AssemblyBuilderX64::log(const char* opcode, OperandX64 op)
1777
{
1778
logAppend(" %-12s", opcode);
1779
log(op);
1780
text.append("\n");
1781
}
1782
1783
void AssemblyBuilderX64::log(const char* opcode, OperandX64 op1, OperandX64 op2)
1784
{
1785
logAppend(" %-12s", opcode);
1786
log(op1);
1787
text.append(",");
1788
log(op2);
1789
text.append("\n");
1790
}
1791
1792
void AssemblyBuilderX64::log(const char* opcode, OperandX64 op1, OperandX64 op2, OperandX64 op3)
1793
{
1794
logAppend(" %-12s", opcode);
1795
log(op1);
1796
text.append(",");
1797
log(op2);
1798
text.append(",");
1799
log(op3);
1800
text.append("\n");
1801
}
1802
1803
void AssemblyBuilderX64::log(const char* opcode, OperandX64 op1, OperandX64 op2, OperandX64 op3, OperandX64 op4)
1804
{
1805
logAppend(" %-12s", opcode);
1806
log(op1);
1807
text.append(",");
1808
log(op2);
1809
text.append(",");
1810
log(op3);
1811
text.append(",");
1812
log(op4);
1813
text.append("\n");
1814
}
1815
1816
void AssemblyBuilderX64::log(Label label)
1817
{
1818
logAppend(".L%d:\n", label.id);
1819
}
1820
1821
void AssemblyBuilderX64::log(const char* opcode, Label label)
1822
{
1823
logAppend(" %-12s.L%d\n", opcode, label.id);
1824
}
1825
1826
void AssemblyBuilderX64::log(const char* opcode, RegisterX64 reg, Label label)
1827
{
1828
logAppend(" %-12s", opcode);
1829
log(reg);
1830
text.append(",");
1831
logAppend(".L%d\n", label.id);
1832
}
1833
1834
void AssemblyBuilderX64::log(OperandX64 op)
1835
{
1836
switch (op.cat)
1837
{
1838
case CategoryX64::reg:
1839
logAppend("%s", getRegisterName(op.base));
1840
break;
1841
case CategoryX64::mem:
1842
if (op.base == rip)
1843
{
1844
if (op.memSize != SizeX64::none)
1845
logAppend("%s ptr ", getSizeName(op.memSize));
1846
logAppend("[.start%+d]", op.imm);
1847
return;
1848
}
1849
1850
if (op.memSize != SizeX64::none)
1851
logAppend("%s ptr ", getSizeName(op.memSize));
1852
1853
logAppend("[");
1854
1855
if (op.base != noreg)
1856
logAppend("%s", getRegisterName(op.base));
1857
1858
if (op.index != noreg)
1859
logAppend("%s%s", op.base != noreg ? "+" : "", getRegisterName(op.index));
1860
1861
if (op.scale != 1)
1862
logAppend("*%d", op.scale);
1863
1864
if (op.imm != 0)
1865
{
1866
if (op.imm >= 0 && op.imm <= 9)
1867
logAppend("+%d", op.imm);
1868
else if (op.imm > 0)
1869
logAppend("+0%Xh", op.imm);
1870
else
1871
logAppend("-0%Xh", -op.imm);
1872
}
1873
1874
text.append("]");
1875
break;
1876
case CategoryX64::imm:
1877
if (op.imm >= 0 && op.imm <= 9)
1878
logAppend("%d", op.imm);
1879
else
1880
logAppend("%Xh", op.imm);
1881
break;
1882
default:
1883
CODEGEN_ASSERT(!"Unknown operand category");
1884
}
1885
}
1886
1887
const char* AssemblyBuilderX64::getSizeName(SizeX64 size) const
1888
{
1889
static const char* sizeNames[] = {"none", "byte", "word", "dword", "qword", "xmmword", "ymmword"};
1890
1891
CODEGEN_ASSERT(unsigned(size) < sizeof(sizeNames) / sizeof(sizeNames[0]));
1892
return sizeNames[unsigned(size)];
1893
}
1894
1895
const char* AssemblyBuilderX64::getRegisterName(RegisterX64 reg) const
1896
{
1897
static const char* names[][16] = {
1898
{"rip", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""},
1899
{"al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"},
1900
{"ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"},
1901
{"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"},
1902
{"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"},
1903
{"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"},
1904
{"ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15"}
1905
};
1906
1907
CODEGEN_ASSERT(reg.index < 16);
1908
CODEGEN_ASSERT(reg.size <= SizeX64::ymmword);
1909
return names[size_t(reg.size)][reg.index];
1910
}
1911
1912
} // namespace X64
1913
} // namespace CodeGen
1914
} // namespace Luau
1915
1916