1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef A64_ASSEMBLER_A64_H_
#define A64_ASSEMBLER_A64_H_
#include "jit/arm64/vixl/Assembler-vixl.h"
#include "jit/JitCompartment.h"
namespace js {
namespace jit {
// VIXL imports.
typedef vixl::Register ARMRegister;
typedef vixl::FPRegister ARMFPRegister;
using vixl::ARMBuffer;
using vixl::Instruction;
static const uint32_t AlignmentAtPrologue = 0;
static const uint32_t AlignmentMidPrologue = 8;
static const Scale ScalePointer = TimesEight;
// The MacroAssembler uses scratch registers extensively and unexpectedly.
// For safety, scratch registers should always be acquired using
// vixl::UseScratchRegisterScope.
static constexpr Register ScratchReg = { Registers::ip0 };
static constexpr ARMRegister ScratchReg64 = { ScratchReg, 64 };
static constexpr Register ScratchReg2 = { Registers::ip1 };
static constexpr ARMRegister ScratchReg2_64 = { ScratchReg2, 64 };
static constexpr FloatRegister ScratchDoubleReg = { FloatRegisters::d31, FloatRegisters::Double };
static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::d0, FloatRegisters::Double };
static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::s0, FloatRegisters::Single };
static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::s31, FloatRegisters::Single };
static constexpr Register InvalidReg = { Registers::invalid_reg };
static constexpr FloatRegister InvalidFloatReg = { FloatRegisters::invalid_fpreg, FloatRegisters::Single };
static constexpr Register OsrFrameReg = { Registers::x3 };
static constexpr Register ArgumentsRectifierReg = { Registers::x8 };
static constexpr Register CallTempReg0 = { Registers::x9 };
static constexpr Register CallTempReg1 = { Registers::x10 };
static constexpr Register CallTempReg2 = { Registers::x11 };
static constexpr Register CallTempReg3 = { Registers::x12 };
static constexpr Register CallTempReg4 = { Registers::x13 };
static constexpr Register CallTempReg5 = { Registers::x14 };
static constexpr Register PreBarrierReg = { Registers::x1 };
static constexpr Register ReturnReg = { Registers::x0 };
static constexpr Register64 ReturnReg64(ReturnReg);
static constexpr Register JSReturnReg = { Registers::x2 };
static constexpr Register FramePointer = { Registers::fp };
static constexpr Register ZeroRegister = { Registers::sp };
static constexpr ARMRegister ZeroRegister64 = { Registers::sp, 64 };
static constexpr ARMRegister ZeroRegister32 = { Registers::sp, 32 };
static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;
// StackPointer is intentionally undefined on ARM64 to prevent misuse:
// using sp as a base register is only valid if sp % 16 == 0.
static constexpr Register RealStackPointer = { Registers::sp };
static constexpr Register PseudoStackPointer = { Registers::x28 };
static constexpr ARMRegister PseudoStackPointer64 = { Registers::x28, 64 };
static constexpr ARMRegister PseudoStackPointer32 = { Registers::x28, 32 };
// StackPointer for use by irregexp.
static constexpr Register RegExpStackPointer = PseudoStackPointer;
static constexpr Register IntArgReg0 = { Registers::x0 };
static constexpr Register IntArgReg1 = { Registers::x1 };
static constexpr Register IntArgReg2 = { Registers::x2 };
static constexpr Register IntArgReg3 = { Registers::x3 };
static constexpr Register IntArgReg4 = { Registers::x4 };
static constexpr Register IntArgReg5 = { Registers::x5 };
static constexpr Register IntArgReg6 = { Registers::x6 };
static constexpr Register IntArgReg7 = { Registers::x7 };
static constexpr Register GlobalReg = { Registers::x20 };
static constexpr Register HeapReg = { Registers::x21 };
static constexpr Register HeapLenReg = { Registers::x22 };
// Define unsized Registers.
#define DEFINE_UNSIZED_REGISTERS(N) \
static constexpr Register r##N = { Registers::x##N };
REGISTER_CODE_LIST(DEFINE_UNSIZED_REGISTERS)
#undef DEFINE_UNSIZED_REGISTERS
static constexpr Register ip0 = { Registers::x16 };
static constexpr Register ip1 = { Registers::x16 };
static constexpr Register fp = { Registers::x30 };
static constexpr Register lr = { Registers::x30 };
static constexpr Register rzr = { Registers::xzr };
// Import VIXL registers into the js::jit namespace.
#define IMPORT_VIXL_REGISTERS(N) \
static constexpr ARMRegister w##N = vixl::w##N; \
static constexpr ARMRegister x##N = vixl::x##N;
REGISTER_CODE_LIST(IMPORT_VIXL_REGISTERS)
#undef IMPORT_VIXL_REGISTERS
static constexpr ARMRegister wzr = vixl::wzr;
static constexpr ARMRegister xzr = vixl::xzr;
static constexpr ARMRegister wsp = vixl::wsp;
static constexpr ARMRegister sp = vixl::sp;
// Import VIXL VRegisters into the js::jit namespace.
#define IMPORT_VIXL_VREGISTERS(N) \
static constexpr ARMFPRegister s##N = vixl::s##N; \
static constexpr ARMFPRegister d##N = vixl::d##N;
REGISTER_CODE_LIST(IMPORT_VIXL_VREGISTERS)
#undef IMPORT_VIXL_VREGISTERS
static constexpr ValueOperand JSReturnOperand = ValueOperand(JSReturnReg);
// Registers used in the GenerateFFIIonExit Enable Activation block.
static constexpr Register WasmIonExitRegCallee = r8;
static constexpr Register WasmIonExitRegE0 = r0;
static constexpr Register WasmIonExitRegE1 = r1;
// Registers used in the GenerateFFIIonExit Disable Activation block.
// None of these may be the second scratch register.
static constexpr Register WasmIonExitRegReturnData = r2;
static constexpr Register WasmIonExitRegReturnType = r3;
static constexpr Register WasmIonExitRegD0 = r0;
static constexpr Register WasmIonExitRegD1 = r1;
static constexpr Register WasmIonExitRegD2 = r4;
// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
static constexpr Register RegExpMatcherRegExpReg = CallTempReg0;
static constexpr Register RegExpMatcherStringReg = CallTempReg1;
static constexpr Register RegExpMatcherLastIndexReg = CallTempReg2;
// Registerd used in RegExpTester instruction (do not use ReturnReg).
static constexpr Register RegExpTesterRegExpReg = CallTempReg0;
static constexpr Register RegExpTesterStringReg = CallTempReg1;
static constexpr Register RegExpTesterLastIndexReg = CallTempReg2;
static constexpr Register JSReturnReg_Type = r3;
static constexpr Register JSReturnReg_Data = r2;
static constexpr FloatRegister NANReg = { FloatRegisters::d14, FloatRegisters::Single };
// N.B. r8 isn't listed as an aapcs temp register, but we can use it as such because we never
// use return-structs.
static constexpr Register CallTempNonArgRegs[] = { r8, r9, r10, r11, r12, r13, r14, r15 };
static const uint32_t NumCallTempNonArgRegs =
mozilla::ArrayLength(CallTempNonArgRegs);
static constexpr uint32_t JitStackAlignment = 16;
static constexpr uint32_t JitStackValueAlignment = JitStackAlignment / sizeof(Value);
static_assert(JitStackAlignment % sizeof(Value) == 0 && JitStackValueAlignment >= 1,
"Stack alignment should be a non-zero multiple of sizeof(Value)");
// This boolean indicates whether we support SIMD instructions flavoured for
// this architecture or not. Rather than a method in the LIRGenerator, it is
// here such that it is accessible from the entire codebase. Once full support
// for SIMD is reached on all tier-1 platforms, this constant can be deleted.
static constexpr bool SupportsSimd = false;
static constexpr uint32_t SimdMemoryAlignment = 16;
static_assert(CodeAlignment % SimdMemoryAlignment == 0,
"Code alignment should be larger than any of the alignments which are used for "
"the constant sections of the code buffer. Thus it should be larger than the "
"alignment for SIMD constants.");
static const uint32_t WasmStackAlignment = SimdMemoryAlignment;
static const int32_t WasmGlobalRegBias = 1024;
// Does this architecture support SIMD conversions between Uint32x4 and Float32x4?
static constexpr bool SupportsUint32x4FloatConversions = false;
// Does this architecture support comparisons of unsigned integer vectors?
static constexpr bool SupportsUint8x16Compares = false;
static constexpr bool SupportsUint16x8Compares = false;
static constexpr bool SupportsUint32x4Compares = false;
class Assembler : public vixl::Assembler
{
public:
Assembler()
: vixl::Assembler()
{ }
typedef vixl::Condition Condition;
void finish();
bool asmMergeWith(const Assembler& other) {
MOZ_CRASH("NYI");
}
void trace(JSTracer* trc);
// Emit the jump table, returning the BufferOffset to the first entry in the table.
BufferOffset emitExtendedJumpTable();
BufferOffset ExtendedJumpTable_;
void executableCopy(uint8_t* buffer);
BufferOffset immPool(ARMRegister dest, uint8_t* value, vixl::LoadLiteralOp op,
ARMBuffer::PoolEntry* pe = nullptr);
BufferOffset immPool64(ARMRegister dest, uint64_t value, ARMBuffer::PoolEntry* pe = nullptr);
BufferOffset immPool64Branch(RepatchLabel* label, ARMBuffer::PoolEntry* pe, vixl::Condition c);
BufferOffset fImmPool(ARMFPRegister dest, uint8_t* value, vixl::LoadLiteralOp op);
BufferOffset fImmPool64(ARMFPRegister dest, double value);
BufferOffset fImmPool32(ARMFPRegister dest, float value);
void bind(Label* label) { bind(label, nextOffset()); }
void bind(Label* label, BufferOffset boff);
void bind(RepatchLabel* label);
void bindLater(Label* label, wasm::TrapDesc target) {
MOZ_CRASH("NYI");
}
bool oom() const {
return AssemblerShared::oom() ||
armbuffer_.oom() ||
jumpRelocations_.oom() ||
dataRelocations_.oom() ||
preBarriers_.oom();
}
void copyJumpRelocationTable(uint8_t* dest) const {
if (jumpRelocations_.length())
memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length());
}
void copyDataRelocationTable(uint8_t* dest) const {
if (dataRelocations_.length())
memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length());
}
void copyPreBarrierTable(uint8_t* dest) const {
if (preBarriers_.length())
memcpy(dest, preBarriers_.buffer(), preBarriers_.length());
}
size_t jumpRelocationTableBytes() const {
return jumpRelocations_.length();
}
size_t dataRelocationTableBytes() const {
return dataRelocations_.length();
}
size_t preBarrierTableBytes() const {
return preBarriers_.length();
}
size_t bytesNeeded() const {
return SizeOfCodeGenerated() +
jumpRelocationTableBytes() +
dataRelocationTableBytes() +
preBarrierTableBytes();
}
void processCodeLabels(uint8_t* rawCode) {
for (size_t i = 0; i < codeLabels_.length(); i++) {
CodeLabel label = codeLabels_[i];
Bind(rawCode, label.patchAt(), rawCode + label.target()->offset());
}
}
void Bind(uint8_t* rawCode, CodeOffset* label, const void* address) {
*reinterpret_cast<const void**>(rawCode + label->offset()) = address;
}
void retarget(Label* cur, Label* next);
// The buffer is about to be linked. Ensure any constant pools or
// excess bookkeeping has been flushed to the instruction stream.
void flush() {
armbuffer_.flushPool();
}
void comment(const char* msg) {
// This is not implemented because setPrinter() is not implemented.
// TODO spew("; %s", msg);
}
int actualIndex(int curOffset) {
ARMBuffer::PoolEntry pe(curOffset);
return armbuffer_.poolEntryOffset(pe);
}
size_t labelToPatchOffset(CodeOffset label) {
return label.offset();
}
static uint8_t* PatchableJumpAddress(JitCode* code, uint32_t index) {
return code->raw() + index;
}
void setPrinter(Sprinter* sp) {
}
static bool SupportsFloatingPoint() { return true; }
static bool SupportsUnalignedAccesses() { return true; }
static bool SupportsSimd() { return js::jit::SupportsSimd; }
// Tracks a jump that is patchable after finalization.
void addJumpRelocation(BufferOffset src, Relocation::Kind reloc);
protected:
// Add a jump whose target is unknown until finalization.
// The jump may not be patched at runtime.
void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind);
// Add a jump whose target is unknown until finalization, and may change
// thereafter. The jump is patchable at runtime.
size_t addPatchableJump(BufferOffset src, Relocation::Kind kind);
public:
static uint32_t PatchWrite_NearCallSize() {
return 4;
}
static uint32_t NopSize() {
return 4;
}
static void PatchWrite_NearCall(CodeLocationLabel start, CodeLocationLabel toCall) {
Instruction* dest = (Instruction*)start.raw();
//printf("patching %p with call to %p\n", start.raw(), toCall.raw());
bl(dest, ((Instruction*)toCall.raw() - dest)>>2);
}
static void PatchDataWithValueCheck(CodeLocationLabel label,
PatchedImmPtr newValue,
PatchedImmPtr expected);
static void PatchDataWithValueCheck(CodeLocationLabel label,
ImmPtr newValue,
ImmPtr expected);
static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) {
// Raw is going to be the return address.
uint32_t* raw = (uint32_t*)label.raw();
// Overwrite the 4 bytes before the return address, which will end up being
// the call instruction.
*(raw - 1) = imm.value;
}
static uint32_t AlignDoubleArg(uint32_t offset) {
MOZ_CRASH("AlignDoubleArg()");
}
static uintptr_t GetPointer(uint8_t* ptr) {
Instruction* i = reinterpret_cast<Instruction*>(ptr);
uint64_t ret = i->Literal64();
return ret;
}
// Toggle a jmp or cmp emitted by toggledJump().
static void ToggleToJmp(CodeLocationLabel inst_);
static void ToggleToCmp(CodeLocationLabel inst_);
static void ToggleCall(CodeLocationLabel inst_, bool enabled);
static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm);
static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
const ObjectVector& nurseryObjects);
public:
// A Jump table entry is 2 instructions, with 8 bytes of raw data
static const size_t SizeOfJumpTableEntry = 16;
struct JumpTableEntry
{
uint32_t ldr;
uint32_t br;
void* data;
Instruction* getLdr() {
return reinterpret_cast<Instruction*>(&ldr);
}
};
// Offset of the patchable target for the given entry.
static const size_t OffsetOfJumpTableEntryPointer = 8;
public:
void writeCodePointer(AbsoluteLabel* absoluteLabel) {
MOZ_ASSERT(!absoluteLabel->bound());
uintptr_t x = LabelBase::INVALID_OFFSET;
BufferOffset off = EmitData(&x, sizeof(uintptr_t));
// The x86/x64 makes general use of AbsoluteLabel and weaves a linked list
// of uses of an AbsoluteLabel through the assembly. ARM only uses labels
// for the case statements of switch jump tables. Thus, for simplicity, we
// simply treat the AbsoluteLabel as a label and bind it to the offset of
// the jump table entry that needs to be patched.
LabelBase* label = absoluteLabel;
label->bind(off.getOffset());
}
void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
const Disassembler::HeapAccess& heapAccess)
{
MOZ_CRASH("verifyHeapAccessDisassembly");
}
protected:
// Because jumps may be relocated to a target inaccessible by a short jump,
// each relocatable jump must have a unique entry in the extended jump table.
// Valid relocatable targets are of type Relocation::JITCODE.
struct JumpRelocation
{
BufferOffset jump; // Offset to the short jump, from the start of the code buffer.
uint32_t extendedTableIndex; // Unique index within the extended jump table.
JumpRelocation(BufferOffset jump, uint32_t extendedTableIndex)
: jump(jump), extendedTableIndex(extendedTableIndex)
{ }
};
// Structure for fixing up pc-relative loads/jumps when the machine
// code gets moved (executable copy, gc, etc.).
struct RelativePatch
{
BufferOffset offset;
void* target;
Relocation::Kind kind;
RelativePatch(BufferOffset offset, void* target, Relocation::Kind kind)
: offset(offset), target(target), kind(kind)
{ }
};
// List of jumps for which the target is either unknown until finalization,
// or cannot be known due to GC. Each entry here requires a unique entry
// in the extended jump table, and is patched at finalization.
js::Vector<RelativePatch, 8, SystemAllocPolicy> pendingJumps_;
// Final output formatters.
CompactBufferWriter jumpRelocations_;
CompactBufferWriter dataRelocations_;
CompactBufferWriter preBarriers_;
};
static const uint32_t NumIntArgRegs = 8;
static const uint32_t NumFloatArgRegs = 8;
class ABIArgGenerator
{
public:
ABIArgGenerator()
: intRegIndex_(0),
floatRegIndex_(0),
stackOffset_(0),
current_()
{ }
ABIArg next(MIRType argType);
ABIArg& current() { return current_; }
uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
protected:
unsigned intRegIndex_;
unsigned floatRegIndex_;
uint32_t stackOffset_;
ABIArg current_;
};
static constexpr Register ABINonArgReg0 = r8;
static constexpr Register ABINonArgReg1 = r9;
static constexpr Register ABINonArgReg2 = r10;
static constexpr Register ABINonArgReturnReg0 = r8;
static constexpr Register ABINonArgReturnReg1 = r9;
// TLS pointer argument register for WebAssembly functions. This must not alias
// any other register used for passing function arguments or return values.
// Preserved by WebAssembly functions.
static constexpr Register WasmTlsReg = { Registers::x17 };
// Registers used for wasm table calls. These registers must be disjoint
// from the ABI argument registers, WasmTlsReg and each other.
static constexpr Register WasmTableCallScratchReg = ABINonArgReg0;
static constexpr Register WasmTableCallSigReg = ABINonArgReg1;
static constexpr Register WasmTableCallIndexReg = ABINonArgReg2;
static inline bool
GetIntArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out)
{
if (usedIntArgs >= NumIntArgRegs)
return false;
*out = Register::FromCode(usedIntArgs);
return true;
}
static inline bool
GetFloatArgReg(uint32_t usedIntArgs, uint32_t usedFloatArgs, FloatRegister* out)
{
if (usedFloatArgs >= NumFloatArgRegs)
return false;
*out = FloatRegister::FromCode(usedFloatArgs);
return true;
}
// Get a register in which we plan to put a quantity that will be used as an
// integer argument. This differs from GetIntArgReg in that if we have no more
// actual argument registers to use we will fall back on using whatever
// CallTempReg* don't overlap the argument registers, and only fail once those
// run out too.
static inline bool
GetTempRegForIntArg(uint32_t usedIntArgs, uint32_t usedFloatArgs, Register* out)
{
if (GetIntArgReg(usedIntArgs, usedFloatArgs, out))
return true;
// Unfortunately, we have to assume things about the point at which
// GetIntArgReg returns false, because we need to know how many registers it
// can allocate.
usedIntArgs -= NumIntArgRegs;
if (usedIntArgs >= NumCallTempNonArgRegs)
return false;
*out = CallTempNonArgRegs[usedIntArgs];
return true;
}
inline Imm32
Imm64::firstHalf() const
{
return low();
}
inline Imm32
Imm64::secondHalf() const
{
return hi();
}
void PatchJump(CodeLocationJump& jump_, CodeLocationLabel label,
ReprotectCode reprotect = DontReprotect);
static inline void
PatchBackedge(CodeLocationJump& jump_, CodeLocationLabel label, JitRuntime::BackedgeTarget target)
{
PatchJump(jump_, label);
}
// Forbids pool generation during a specified interval. Not nestable.
class AutoForbidPools
{
Assembler* asm_;
public:
AutoForbidPools(Assembler* asm_, size_t maxInst)
: asm_(asm_)
{
asm_->enterNoPool(maxInst);
}
~AutoForbidPools() {
asm_->leaveNoPool();
}
};
} // namespace jit
} // namespace js
#endif // A64_ASSEMBLER_A64_H_
|