Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/compiler/x/codegen/CallSnippet.cpp
6004 views
1
/*******************************************************************************
2
* Copyright (c) 2000, 2022 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
#include "x/codegen/CallSnippet.hpp"
24
25
#include "codegen/CodeGenerator.hpp"
26
#include "codegen/Linkage_inlines.hpp"
27
#include "codegen/Relocation.hpp"
28
#include "codegen/SnippetGCMap.hpp"
29
#include "codegen/X86PrivateLinkage.hpp"
30
#include "env/CompilerEnv.hpp"
31
#include "env/IO.hpp"
32
#include "env/jittypes.h"
33
#include "env/VMJ9.h"
34
#include "il/LabelSymbol.hpp"
35
#include "il/MethodSymbol.hpp"
36
#include "il/Node.hpp"
37
#include "il/Node_inlines.hpp"
38
#include "il/RegisterMappedSymbol.hpp"
39
#include "il/ResolvedMethodSymbol.hpp"
40
#include "il/StaticSymbol.hpp"
41
#include "il/Symbol.hpp"
42
43
bool TR::X86PicDataSnippet::shouldEmitJ2IThunkPointer()
44
{
45
if (!cg()->comp()->target().is64Bit())
46
return false; // no j2i thunks on 32-bit
47
48
if (!isInterface())
49
return unresolvedDispatch(); // invokevirtual could be private
50
51
// invokeinterface
52
if (cg()->comp()->compileRelocatableCode())
53
return true; // forced to assume it could be private/Object
54
55
// Since interface method symrefs are always unresolved, check to see
56
// whether we know that it's a normal interface call. If we don't, then
57
// it could be private/Object.
58
uintptr_t itableIndex = (uintptr_t)-1;
59
int32_t cpIndex = _methodSymRef->getCPIndex();
60
TR_ResolvedMethod *owningMethod = _methodSymRef->getOwningMethod(cg()->comp());
61
TR_OpaqueClassBlock *interfaceClass =
62
owningMethod->getResolvedInterfaceMethod(cpIndex, &itableIndex);
63
return interfaceClass == NULL;
64
}
65
66
uint8_t *TR::X86PicDataSnippet::encodeConstantPoolInfo(uint8_t *cursor)
67
{
68
TR::Compilation *comp = cg()->comp();
69
uintptr_t cpAddr = (uintptr_t)_methodSymRef->getOwningMethod(comp)->constantPool();
70
*(uintptr_t *)cursor = cpAddr;
71
72
uintptr_t inlinedSiteIndex = (uintptr_t)-1;
73
if (_startOfPicInstruction->getNode() != NULL)
74
inlinedSiteIndex = _startOfPicInstruction->getNode()->getInlinedSiteIndex();
75
76
if (_hasJ2IThunkInPicData)
77
{
78
TR_ASSERT(
79
comp->target().is64Bit(),
80
"expecting a 64-bit target for thunk relocations");
81
82
auto info =
83
(TR_RelocationRecordInformation *)comp->trMemory()->allocateMemory(
84
sizeof (TR_RelocationRecordInformation),
85
heapAlloc);
86
87
int offsetToJ2IVirtualThunk = isInterface() ? 0x22 : 0x18;
88
89
info->data1 = cpAddr;
90
info->data2 = inlinedSiteIndex;
91
info->data3 = offsetToJ2IVirtualThunk;
92
93
cg()->addExternalRelocation(
94
new (cg()->trHeapMemory()) TR::ExternalRelocation(
95
cursor,
96
(uint8_t *)info,
97
NULL,
98
TR_J2IVirtualThunkPointer,
99
cg()),
100
__FILE__,
101
__LINE__,
102
_startOfPicInstruction->getNode());
103
}
104
else if (_thunkAddress)
105
{
106
TR_ASSERT(comp->target().is64Bit(), "expecting a 64-bit target for thunk relocations");
107
cg()->addExternalRelocation(new (cg()->trHeapMemory()) TR::ExternalRelocation(cursor,
108
*(uint8_t **)cursor,
109
(uint8_t *)inlinedSiteIndex,
110
TR_Thunks, cg()),
111
__FILE__,
112
__LINE__,
113
_startOfPicInstruction->getNode());
114
}
115
else
116
{
117
cg()->addExternalRelocation(new (cg()->trHeapMemory()) TR::ExternalRelocation(cursor,
118
(uint8_t *)cpAddr,
119
(uint8_t *)inlinedSiteIndex,
120
TR_ConstantPool,
121
cg()),
122
__FILE__,
123
__LINE__,
124
_startOfPicInstruction->getNode());
125
}
126
127
// DD/DQ cpIndex
128
//
129
cursor += sizeof(uintptr_t);
130
*(uintptr_t *)cursor = (uintptr_t)_methodSymRef->getCPIndexForVM();
131
cursor += sizeof(uintptr_t);
132
133
return cursor;
134
}
135
136
uint8_t *TR::X86PicDataSnippet::encodeJ2IThunkPointer(uint8_t *cursor)
137
{
138
TR_ASSERT_FATAL(_hasJ2IThunkInPicData, "did not expect j2i thunk pointer");
139
TR_ASSERT_FATAL(_thunkAddress != NULL, "null virtual j2i thunk");
140
141
// DD/DQ j2iThunk
142
*(uintptr_t *)cursor = (uintptr_t)_thunkAddress;
143
cursor += sizeof(uintptr_t);
144
145
return cursor;
146
}
147
148
uint8_t *TR::X86PicDataSnippet::emitSnippetBody()
149
{
150
TR::Compilation *comp = cg()->comp();
151
152
uint8_t *startOfSnippet = cg()->getBinaryBufferCursor();
153
154
uint8_t *cursor = startOfSnippet;
155
156
J9::X86::PrivateLinkage *x86Linkage = static_cast<J9::X86::PrivateLinkage *>(cg()->getLinkage());
157
158
int32_t disp32;
159
160
TR_RuntimeHelper resolveSlotHelper, populateSlotHelper;
161
int32_t sizeofPicSlot;
162
163
if (isInterface())
164
{
165
// IPIC
166
//
167
// Slow interface lookup dispatch.
168
//
169
170
// Align the IPIC data to a pointer-sized boundary to ensure that the
171
// interface class and itable offset are naturally aligned.
172
uintptr_t offsetToIpicData = 10;
173
uintptr_t unalignedIpicDataStart = (uintptr_t)cursor + offsetToIpicData;
174
uintptr_t alignMask = sizeof (uintptr_t) - 1;
175
uintptr_t alignedIpicDataStart =
176
(unalignedIpicDataStart + alignMask) & ~alignMask;
177
cursor += alignedIpicDataStart - unalignedIpicDataStart;
178
179
getSnippetLabel()->setCodeLocation(cursor);
180
181
// Slow path lookup dispatch
182
//
183
_dispatchSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_X86IPicLookupDispatch);
184
185
*cursor++ = 0xe8; // CALL
186
disp32 = cg()->branchDisplacementToHelperOrTrampoline(cursor+4, _dispatchSymRef);
187
*(int32_t *)cursor = disp32;
188
189
cg()->addExternalRelocation(new (cg()->trHeapMemory())
190
TR::ExternalRelocation(cursor,
191
(uint8_t *)_dispatchSymRef,
192
TR_HelperAddress,
193
cg()), __FILE__, __LINE__, _startOfPicInstruction->getNode());
194
cursor += 4;
195
196
// Lookup dispatch needs its stack map here.
197
//
198
gcMap().registerStackMap(cursor, cg());
199
200
// Restart jump (always long for predictable size).
201
//
202
disp32 = _doneLabel->getCodeLocation() - (cursor + 5);
203
*cursor++ = 0xe9;
204
*(int32_t *)cursor = disp32;
205
cursor += 4;
206
207
// DD/DQ constantPool address
208
// DD/DQ cpIndex
209
//
210
if (unresolvedDispatch())
211
{
212
cursor = encodeConstantPoolInfo(cursor);
213
}
214
else
215
{
216
TR_ASSERT_FATAL(0, "Can't handle resolved IPICs here yet!");
217
}
218
219
// Because the interface class and itable offset (immediately following)
220
// are written at runtime and might be read concurrently by another
221
// thread, they must be naturally aligned to guarantee that all accesses
222
// to them are atomic.
223
TR_ASSERT_FATAL(
224
((uintptr_t)cursor & (sizeof(uintptr_t) - 1)) == 0,
225
"interface class and itable offset IPIC data slots are unaligned");
226
227
// Reserve space for resolved interface class and itable offset.
228
// These slots will be populated during interface class resolution.
229
// The itable offset slot doubles as a direct J9Method pointer slot.
230
//
231
// DD/DQ 0x00000000
232
// DD/DQ 0x00000000
233
//
234
*(uintptr_t*)cursor = 0;
235
cursor += sizeof(uintptr_t);
236
*(uintptr_t*)cursor = 0;
237
cursor += sizeof(uintptr_t);
238
239
if (comp->target().is64Bit())
240
{
241
// REX+MOV of MOVRegImm64 instruction
242
//
243
uint16_t *slotPatchInstructionBytes = (uint16_t *)_slotPatchInstruction->getBinaryEncoding();
244
*(uint16_t *)cursor = *slotPatchInstructionBytes;
245
cursor += 2;
246
247
if (unresolvedDispatch() && _hasJ2IThunkInPicData)
248
cursor = encodeJ2IThunkPointer(cursor);
249
}
250
else
251
{
252
// ModRM byte of TR::InstOpCode::CMPMemImm4 instruction
253
//
254
uint8_t *slotPatchInstructionBytes = _slotPatchInstruction->getBinaryEncoding();
255
*cursor = *(slotPatchInstructionBytes+1);
256
cursor++;
257
}
258
259
resolveSlotHelper = TR_X86resolveIPicClass;
260
populateSlotHelper = TR_X86populateIPicSlotClass;
261
sizeofPicSlot = x86Linkage->IPicParameters.roundedSizeOfSlot;
262
}
263
else
264
{
265
// VPIC
266
//
267
// Slow path dispatch through vtable
268
//
269
270
uint8_t callModRMByte = 0;
271
272
// DD/DQ constantPool address
273
// DD/DQ cpIndex
274
//
275
if (unresolvedDispatch())
276
{
277
// Align the real snippet entry point because it will be patched with
278
// the vtable dispatch when the method is resolved.
279
//
280
intptr_t entryPoint = ((intptr_t)cursor +
281
((3 * sizeof(uintptr_t)) +
282
(hasJ2IThunkInPicData() ? sizeof(uintptr_t) : 0) +
283
(comp->target().is64Bit() ? 4 : 1)));
284
285
intptr_t requiredEntryPoint =
286
(entryPoint + (cg()->getLowestCommonCodePatchingAlignmentBoundary()-1) &
287
(intptr_t)(~(cg()->getLowestCommonCodePatchingAlignmentBoundary()-1)));
288
289
cursor += (requiredEntryPoint - entryPoint);
290
291
// Put the narrow integers before the pointer-sized ones. This way,
292
// directMethod (which is mutable) will be aligned simply as a
293
// consequence of the alignment required for patching the code that
294
// immediately follows the VPIC data.
295
if (comp->target().is64Bit())
296
{
297
// REX prefix of MOVRegImm64 instruction
298
//
299
uint8_t *slotPatchInstructionBytes = (uint8_t *)_slotPatchInstruction->getBinaryEncoding();
300
*cursor++ = *slotPatchInstructionBytes++;
301
302
// MOV op of MOVRegImm64 instruction
303
//
304
*cursor++ = *slotPatchInstructionBytes;
305
306
// REX prefix for the TR::InstOpCode::CALLMem instruction.
307
//
308
*cursor++ = *(slotPatchInstructionBytes+9);
309
310
// Convert the CMP ModRM byte into the ModRM byte for the TR::InstOpCode::CALLMem instruction.
311
//
312
slotPatchInstructionBytes += 11;
313
callModRMByte = (*slotPatchInstructionBytes & 7) + 0x90;
314
*cursor++ = callModRMByte;
315
}
316
else
317
{
318
// CMP ModRM byte
319
//
320
uint8_t *slotPatchInstructionBytes = (uint8_t *)_slotPatchInstruction->getBinaryEncoding();
321
*cursor++ = *(slotPatchInstructionBytes+1);
322
}
323
324
// DD/DQ cpAddr
325
// DD/DQ cpIndex
326
//
327
cursor = encodeConstantPoolInfo(cursor);
328
329
// Because directMethod (immediately following) is written at runtime
330
// and might be read concurrently by another thread, it must be
331
// naturally aligned to ensure that all accesses to it are atomic.
332
TR_ASSERT_FATAL(
333
((uintptr_t)cursor & (sizeof(uintptr_t) - 1)) == 0,
334
"directMethod VPIC data slot is unaligned");
335
336
// DD/DQ directMethod (initially null)
337
*(uintptr_t *)cursor = 0;
338
cursor += sizeof(uintptr_t);
339
340
if (comp->target().is64Bit())
341
{
342
// DD/DQ j2iThunk
343
cursor = encodeJ2IThunkPointer(cursor);
344
}
345
}
346
else
347
{
348
TR_ASSERT_FATAL(0, "Can't handle resolved VPICs here yet!");
349
}
350
351
_dispatchSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_X86populateVPicVTableDispatch);
352
353
getSnippetLabel()->setCodeLocation(cursor);
354
355
if (!isInterface() && _methodSymRef->isUnresolved())
356
{
357
TR_ASSERT((((intptr_t)cursor & (cg()->getLowestCommonCodePatchingAlignmentBoundary()-1)) == 0),
358
"Mis-aligned VPIC snippet");
359
}
360
361
*cursor++ = 0xe8; // CALL
362
disp32 = cg()->branchDisplacementToHelperOrTrampoline(cursor+4, _dispatchSymRef);
363
*(int32_t *)cursor = disp32;
364
365
cg()->addExternalRelocation(new (cg()->trHeapMemory())
366
TR::ExternalRelocation(cursor,
367
(uint8_t *)_dispatchSymRef,
368
TR_HelperAddress,
369
cg()), __FILE__, __LINE__, _startOfPicInstruction->getNode());
370
cursor += 4;
371
372
// Populate vtable dispatch needs its stack map here.
373
//
374
gcMap().registerStackMap(cursor, cg());
375
376
// Add padding after the call to snippet to hold the eventual indirect call instruction.
377
//
378
if (comp->target().is64Bit())
379
{
380
*(uint16_t *)cursor = 0;
381
cursor += 2;
382
383
if (callModRMByte == 0x94)
384
{
385
// SIB byte required for CMP
386
//
387
*(uint8_t *)cursor = 0;
388
cursor++;
389
}
390
}
391
else
392
{
393
*(uint8_t *)cursor = 0;
394
cursor++;
395
}
396
397
// Restart jump (always long for predictable size).
398
//
399
// TODO: no longer the case since data moved before call.
400
//
401
disp32 = _doneLabel->getCodeLocation() - (cursor + 5);
402
*cursor++ = 0xe9;
403
*(int32_t *)cursor = disp32;
404
cursor += 4;
405
406
resolveSlotHelper = TR_X86resolveVPicClass;
407
populateSlotHelper = TR_X86populateVPicSlotClass;
408
sizeofPicSlot = x86Linkage->VPicParameters.roundedSizeOfSlot;
409
}
410
411
if (_numberOfSlots >= 1)
412
{
413
// Patch each Pic slot to route through the population helper
414
//
415
int32_t numPicSlots = _numberOfSlots;
416
uint8_t *picSlotCursor = _startOfPicInstruction->getBinaryEncoding();
417
418
TR::SymbolReference *resolveSlotHelperSymRef =
419
cg()->symRefTab()->findOrCreateRuntimeHelper(resolveSlotHelper);
420
TR::SymbolReference *populateSlotHelperSymRef =
421
cg()->symRefTab()->findOrCreateRuntimeHelper(populateSlotHelper);
422
423
// Patch first slot test with call to resolution helper.
424
//
425
*picSlotCursor++ = 0xe8; // CALL
426
disp32 = cg()->branchDisplacementToHelperOrTrampoline(picSlotCursor+4, resolveSlotHelperSymRef);
427
*(int32_t *)picSlotCursor = disp32;
428
429
cg()->addExternalRelocation(new (cg()->trHeapMemory())
430
TR::ExternalRelocation(picSlotCursor,
431
(uint8_t *)resolveSlotHelperSymRef,
432
TR_HelperAddress,
433
cg()), __FILE__, __LINE__, _startOfPicInstruction->getNode());
434
435
picSlotCursor = (uint8_t *)(picSlotCursor - 1 + sizeofPicSlot);
436
437
// Patch remaining slots with call to populate helper.
438
//
439
while (--numPicSlots)
440
{
441
*picSlotCursor++ = 0xe8; // CALL
442
disp32 = cg()->branchDisplacementToHelperOrTrampoline(picSlotCursor+4, populateSlotHelperSymRef);
443
*(int32_t *)picSlotCursor = disp32;
444
445
cg()->addExternalRelocation(new (cg()->trHeapMemory())
446
TR::ExternalRelocation(picSlotCursor,
447
(uint8_t *)populateSlotHelperSymRef,
448
TR_HelperAddress,
449
cg()), __FILE__, __LINE__, _startOfPicInstruction->getNode());
450
picSlotCursor = (uint8_t *)(picSlotCursor - 1 + sizeofPicSlot);
451
}
452
}
453
454
return cursor;
455
}
456
457
458
void
459
TR_Debug::print(TR::FILE *pOutFile, TR::X86PicDataSnippet *snippet)
460
{
461
if (pOutFile == NULL)
462
return;
463
464
TR_J9VMBase *fej9 = (TR_J9VMBase *)(_cg->fe());
465
466
uint8_t *bufferPos = snippet->getSnippetLabel()->getCodeLocation();
467
468
// Account for VPic data appearing before the actual entry label.
469
//
470
if (!snippet->isInterface())
471
{
472
// TODO: clean this up!
473
//
474
bufferPos -= _comp->target().is64Bit() ? 4 : 1;
475
bufferPos -= 2 * sizeof(uintptr_t);
476
if (snippet->unresolvedDispatch())
477
{
478
bufferPos -= sizeof(uintptr_t);
479
if (snippet->hasJ2IThunkInPicData())
480
bufferPos -= sizeof(uintptr_t);
481
}
482
483
uint32_t offset = bufferPos - _cg->getCodeStart();
484
trfprintf(pOutFile, "\n\n" POINTER_PRINTF_FORMAT " %08x %*s", bufferPos, offset, 65, " <<< VPic Data >>>");
485
}
486
else
487
{
488
printSnippetLabel(pOutFile, snippet->getSnippetLabel(), bufferPos, getName(snippet));
489
}
490
491
TR::SymbolReference *methodSymRef = snippet->getMethodSymRef();
492
TR::SymbolReference *dispatchSymRef = snippet->getDispatchSymRef();
493
494
if (snippet->isInterface())
495
{
496
// Call lookup dispatch.
497
//
498
printPrefix(pOutFile, NULL, bufferPos, 5);
499
trfprintf(pOutFile, "call\t%s \t\t%s " POINTER_PRINTF_FORMAT,
500
getName(dispatchSymRef),
501
commentString(),
502
dispatchSymRef->getMethodAddress());
503
bufferPos += 5;
504
505
// Restart JMP (always 5 bytes).
506
//
507
printPrefix(pOutFile, NULL, bufferPos, 5);
508
printLabelInstruction(pOutFile, "jmp", snippet->getDoneLabel());
509
bufferPos += 5;
510
511
if (methodSymRef->isUnresolved())
512
{
513
const char *op = (sizeof(uintptr_t) == 4) ? "DD" : "DQ";
514
515
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
516
trfprintf(
517
pOutFile,
518
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s owning method cpAddr",
519
op,
520
(void*)*(uintptr_t*)bufferPos,
521
commentString());
522
bufferPos += sizeof(uintptr_t);
523
524
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
525
trfprintf(
526
pOutFile,
527
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s cpIndex",
528
op,
529
(void*)*(uintptr_t*)bufferPos,
530
commentString());
531
bufferPos += sizeof(uintptr_t);
532
533
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
534
trfprintf(
535
pOutFile,
536
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s interface class (initially null)",
537
op,
538
(void*)*(uintptr_t*)bufferPos,
539
commentString());
540
bufferPos += sizeof(uintptr_t);
541
542
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
543
trfprintf(
544
pOutFile,
545
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s itable offset%s (initially zero)",
546
op,
547
(void*)*(uintptr_t*)bufferPos,
548
commentString(),
549
snippet->hasJ2IThunkInPicData() ? " or direct J9Method" : "");
550
bufferPos += sizeof(uintptr_t);
551
552
if (_comp->target().is64Bit())
553
{
554
// REX+MOV of MOVRegImm64 instruction
555
//
556
printPrefix(pOutFile, NULL, bufferPos, 1);
557
trfprintf(pOutFile, "%s\t%s%02x%s\t\t\t\t\t\t\t\t%s REX of MOVRegImm64",
558
dbString(),
559
hexPrefixString(),
560
*bufferPos,
561
hexSuffixString(),
562
commentString());
563
bufferPos += 1;
564
565
printPrefix(pOutFile, NULL, bufferPos, 1);
566
trfprintf(pOutFile, "%s\t%02x\t\t\t\t\t\t\t\t%s MOV opcode of MOVRegImm64",
567
dbString(),
568
*bufferPos,
569
commentString());
570
bufferPos += 1;
571
572
if (snippet->hasJ2IThunkInPicData())
573
{
574
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
575
trfprintf(
576
pOutFile,
577
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s j2i virtual thunk",
578
op,
579
(void*)*(uintptr_t*)bufferPos,
580
commentString());
581
bufferPos += sizeof(uintptr_t);
582
}
583
}
584
else
585
{
586
// ModRM of TR::InstOpCode::CMPRegImm4
587
//
588
printPrefix(pOutFile, NULL, bufferPos, 1);
589
trfprintf(pOutFile, "%s\t%s%02x%s\t\t\t\t\t\t\t\t%s ModRM of CMP",
590
dbString(),
591
hexPrefixString(),
592
*bufferPos,
593
hexSuffixString(),
594
commentString());
595
bufferPos += 1;
596
}
597
}
598
}
599
else
600
{
601
uint8_t callModRM = 0;
602
603
if (snippet->unresolvedDispatch())
604
{
605
const char *op = (sizeof(uintptr_t) == 4) ? "DD" : "DQ";
606
607
if (_comp->target().is64Bit())
608
{
609
printPrefix(pOutFile, NULL, bufferPos, 1);
610
trfprintf(pOutFile, "%s\t%02x\t\t\t\t\t\t\t\t%s REX of MOVRegImm64",
611
dbString(),
612
*bufferPos,
613
commentString());
614
bufferPos += 1;
615
616
printPrefix(pOutFile, NULL, bufferPos, 1);
617
trfprintf(pOutFile, "%s\t%02x\t\t\t\t\t\t\t\t%s MOV opcode of MOVRegImm64",
618
dbString(),
619
*bufferPos,
620
commentString());
621
bufferPos += 1;
622
623
printPrefix(pOutFile, NULL, bufferPos, 1);
624
trfprintf(pOutFile, "%s\t%02x\t\t\t\t\t\t\t\t%s REX of CallMem",
625
dbString(),
626
*bufferPos,
627
commentString());
628
bufferPos += 1;
629
630
callModRM = *bufferPos;
631
printPrefix(pOutFile, NULL, bufferPos, 1);
632
trfprintf(pOutFile, "%s\t%02x\t\t\t\t\t\t\t\t%s ModRM for TR::InstOpCode::CALLMem",
633
dbString(),
634
*bufferPos,
635
commentString());
636
bufferPos += 1;
637
}
638
else
639
{
640
printPrefix(pOutFile, NULL, bufferPos, 1);
641
trfprintf(pOutFile, "%s\t%02x\t\t\t\t\t\t\t\t%s ModRM for TR::InstOpCode::CMPRegImm4",
642
dbString(),
643
*bufferPos,
644
commentString());
645
bufferPos += 1;
646
}
647
648
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
649
trfprintf(
650
pOutFile,
651
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s owning method cpAddr",
652
op,
653
(void*)*(uintptr_t*)bufferPos,
654
commentString());
655
bufferPos += sizeof(uintptr_t);
656
657
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
658
trfprintf(
659
pOutFile,
660
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s cpIndex",
661
op,
662
(void*)*(uintptr_t*)bufferPos,
663
commentString());
664
bufferPos += sizeof(uintptr_t);
665
666
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
667
trfprintf(pOutFile,
668
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s direct J9Method (initially null)",
669
op,
670
(void*)*(uintptr_t*)bufferPos,
671
commentString());
672
bufferPos += sizeof(uintptr_t);
673
674
if (_comp->target().is64Bit())
675
{
676
printPrefix(pOutFile, NULL, bufferPos, sizeof(uintptr_t));
677
trfprintf(
678
pOutFile,
679
"%s\t" POINTER_PRINTF_FORMAT "\t\t%s j2i virtual thunk",
680
op,
681
(void*)*(uintptr_t*)bufferPos,
682
commentString());
683
bufferPos += sizeof(uintptr_t);
684
}
685
}
686
687
if (_comp->target().is64Bit())
688
printSnippetLabel(pOutFile, snippet->getSnippetLabel(), bufferPos, getName(snippet));
689
690
// Call through vtable.
691
//
692
int32_t length;
693
694
if (_comp->target().is64Bit())
695
{
696
length = 7;
697
if (callModRM == 0x94)
698
length++;
699
}
700
else
701
{
702
length = 6;
703
}
704
705
printPrefix(pOutFile, NULL, bufferPos, length);
706
trfprintf(pOutFile, "call\t%s \t\t%s " POINTER_PRINTF_FORMAT "\tpatched with vtable call",
707
getName(dispatchSymRef),
708
commentString(),
709
dispatchSymRef->getMethodAddress());
710
bufferPos += length;
711
712
// Restart JMP (always 5 bytes).
713
//
714
printPrefix(pOutFile, NULL, bufferPos, 5);
715
printLabelInstruction(pOutFile, "jmp", snippet->getDoneLabel());
716
bufferPos += 5;
717
718
}
719
}
720
721
722
723
uint32_t TR::X86PicDataSnippet::getLength(int32_t estimatedSnippetStart)
724
{
725
TR::Compilation *comp = cg()->comp();
726
727
if (isInterface())
728
{
729
return 5 // Lookup dispatch
730
+ 5 // JMP done
731
+ (4 * sizeof(uintptr_t)) // Resolve slots
732
+ (comp->target().is64Bit() ? 2 : 1) // ModRM or REX+MOV
733
+ (_hasJ2IThunkInPicData ? sizeof(uintptr_t) : 0) // j2i thunk pointer
734
+ sizeof (uintptr_t) - 1; // alignment
735
}
736
else
737
{
738
return 6 // CALL [Mem] (pessimistically assume a SIB is needed)
739
+ (comp->target().is64Bit() ? 2 : 0) // REX for CALL + SIB for CALL (64-bit)
740
+ 5 // JMP done
741
+ (2 * sizeof(uintptr_t)) // cpAddr, cpIndex
742
+ (unresolvedDispatch() ? sizeof(uintptr_t) : 0) // directMethod
743
+ (_hasJ2IThunkInPicData ? sizeof(uintptr_t) : 0) // j2i thunk
744
745
// 64-bit Data
746
// -----------
747
// 2 (REX+MOV)
748
// +2 (REX+ModRM for CALL)
749
//
750
// 32-bit Data
751
// -----------
752
// 1 (ModRM for CMP)
753
//
754
+ (comp->target().is64Bit() ? 4 : 1)
755
+ cg()->getLowestCommonCodePatchingAlignmentBoundary()-1;
756
}
757
}
758
759
760
uint8_t *
761
TR::X86CallSnippet::alignCursorForCodePatching(
762
uint8_t *cursor,
763
bool alignWithNOPs)
764
{
765
intptr_t alignedCursor =
766
((intptr_t)cursor + (cg()->getLowestCommonCodePatchingAlignmentBoundary()-1) &
767
(intptr_t)(~(cg()->getLowestCommonCodePatchingAlignmentBoundary()-1)));
768
769
intptr_t paddingLength = alignedCursor - (intptr_t)cursor;
770
771
if (alignWithNOPs && (paddingLength > 0))
772
{
773
return (uint8_t *)(cg()->generatePadding(cursor, paddingLength));
774
}
775
else
776
{
777
return (uint8_t *)alignedCursor;
778
}
779
}
780
781
782
uint8_t *TR::X86CallSnippet::emitSnippetBody()
783
{
784
TR::Compilation *comp = cg()->comp();
785
TR_J9VMBase* fej9 = (TR_J9VMBase *)(cg()->fe());
786
TR::SymbolReference* methodSymRef = _realMethodSymbolReference ? _realMethodSymbolReference : getNode()->getSymbolReference();
787
TR::MethodSymbol* methodSymbol = methodSymRef->getSymbol()->castToMethodSymbol();
788
uint8_t* cursor = cg()->getBinaryBufferCursor();
789
790
bool needToSetCodeLocation = true;
791
bool isJitInduceOSRCall = false;
792
793
if (comp->target().is64Bit() &&
794
methodSymbol->isHelper() &&
795
methodSymRef->isOSRInductionHelper())
796
{
797
isJitInduceOSRCall = true;
798
}
799
800
if (comp->target().is64Bit())
801
{
802
// Backspill register linkage arguments to the stack.
803
//
804
TR::Linkage *linkage = cg()->getLinkage(methodSymbol->getLinkageConvention());
805
getSnippetLabel()->setCodeLocation(cursor);
806
cursor = linkage->storeArguments(getNode(), cursor, false, NULL);
807
needToSetCodeLocation = false;
808
809
if (cg()->hasCodeCacheSwitched() &&
810
(methodSymRef->getReferenceNumber()>=TR_AMD64numRuntimeHelpers))
811
{
812
fej9->reserveTrampolineIfNecessary(comp, methodSymRef, true);
813
}
814
}
815
816
bool forceUnresolvedDispatch = !fej9->isResolvedDirectDispatchGuaranteed(comp);
817
if (methodSymRef->isUnresolved() || forceUnresolvedDispatch)
818
{
819
// Unresolved interpreted dispatch snippet shape:
820
//
821
// 64-bit
822
// ======
823
// align 8
824
// (10) CALL interpreterUnresolved{Static|Special}Glue ; replaced with "mov rdi, 0x0000aabbccddeeff"
825
// (5) JMP interpreterStaticAndSpecialGlue
826
// (2) DW 2-byte glue method helper index
827
// (8) DQ cpAddr
828
// (4) DD cpIndex
829
//
830
// 32-bit
831
// ======
832
// align 8
833
// (5) CALL interpreterUnresolved{Static|Special}Glue ; replaced with "mov edi, 0xaabbccdd"
834
// (3) NOP
835
// (5) JMP interpreterStaticAndSpecialGlue
836
// (2) DW 2-byte glue method helper index
837
// (4) DD cpAddr
838
// (4) DD cpIndex
839
//
840
841
TR_ASSERT(!isJitInduceOSRCall || !forceUnresolvedDispatch, "calling jitInduceOSR is not supported yet under AOT\n");
842
cursor = alignCursorForCodePatching(cursor, comp->target().is64Bit());
843
844
if (comp->getOption(TR_EnableHCR))
845
{
846
cg()->jitAddUnresolvedAddressMaterializationToPatchOnClassRedefinition(cursor);
847
}
848
849
if (needToSetCodeLocation)
850
{
851
getSnippetLabel()->setCodeLocation(cursor);
852
}
853
854
TR_ASSERT((methodSymbol->isStatic() || methodSymbol->isSpecial() || forceUnresolvedDispatch), "Unexpected unresolved dispatch");
855
856
// CALL interpreterUnresolved{Static|Special}Glue
857
//
858
TR_RuntimeHelper resolutionHelper = methodSymbol->isStatic() ?
859
TR_X86interpreterUnresolvedStaticGlue : TR_X86interpreterUnresolvedSpecialGlue;
860
861
TR::SymbolReference *helperSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(resolutionHelper);
862
863
*cursor++ = 0xe8; // CALL
864
*(int32_t *)cursor = cg()->branchDisplacementToHelperOrTrampoline(cursor + 4, helperSymRef);
865
866
cg()->addExternalRelocation(new (cg()->trHeapMemory()) TR::ExternalRelocation(cursor,
867
(uint8_t *)helperSymRef,
868
TR_HelperAddress,
869
cg()),
870
__FILE__, __LINE__, getNode());
871
cursor += 4;
872
873
if (comp->target().is64Bit())
874
{
875
// 5 bytes of zeros to fill out the MOVRegImm64 instruction.
876
//
877
*(int32_t *)cursor = 0;
878
cursor += 4;
879
*cursor++ = 0x00;
880
}
881
else
882
{
883
// 3-byte NOP (executable).
884
//
885
cursor = cg()->generatePadding(cursor, 3);
886
}
887
888
// JMP interpreterStaticAndSpecialGlue
889
//
890
helperSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_X86interpreterStaticAndSpecialGlue);
891
892
*cursor++ = 0xe9; // JMP
893
*(int32_t *)cursor = cg()->branchDisplacementToHelperOrTrampoline(cursor + 4, helperSymRef);
894
895
cg()->addExternalRelocation(new (cg()->trHeapMemory()) TR::ExternalRelocation(cursor,
896
(uint8_t*)helperSymRef,
897
TR_HelperAddress,
898
cg()),
899
__FILE__, __LINE__, getNode());
900
cursor += 4;
901
902
// DW dispatch helper index for the method's return type.
903
// this argument is not in use and hence will be cleaned-up in a subsequent changeset.
904
cursor += 2;
905
906
// DD/DQ cpAddr
907
//
908
intptr_t cpAddr = (intptr_t)methodSymRef->getOwningMethod(comp)->constantPool();
909
*(intptr_t *)cursor = cpAddr;
910
911
cg()->addExternalRelocation(new (cg()->trHeapMemory()) TR::ExternalRelocation(cursor,
912
*(uint8_t **)cursor,
913
getNode() ? (uint8_t *)(uintptr_t)getNode()->getInlinedSiteIndex() : (uint8_t *)-1,
914
TR_ConstantPool,
915
cg()),
916
__FILE__, __LINE__, getNode());
917
cursor += sizeof(intptr_t);
918
919
// DD cpIndex
920
//
921
*(uint32_t *)cursor = methodSymRef->getCPIndexForVM();
922
cursor += 4;
923
}
924
else
925
{
926
// Resolved method dispatch.
927
//
928
// 64-bit
929
// ======
930
931
// (10) MOV rdi, 0x0000aabbccddeeff ; load RAM method
932
// (5) JMP interpreterStaticAndSpecialGlue
933
//
934
// 32-bit
935
// ======
936
//
937
// (5) MOV edi, 0xaabbccdd ; load RAM method
938
// (5) JMP interpreterStaticAndSpecialGlue
939
//
940
941
if (needToSetCodeLocation)
942
{
943
getSnippetLabel()->setCodeLocation(cursor);
944
}
945
946
//SD: for jitInduceOSR we don't need to set the RAM method (the method that the VM needs to start executing)
947
//because VM is going to figure what method to execute by looking up the jitPC in the GC map and finding
948
//the desired invoke bytecode.
949
if (!isJitInduceOSRCall)
950
{
951
#if defined(J9VM_OPT_JITSERVER)
952
intptr_t ramMethod = comp->isOutOfProcessCompilation() && !methodSymbol->isInterpreted() ?
953
(intptr_t)methodSymRef->getSymbol()->castToResolvedMethodSymbol()->getResolvedMethod()->getPersistentIdentifier() :
954
(intptr_t)methodSymbol->getMethodAddress();
955
#else
956
intptr_t ramMethod = (intptr_t)methodSymbol->getMethodAddress();
957
#endif /* defined(J9VM_OPT_JITSERVER) */
958
959
if (comp->target().is64Bit())
960
{
961
// MOV rdi, Imm64
962
//
963
*(uint16_t *)cursor = 0xbf48;
964
cursor += 2;
965
}
966
else
967
{
968
// MOV edi, Imm32
969
//
970
*cursor++ = 0xbf;
971
}
972
973
*(intptr_t *)cursor = ramMethod;
974
975
if (comp->getOption(TR_UseSymbolValidationManager))
976
{
977
cg()->addExternalRelocation(new (cg()->trHeapMemory()) TR::ExternalRelocation(cursor,
978
(uint8_t *)ramMethod,
979
(uint8_t *)TR::SymbolType::typeMethod,
980
TR_SymbolFromManager,
981
cg()),
982
__FILE__, __LINE__, getNode());
983
}
984
985
// HCR in TR::X86CallSnippet::emitSnippetBody register the method address
986
//
987
if (comp->getOption(TR_EnableHCR))
988
cg()->jitAddPicToPatchOnClassRedefinition((void *)ramMethod, (void *)cursor);
989
990
cursor += sizeof(intptr_t);
991
}
992
993
// JMP interpreterStaticAndSpecialGlue
994
//
995
*cursor++ = 0xe9;
996
997
TR::SymbolReference* dispatchSymRef =
998
methodSymbol->isHelper() && methodSymRef->isOSRInductionHelper() ? methodSymRef :
999
cg()->symRefTab()->findOrCreateRuntimeHelper(TR_X86interpreterStaticAndSpecialGlue);
1000
1001
*(int32_t *)cursor = cg()->branchDisplacementToHelperOrTrampoline(cursor + 4, dispatchSymRef);
1002
1003
cg()->addExternalRelocation(new (cg()->trHeapMemory()) TR::ExternalRelocation(cursor,
1004
(uint8_t *)dispatchSymRef,
1005
TR_HelperAddress,
1006
cg()),
1007
__FILE__, __LINE__, getNode());
1008
cursor += 4;
1009
}
1010
1011
return cursor;
1012
}
1013
1014
1015
uint32_t TR::X86CallSnippet::getLength(int32_t estimatedSnippetStart)
1016
{
1017
TR::Compilation *comp = cg()->comp();
1018
TR_J9VMBase *fej9 = (TR_J9VMBase *)(cg()->fe());
1019
uint32_t length = 0;
1020
1021
TR::SymbolReference *methodSymRef = _realMethodSymbolReference ? _realMethodSymbolReference : getNode()->getSymbolReference();
1022
TR::MethodSymbol *methodSymbol = methodSymRef->getSymbol()->castToMethodSymbol();
1023
1024
if (comp->target().is64Bit())
1025
{
1026
TR::Linkage *linkage = cg()->getLinkage(methodSymbol->getLinkageConvention());
1027
1028
int32_t codeSize;
1029
(void)linkage->storeArguments(getNode(), NULL, true, &codeSize);
1030
length += codeSize;
1031
}
1032
1033
bool forceUnresolvedDispatch = !fej9->isResolvedDirectDispatchGuaranteed(comp);
1034
if (methodSymRef->isUnresolved() || forceUnresolvedDispatch)
1035
{
1036
// +7 accounts for maximum length alignment padding.
1037
//
1038
if (comp->target().is64Bit())
1039
length += (7+10+5+2+8+4);
1040
else
1041
length += (7+5+3+5+2+4+4);
1042
}
1043
else
1044
{
1045
if (comp->target().is64Bit())
1046
length += (10+5);
1047
else
1048
length += (5+5);
1049
}
1050
1051
return length;
1052
}
1053
1054