Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/compiler/x/amd64/codegen/AMD64PrivateLinkage.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
#ifdef TR_TARGET_64BIT
24
25
#include "codegen/AMD64PrivateLinkage.hpp"
26
#include "codegen/AMD64JNILinkage.hpp"
27
28
#include <stdint.h>
29
#include "codegen/CodeGenerator.hpp"
30
#include "codegen/Linkage_inlines.hpp"
31
#include "codegen/Machine.hpp"
32
#include "compile/Method.hpp"
33
#include "control/Recompilation.hpp"
34
#include "control/RecompilationInfo.hpp"
35
#include "env/CHTable.hpp"
36
#include "env/IO.hpp"
37
#include "env/J2IThunk.hpp"
38
#include "env/VMJ9.h"
39
#include "env/VerboseLog.hpp"
40
#include "env/jittypes.h"
41
#include "il/Node.hpp"
42
#include "il/Node_inlines.hpp"
43
#include "il/ParameterSymbol.hpp"
44
#include "x/amd64/codegen/AMD64GuardedDevirtualSnippet.hpp"
45
#include "x/codegen/CallSnippet.hpp"
46
#include "x/codegen/CheckFailureSnippet.hpp"
47
#include "x/codegen/HelperCallSnippet.hpp"
48
#include "x/codegen/X86Instruction.hpp"
49
#include "codegen/InstOpCode.hpp"
50
51
////////////////////////////////////////////////
52
//
53
// Helpful definitions
54
//
55
// These are only here to make the rest of the code below somewhat
56
// self-documenting.
57
//
58
59
enum
60
{
61
RETURN_ADDRESS_SIZE=8,
62
NUM_INTEGER_LINKAGE_REGS=4,
63
NUM_FLOAT_LINKAGE_REGS=8,
64
};
65
66
////////////////////////////////////////////////
67
//
68
// Hack markers
69
//
70
// These things may become unnecessary as the implementation matures
71
//
72
73
// Hacks related to 8 byte slots
74
#define DOUBLE_SIZED_ARGS (1)
75
76
// Misc
77
#define INTERPRETER_CLOBBERS_XMMS (!debug("interpreterClobbersXmms"))
78
// Note AOT relocations assumes that this value is 0. Shall this value change, the AOT relocations also need to reflect this change.
79
#define VM_NEEDS_8_BYTE_CPINDEX (0)
80
81
82
////////////////////////////////////////////////
83
//
84
// Initialization
85
//
86
87
J9::X86::AMD64::PrivateLinkage::PrivateLinkage(TR::CodeGenerator *cg)
88
: J9::X86::PrivateLinkage(cg)
89
{
90
TR_J9VMBase *fej9 = (TR_J9VMBase *)(cg->fe());
91
const TR::RealRegister::RegNum noReg = TR::RealRegister::NoReg;
92
uint8_t r, p;
93
94
TR::RealRegister::RegNum metaReg = TR::RealRegister::ebp;
95
96
_properties._properties =
97
EightBytePointers | EightByteParmSlots
98
| IntegersInRegisters | LongsInRegisters | FloatsInRegisters
99
| NeedsThunksForIndirectCalls
100
| UsesRegsForHelperArgs
101
;
102
103
if (!fej9->pushesOutgoingArgsAtCallSite(cg->comp()))
104
_properties._properties |= CallerCleanup | ReservesOutgoingArgsInPrologue;
105
106
// Integer arguments
107
p=0;
108
_properties._firstIntegerArgumentRegister = p;
109
_properties._argumentRegisters[p++] = TR::RealRegister::eax;
110
_properties._argumentRegisters[p++] = TR::RealRegister::esi;
111
_properties._argumentRegisters[p++] = TR::RealRegister::edx;
112
_properties._argumentRegisters[p++] = TR::RealRegister::ecx;
113
TR_ASSERT(p == NUM_INTEGER_LINKAGE_REGS, "assertion failure");
114
_properties._numIntegerArgumentRegisters = NUM_INTEGER_LINKAGE_REGS;
115
116
// Float arguments
117
_properties._firstFloatArgumentRegister = p;
118
for(r=0; r<=7; r++)
119
_properties._argumentRegisters[p++] = TR::RealRegister::xmmIndex(r);
120
TR_ASSERT(p == NUM_INTEGER_LINKAGE_REGS + NUM_FLOAT_LINKAGE_REGS, "assertion failure");
121
_properties._numFloatArgumentRegisters = NUM_FLOAT_LINKAGE_REGS;
122
123
// Preserved
124
p=0;
125
_properties._preservedRegisters[p++] = TR::RealRegister::ebx;
126
_properties._preservedRegisterMapForGC = TR::RealRegister::ebxMask;
127
128
int32_t lastPreservedRegister = 9; // changed to 9 for liberty, it used to be 15
129
130
for (r=9; r<=lastPreservedRegister; r++)
131
{
132
_properties._preservedRegisters[p++] = TR::RealRegister::rIndex(r);
133
_properties._preservedRegisterMapForGC |= TR::RealRegister::gprMask(TR::RealRegister::rIndex(r));
134
}
135
_properties._numberOfPreservedGPRegisters = p;
136
137
if (!INTERPRETER_CLOBBERS_XMMS)
138
for (r=8; r<=15; r++)
139
{
140
_properties._preservedRegisters[p++] = TR::RealRegister::xmmIndex(r);
141
_properties._preservedRegisterMapForGC |= TR::RealRegister::xmmrMask(TR::RealRegister::xmmIndex(r));
142
}
143
144
_properties._numberOfPreservedXMMRegisters = p - _properties._numberOfPreservedGPRegisters;
145
_properties._maxRegistersPreservedInPrologue = p;
146
_properties._preservedRegisters[p++] = metaReg;
147
_properties._preservedRegisters[p++] = TR::RealRegister::esp;
148
_properties._numPreservedRegisters = p;
149
150
// Other
151
_properties._returnRegisters[0] = TR::RealRegister::eax;
152
_properties._returnRegisters[1] = TR::RealRegister::xmm0;
153
_properties._returnRegisters[2] = noReg;
154
155
_properties._scratchRegisters[0] = TR::RealRegister::edi;
156
_properties._scratchRegisters[1] = TR::RealRegister::r8;
157
_properties._numScratchRegisters = 2;
158
159
_properties._vtableIndexArgumentRegister = TR::RealRegister::r8;
160
_properties._j9methodArgumentRegister = TR::RealRegister::edi;
161
_properties._framePointerRegister = TR::RealRegister::esp;
162
_properties._methodMetaDataRegister = metaReg;
163
164
_properties._numberOfVolatileGPRegisters = 6; // rax, rsi, rdx, rcx, rdi, r8
165
_properties._numberOfVolatileXMMRegisters = INTERPRETER_CLOBBERS_XMMS? 16 : 8; // xmm0-xmm7
166
167
// Offsets relative to where the frame pointer *would* point if we had one;
168
// namely, the local with the highest address (ie. the "first" local)
169
setOffsetToFirstParm(RETURN_ADDRESS_SIZE);
170
_properties._offsetToFirstLocal = 0;
171
172
// TODO: Need a better way to build the flags so they match the info above
173
//
174
memset(_properties._registerFlags, 0, sizeof(_properties._registerFlags));
175
176
// Integer arguments/return
177
_properties._registerFlags[TR::RealRegister::eax] = IntegerArgument | IntegerReturn;
178
_properties._registerFlags[TR::RealRegister::esi] = IntegerArgument;
179
_properties._registerFlags[TR::RealRegister::edx] = IntegerArgument;
180
_properties._registerFlags[TR::RealRegister::ecx] = IntegerArgument;
181
182
// Float arguments/return
183
_properties._registerFlags[TR::RealRegister::xmm0] = FloatArgument | FloatReturn;
184
for(r=1; r <= 7; r++)
185
_properties._registerFlags[TR::RealRegister::xmmIndex(r)] = FloatArgument;
186
187
// Preserved
188
_properties._registerFlags[TR::RealRegister::ebx] = Preserved;
189
_properties._registerFlags[TR::RealRegister::esp] = Preserved;
190
_properties._registerFlags[metaReg] = Preserved;
191
for(r=9; r <= lastPreservedRegister; r++)
192
_properties._registerFlags[TR::RealRegister::rIndex(r)] = Preserved;
193
if(!INTERPRETER_CLOBBERS_XMMS)
194
for(r=8; r <= 15; r++)
195
_properties._registerFlags[TR::RealRegister::xmmIndex(r)] = Preserved;
196
197
198
p = 0;
199
if (TR::Machine::enableNewPickRegister())
200
{
201
// Volatiles that aren't linkage regs
202
if (TR::Machine::numGPRRegsWithheld(cg) == 0)
203
{
204
_properties._allocationOrder[p++] = TR::RealRegister::edi;
205
_properties._allocationOrder[p++] = TR::RealRegister::r8;
206
}
207
else
208
{
209
TR_ASSERT(TR::Machine::numGPRRegsWithheld(cg) == 2, "numRegsWithheld: only 0 and 2 currently supported");
210
}
211
212
// Linkage regs in reverse order
213
_properties._allocationOrder[p++] = TR::RealRegister::ecx;
214
_properties._allocationOrder[p++] = TR::RealRegister::edx;
215
_properties._allocationOrder[p++] = TR::RealRegister::esi;
216
_properties._allocationOrder[p++] = TR::RealRegister::eax;
217
}
218
// Preserved regs
219
_properties._allocationOrder[p++] = TR::RealRegister::ebx;
220
_properties._allocationOrder[p++] = TR::RealRegister::r9;
221
_properties._allocationOrder[p++] = TR::RealRegister::r10;
222
_properties._allocationOrder[p++] = TR::RealRegister::r11;
223
_properties._allocationOrder[p++] = TR::RealRegister::r12;
224
_properties._allocationOrder[p++] = TR::RealRegister::r13;
225
_properties._allocationOrder[p++] = TR::RealRegister::r14;
226
_properties._allocationOrder[p++] = TR::RealRegister::r15;
227
228
TR_ASSERT(p == machine()->getNumGlobalGPRs(), "assertion failure");
229
230
if (TR::Machine::enableNewPickRegister())
231
{
232
// Linkage regs in reverse order
233
if (TR::Machine::numRegsWithheld(cg) == 0)
234
{
235
_properties._allocationOrder[p++] = TR::RealRegister::xmm7;
236
_properties._allocationOrder[p++] = TR::RealRegister::xmm6;
237
}
238
else
239
{
240
TR_ASSERT(TR::Machine::numRegsWithheld(cg) == 2, "numRegsWithheld: only 0 and 2 currently supported");
241
}
242
_properties._allocationOrder[p++] = TR::RealRegister::xmm5;
243
_properties._allocationOrder[p++] = TR::RealRegister::xmm4;
244
_properties._allocationOrder[p++] = TR::RealRegister::xmm3;
245
_properties._allocationOrder[p++] = TR::RealRegister::xmm2;
246
_properties._allocationOrder[p++] = TR::RealRegister::xmm1;
247
_properties._allocationOrder[p++] = TR::RealRegister::xmm0;
248
}
249
// Preserved regs
250
_properties._allocationOrder[p++] = TR::RealRegister::xmm8;
251
_properties._allocationOrder[p++] = TR::RealRegister::xmm9;
252
_properties._allocationOrder[p++] = TR::RealRegister::xmm10;
253
_properties._allocationOrder[p++] = TR::RealRegister::xmm11;
254
_properties._allocationOrder[p++] = TR::RealRegister::xmm12;
255
_properties._allocationOrder[p++] = TR::RealRegister::xmm13;
256
_properties._allocationOrder[p++] = TR::RealRegister::xmm14;
257
_properties._allocationOrder[p++] = TR::RealRegister::xmm15;
258
259
TR_ASSERT(p == (machine()->getNumGlobalGPRs() + machine()->_numGlobalFPRs), "assertion failure");
260
}
261
262
263
////////////////////////////////////////////////
264
//
265
// Argument manipulation
266
//
267
268
static uint8_t *flushArgument(
269
uint8_t*cursor,
270
TR::InstOpCode::Mnemonic op,
271
TR::RealRegister::RegNum regIndex,
272
int32_t offset,
273
TR::CodeGenerator *cg)
274
{
275
/* TODO:AMD64: Share code with the other flushArgument */
276
cursor = TR::InstOpCode(op).binary(cursor);
277
278
// Mod | Reg | R/M
279
//
280
// 01 r 100 (8 bit displacement)
281
// 10 r 100 (32 bit displacement)
282
//
283
uint8_t ModRM = 0x04;
284
ModRM |= (offset >= -128 && offset <= 127) ? 0x40 : 0x80;
285
286
*(cursor - 1) = ModRM;
287
cg->machine()->getRealRegister(regIndex)->setRegisterFieldInModRM(cursor - 1);
288
289
// Scale = 0x00, Index = none, Base = rsp
290
//
291
*cursor++ = 0x24;
292
293
if (offset >= -128 && offset <= 127)
294
{
295
*cursor++ = (uint8_t)offset;
296
}
297
else
298
{
299
*(uint32_t *)cursor = offset;
300
cursor += 4;
301
}
302
303
return cursor;
304
}
305
306
static int32_t flushArgumentSize(
307
TR::InstOpCode::Mnemonic op,
308
int32_t offset)
309
{
310
int32_t size = TR::InstOpCode(op).length() + 1; // length including ModRM + 1 SIB
311
return size + (((offset >= -128 && offset <= 127)) ? 1 : 4);
312
}
313
314
uint8_t *J9::X86::AMD64::PrivateLinkage::flushArguments(
315
TR::Node *callNode,
316
uint8_t *cursor,
317
bool calculateSizeOnly,
318
int32_t *sizeOfFlushArea,
319
bool isReturnAddressOnStack,
320
bool isLoad)
321
{
322
// TODO:AMD64: Share code with the other flushArguments
323
TR::MethodSymbol *methodSymbol = callNode->getSymbol()->castToMethodSymbol();
324
325
int32_t numGPArgs = 0;
326
int32_t numFPArgs = 0;
327
int32_t argSize = argAreaSize(callNode);
328
int32_t offset = argSize;
329
bool needFlush = false;
330
331
// account for the return address in thunks and snippets.
332
if (isReturnAddressOnStack)
333
offset += sizeof(intptr_t);
334
335
TR::RealRegister::RegNum reg = TR::RealRegister::NoReg;
336
TR::InstOpCode::Mnemonic op = TR::InstOpCode::bad;
337
TR::DataType dt = TR::NoType;
338
339
if (calculateSizeOnly)
340
*sizeOfFlushArea = 0;
341
342
const uint8_t slotSize = DOUBLE_SIZED_ARGS? 8 : 4;
343
344
for (int i = callNode->getFirstArgumentIndex(); i < callNode->getNumChildren(); i++)
345
{
346
TR::DataType type = callNode->getChild(i)->getType();
347
dt = type.getDataType();
348
op = TR::Linkage::movOpcodes(isLoad? RegMem : MemReg, movType(dt));
349
350
switch (dt)
351
{
352
case TR::Int64:
353
offset -= slotSize;
354
355
// Longs take two slots. Deliberate fall through.
356
357
case TR::Int8:
358
case TR::Int16:
359
case TR::Int32:
360
case TR::Address:
361
offset -= slotSize;
362
if (numGPArgs < getProperties().getNumIntegerArgumentRegisters())
363
{
364
needFlush = true;
365
if (!calculateSizeOnly)
366
reg = getProperties().getIntegerArgumentRegister(numGPArgs);
367
}
368
numGPArgs++;
369
break;
370
371
case TR::Float:
372
case TR::Double:
373
offset -= ((dt == TR::Double) ? 2 : 1) * slotSize;
374
375
if (numFPArgs < getProperties().getNumFloatArgumentRegisters())
376
{
377
needFlush = true;
378
if (!calculateSizeOnly)
379
reg = getProperties().getFloatArgumentRegister(numFPArgs);
380
}
381
382
numFPArgs++;
383
break;
384
default:
385
break;
386
}
387
388
if (needFlush)
389
{
390
if (calculateSizeOnly)
391
*sizeOfFlushArea += flushArgumentSize(op, offset);
392
else
393
cursor = flushArgument(cursor, op, reg, offset, cg());
394
395
needFlush = false;
396
}
397
}
398
399
return cursor;
400
}
401
402
TR::Instruction *
403
J9::X86::AMD64::PrivateLinkage::generateFlushInstruction(
404
TR::Instruction *prev,
405
TR_MovOperandTypes operandType,
406
TR::DataType dataType,
407
TR::RealRegister::RegNum regIndex,
408
TR::Register *espReg,
409
int32_t offset,
410
TR::CodeGenerator *cg
411
)
412
{
413
TR::Instruction *result;
414
415
// Opcode
416
//
417
TR::InstOpCode::Mnemonic opCode = TR::Linkage::movOpcodes(operandType, movType(dataType));
418
419
// Registers
420
//
421
TR::Register *argReg = cg->allocateRegister(movRegisterKind(movType(dataType)));
422
423
TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions((uint8_t)2, 2, cg);
424
deps->addPreCondition (argReg, regIndex, cg);
425
deps->addPostCondition(argReg, regIndex, cg);
426
deps->addPreCondition (espReg, TR::RealRegister::esp, cg);
427
deps->addPostCondition(espReg, TR::RealRegister::esp, cg);
428
429
// Generate the instruction
430
//
431
TR::MemoryReference *memRef = generateX86MemoryReference(espReg, offset, cg);
432
switch (operandType)
433
{
434
case RegMem:
435
result = new (cg->trHeapMemory()) TR::X86RegMemInstruction(prev, opCode, argReg, memRef, deps, cg);
436
break;
437
case MemReg:
438
result = new (cg->trHeapMemory()) TR::X86MemRegInstruction(prev, opCode, memRef, argReg, deps, cg);
439
break;
440
default:
441
TR_ASSERT(0, "Flush instruction must be RegMem or MemReg");
442
result = NULL;
443
break;
444
}
445
446
// Cleanup
447
//
448
cg->stopUsingRegister(argReg);
449
450
return result;
451
}
452
453
TR::Instruction *
454
J9::X86::AMD64::PrivateLinkage::flushArguments(
455
TR::Instruction *prev,
456
TR::ResolvedMethodSymbol *methodSymbol,
457
bool isReturnAddressOnStack,
458
bool isLoad)
459
{
460
// TODO:AMD64: Share code with the other flushArguments
461
462
int32_t numGPArgs = 0;
463
int32_t numFPArgs = 0;
464
int32_t offset = argAreaSize(methodSymbol);
465
bool needFlush = false;
466
467
const uint8_t slotSize = DOUBLE_SIZED_ARGS? 8 : 4;
468
469
// account for the return address in SwitchToInterpreterPrePrologue
470
if (isReturnAddressOnStack)
471
offset += sizeof(intptr_t);
472
473
TR::RealRegister::RegNum reg;
474
TR::Register *espReg = cg()->allocateRegister();
475
476
ListIterator<TR::ParameterSymbol> parameterIterator(&methodSymbol->getParameterList());
477
TR::ParameterSymbol *parmCursor;
478
for (parmCursor = parameterIterator.getFirst(); parmCursor; parmCursor = parameterIterator.getNext())
479
{
480
TR::DataType type = parmCursor->getType();
481
TR::DataType dt = type.getDataType();
482
TR_MovOperandTypes ot = isLoad? RegMem : MemReg;
483
484
switch (dt)
485
{
486
case TR::Int64:
487
offset -= slotSize;
488
489
// Deliberate fall through
490
491
case TR::Int8:
492
case TR::Int16:
493
case TR::Int32:
494
case TR::Address:
495
offset -= slotSize;
496
if (numGPArgs < getProperties().getNumIntegerArgumentRegisters())
497
{
498
prev = generateFlushInstruction(prev, ot, dt, getProperties().getIntegerArgumentRegister(numGPArgs), espReg, offset, cg());
499
}
500
501
numGPArgs++;
502
break;
503
504
case TR::Double:
505
offset -= slotSize;
506
507
// Deliberate fall through
508
509
case TR::Float:
510
offset -= slotSize;
511
512
if (numFPArgs < getProperties().getNumFloatArgumentRegisters())
513
{
514
prev = generateFlushInstruction(prev, ot, dt, getProperties().getFloatArgumentRegister(numFPArgs), espReg, offset, cg());
515
}
516
517
numFPArgs++;
518
break;
519
default:
520
break;
521
}
522
523
}
524
525
cg()->stopUsingRegister(espReg);
526
527
return prev;
528
}
529
530
uint8_t *J9::X86::AMD64::PrivateLinkage::generateVirtualIndirectThunk(TR::Node *callNode)
531
{
532
int32_t codeSize;
533
TR::SymbolReference *glueSymRef;
534
uint8_t *thunk;
535
uint8_t *thunkEntry;
536
uint8_t *cursor;
537
TR::Compilation *comp = cg()->comp();
538
539
(void)storeArguments(callNode, NULL, true, &codeSize);
540
codeSize += 12; // +10 TR::InstOpCode::MOV8RegImm64 +2 TR::InstOpCode::JMPReg
541
542
TR_J9VMBase *fej9 = (TR_J9VMBase *)(cg()->fe());
543
544
// Allocate the thunk in the cold code section
545
//
546
if (fej9->storeOffsetToArgumentsInVirtualIndirectThunks())
547
{
548
codeSize += 8;
549
thunk = (uint8_t *)comp->trMemory()->allocateMemory(codeSize, heapAlloc);
550
cursor = thunkEntry = thunk + 8; // 4 bytes holding the size of storeArguments
551
}
552
else
553
{
554
thunk = (uint8_t *)cg()->allocateCodeMemory(codeSize, true);
555
cursor = thunkEntry = thunk;
556
}
557
558
switch (callNode->getDataType())
559
{
560
case TR::NoType:
561
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_AMD64icallVMprJavaSendVirtual0);
562
break;
563
case TR::Int64:
564
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_AMD64icallVMprJavaSendVirtualJ);
565
break;
566
case TR::Address:
567
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_AMD64icallVMprJavaSendVirtualL);
568
break;
569
case TR::Int32:
570
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_AMD64icallVMprJavaSendVirtual1);
571
break;
572
case TR::Float:
573
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_AMD64icallVMprJavaSendVirtualF);
574
break;
575
case TR::Double:
576
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_AMD64icallVMprJavaSendVirtualD);
577
break;
578
default:
579
TR_ASSERT(0, "Bad return data type '%s' for call node [" POINTER_PRINTF_FORMAT "]\n",
580
comp->getDebug()->getName(callNode->getDataType()),
581
callNode);
582
}
583
584
cursor = storeArguments(callNode, cursor, false, NULL);
585
586
if (fej9->storeOffsetToArgumentsInVirtualIndirectThunks())
587
{
588
*((int32_t *)thunk + 1) = cursor - thunkEntry;
589
}
590
591
// TR::InstOpCode::MOV8RegImm64 rdi, glueAddress
592
//
593
*(uint16_t *)cursor = 0xbf48;
594
cursor += 2;
595
*(uint64_t *)cursor = (uintptr_t)glueSymRef->getMethodAddress();
596
cursor += 8;
597
598
// TR::InstOpCode::JMPReg rdi
599
//
600
*(uint8_t *)cursor++ = 0xff;
601
*(uint8_t *)cursor++ = 0xe7;
602
603
if (fej9->storeOffsetToArgumentsInVirtualIndirectThunks())
604
*(int32_t *)thunk = cursor - thunkEntry;
605
606
diagnostic("\n-- ( Created J2I thunk " POINTER_PRINTF_FORMAT " for node " POINTER_PRINTF_FORMAT " )\n", thunkEntry, callNode);
607
608
return thunkEntry;
609
}
610
611
TR_J2IThunk *J9::X86::AMD64::PrivateLinkage::generateInvokeExactJ2IThunk(TR::Node *callNode, char *signature)
612
{
613
TR::Compilation *comp = cg()->comp();
614
615
// Allocate thunk storage
616
//
617
int32_t codeSize;
618
(void)storeArguments(callNode, NULL, true, &codeSize);
619
codeSize += 10; // TR::InstOpCode::MOV8RegImm64
620
if (comp->getOption(TR_BreakOnJ2IThunk))
621
codeSize += 1; // breakpoint opcode
622
if (comp->getOptions()->getVerboseOption(TR_VerboseJ2IThunks))
623
codeSize += 5; // JMPImm4
624
else
625
codeSize += 2; // TR::InstOpCode::JMPReg
626
627
TR_J2IThunkTable *thunkTable = comp->getPersistentInfo()->getInvokeExactJ2IThunkTable();
628
TR_J2IThunk *thunk = TR_J2IThunk::allocate(codeSize, signature, cg(), thunkTable);
629
uint8_t *cursor = thunk->entryPoint();
630
631
TR::SymbolReference *glueSymRef;
632
switch (callNode->getDataType())
633
{
634
case TR::NoType:
635
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_icallVMprJavaSendInvokeExact0);
636
break;
637
case TR::Int64:
638
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_icallVMprJavaSendInvokeExactJ);
639
break;
640
case TR::Address:
641
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_icallVMprJavaSendInvokeExactL);
642
break;
643
case TR::Int32:
644
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_icallVMprJavaSendInvokeExact1);
645
break;
646
case TR::Float:
647
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_icallVMprJavaSendInvokeExactF);
648
break;
649
case TR::Double:
650
glueSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_icallVMprJavaSendInvokeExactD);
651
break;
652
default:
653
TR_ASSERT(0, "Bad return data type '%s' for call node [" POINTER_PRINTF_FORMAT "]\n",
654
comp->getDebug()->getName(callNode->getDataType()),
655
callNode);
656
}
657
658
if (comp->getOption(TR_BreakOnJ2IThunk))
659
*cursor++ = 0xcc;
660
661
// TR::InstOpCode::MOV8RegImm64 rdi, glueAddress
662
//
663
*(uint16_t *)cursor = 0xbf48;
664
cursor += 2;
665
*(uint64_t *)cursor = (uintptr_t) cg()->fej9()->getInvokeExactThunkHelperAddress(comp, glueSymRef, callNode->getDataType());
666
cursor += 8;
667
668
// Arg stores
669
//
670
cursor = storeArguments(callNode, cursor, false, NULL);
671
672
if (comp->getOptions()->getVerboseOption(TR_VerboseJ2IThunks))
673
{
674
// JMPImm4 helper
675
//
676
*(uint8_t *)cursor++ = 0xe9;
677
TR::SymbolReference *helper = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_methodHandleJ2IGlue);
678
int32_t disp32 = cg()->branchDisplacementToHelperOrTrampoline(cursor+4, helper);
679
*(int32_t *)cursor = disp32;
680
cursor += 4;
681
}
682
else
683
{
684
// TR::InstOpCode::JMPReg rdi
685
//
686
*(uint8_t *)cursor++ = 0xff;
687
*(uint8_t *)cursor++ = 0xe7;
688
}
689
690
if (comp->getOption(TR_TraceCG))
691
{
692
traceMsg(comp, "\n-- ( Created invokeExact J2I thunk " POINTER_PRINTF_FORMAT " for node " POINTER_PRINTF_FORMAT " )", thunk, callNode);
693
}
694
695
return thunk;
696
}
697
698
TR::Instruction *J9::X86::AMD64::PrivateLinkage::savePreservedRegisters(TR::Instruction *cursor)
699
{
700
TR::ResolvedMethodSymbol *bodySymbol = comp()->getJittedMethodSymbol();
701
const int32_t localSize = _properties.getOffsetToFirstLocal() - bodySymbol->getLocalMappingCursor();
702
const int32_t pointerSize = _properties.getPointerSize();
703
704
705
int32_t offsetCursor = -localSize - pointerSize;
706
707
int32_t preservedRegStoreBytesSaved = 0;
708
if (_properties.getOffsetToFirstLocal() - bodySymbol->getLocalMappingCursor() > 0)
709
preservedRegStoreBytesSaved -= 4; // There's an extra mov rsp
710
711
for (int32_t pindex = _properties.getMaxRegistersPreservedInPrologue()-1;
712
pindex >= 0;
713
pindex--)
714
{
715
TR::RealRegister *scratch = machine()->getRealRegister(getProperties().getIntegerScratchRegister(0));
716
TR::RealRegister::RegNum idx = _properties.getPreservedRegister((uint32_t)pindex);
717
TR::RealRegister::RegNum r8 = TR::RealRegister::r8;
718
TR::RealRegister *reg = machine()->getRealRegister(idx);
719
if (reg->getHasBeenAssignedInMethod() &&
720
(reg->getState() != TR::RealRegister::Locked))
721
{
722
cursor = generateMemRegInstruction(
723
cursor,
724
TR::Linkage::movOpcodes(MemReg, fullRegisterMovType(reg)),
725
generateX86MemoryReference(machine()->getRealRegister(TR::RealRegister::vfp), offsetCursor, cg()),
726
reg,
727
cg()
728
);
729
offsetCursor -= pointerSize;
730
}
731
}
732
733
cursor = cg()->generateDebugCounter(cursor, "cg.prologues:no-preservedRegStoreBytesSaved", 1, TR::DebugCounter::Expensive);
734
735
return cursor;
736
}
737
738
TR::Instruction *J9::X86::AMD64::PrivateLinkage::restorePreservedRegisters(TR::Instruction *cursor)
739
{
740
TR::ResolvedMethodSymbol *bodySymbol = comp()->getJittedMethodSymbol();
741
const int32_t localSize = _properties.getOffsetToFirstLocal() - bodySymbol->getLocalMappingCursor();
742
const int32_t pointerSize = _properties.getPointerSize();
743
744
int32_t pindex;
745
int32_t offsetCursor = -localSize - _properties.getPointerSize();
746
for (pindex = _properties.getMaxRegistersPreservedInPrologue()-1;
747
pindex >= 0;
748
pindex--)
749
{
750
TR::RealRegister::RegNum idx = _properties.getPreservedRegister((uint32_t)pindex);
751
TR::RealRegister *reg = machine()->getRealRegister(idx);
752
if (reg->getHasBeenAssignedInMethod())
753
{
754
cursor = generateRegMemInstruction(
755
cursor,
756
TR::Linkage::movOpcodes(RegMem, fullRegisterMovType(reg)),
757
reg,
758
generateX86MemoryReference(machine()->getRealRegister(TR::RealRegister::vfp), offsetCursor, cg()),
759
cg()
760
);
761
offsetCursor -= _properties.getPointerSize();
762
}
763
}
764
cursor = cg()->generateDebugCounter(cursor, "cg.epilogues:no-preservedRegStoreBytesSaved", 1, TR::DebugCounter::Expensive);
765
return cursor;
766
}
767
768
////////////////////////////////////////////////
769
//
770
// Call node evaluation
771
//
772
773
int32_t J9::X86::AMD64::PrivateLinkage::argAreaSize(TR::ResolvedMethodSymbol *methodSymbol)
774
{
775
int32_t result = 0;
776
ListIterator<TR::ParameterSymbol> paramIterator(&(methodSymbol->getParameterList()));
777
TR::ParameterSymbol *paramCursor;
778
for (paramCursor = paramIterator.getFirst(); paramCursor; paramCursor = paramIterator.getNext())
779
{
780
result += paramCursor->getRoundedSize() * ((DOUBLE_SIZED_ARGS && paramCursor->getDataType() != TR::Address) ? 2 : 1);
781
}
782
return result;
783
}
784
785
int32_t J9::X86::AMD64::PrivateLinkage::argAreaSize(TR::Node *callNode)
786
{
787
// TODO: We only need this function because unresolved calls don't have a
788
// TR::ResolvedMethodSymbol, and only TR::ResolvedMethodSymbol has
789
// getParameterList(). If getParameterList() ever moves to TR::MethodSymbol,
790
// then this function becomes unnecessary.
791
//
792
TR::Node *child;
793
int32_t i;
794
int32_t result = 0;
795
int32_t firstArgument = callNode->getFirstArgumentIndex();
796
int32_t lastArgument = callNode->getNumChildren() - 1;
797
for (i=firstArgument; i <= lastArgument; i++)
798
{
799
child = callNode->getChild(i);
800
result += child->getRoundedSize() * ((DOUBLE_SIZED_ARGS && child->getDataType() != TR::Address) ? 2 : 1);
801
}
802
return result;
803
}
804
805
int32_t J9::X86::AMD64::PrivateLinkage::buildArgs(TR::Node *callNode,
806
TR::RegisterDependencyConditions *dependencies)
807
{
808
TR::MethodSymbol *methodSymbol = callNode->getSymbol()->getMethodSymbol();
809
TR::SymbolReference *methodSymRef = callNode->getSymbolReference();
810
bool passArgsOnStack;
811
bool rightToLeft = methodSymbol && methodSymbol->isHelper()
812
&& !methodSymRef->isOSRInductionHelper(); //we want the arguments for induceOSR to be passed from left to right as in any other non-helper call
813
if (callNode->getOpCode().isIndirect())
814
{
815
if (methodSymbol->isVirtual() &&
816
!methodSymRef->isUnresolved() &&
817
!comp()->getOption(TR_FullSpeedDebug) && // On FSD we need to call these through the vtable (because the methodIsOverridden flag is unreliable) so we need args to be in regs
818
methodSymbol->isVMInternalNative())
819
{
820
TR_ResolvedMethod *resolvedMethod = methodSymbol->castToResolvedMethodSymbol()->getResolvedMethod();
821
passArgsOnStack = (
822
!resolvedMethod->virtualMethodIsOverridden() &&
823
!resolvedMethod->isAbstract());
824
}
825
else
826
{
827
passArgsOnStack = false;
828
}
829
}
830
else
831
{
832
passArgsOnStack = methodSymbol->isVMInternalNative() && cg()->supportVMInternalNatives();
833
}
834
835
switch (callNode->getSymbol()->castToMethodSymbol()->getMandatoryRecognizedMethod())
836
{
837
case TR::java_lang_invoke_ComputedCalls_dispatchJ9Method:
838
passArgsOnStack = true;
839
break;
840
default:
841
break;
842
}
843
844
#ifdef J9VM_OPT_JAVA_CRYPTO_ACCELERATION
845
// Violate the right-to-left linkage requirement for JIT helpers for the AES helpers.
846
// Call them with Java method private linkage.
847
//
848
if (cg()->enableAESInHardwareTransformations())
849
{
850
if (methodSymbol && methodSymbol->isHelper())
851
{
852
switch (methodSymRef->getReferenceNumber())
853
{
854
case TR_doAESInHardwareInner:
855
case TR_expandAESKeyInHardwareInner:
856
rightToLeft = false;
857
break;
858
859
default:
860
break;
861
}
862
}
863
}
864
#endif
865
866
return buildPrivateLinkageArgs(callNode, dependencies, rightToLeft, passArgsOnStack);
867
}
868
869
int32_t J9::X86::AMD64::PrivateLinkage::buildPrivateLinkageArgs(TR::Node *callNode,
870
TR::RegisterDependencyConditions *dependencies,
871
bool rightToLeft,
872
bool passArgsOnStack)
873
{
874
TR::RealRegister::RegNum noReg = TR::RealRegister::NoReg;
875
TR::RealRegister *stackPointer = machine()->getRealRegister(TR::RealRegister::esp);
876
int32_t firstArgument = callNode->getFirstArgumentIndex();
877
int32_t lastArgument = callNode->getNumChildren() - 1;
878
int32_t offset = 0;
879
880
uint16_t numIntArgs = 0,
881
numFloatArgs = 0,
882
numSpecialArgs = 0;
883
884
int numDupedArgRegs = 0;
885
TR::Register *dupedArgRegs[NUM_INTEGER_LINKAGE_REGS + NUM_FLOAT_LINKAGE_REGS];
886
887
// Even though the parameters will be passed on the stack, create dummy linkage registers
888
// to ensure that if the call were to be made using the linkage registers (e.g., in a guarded
889
// devirtual snippet if it was overridden) then the registers would be available at this
890
// point.
891
//
892
bool createDummyLinkageRegisters = (callNode->getOpCode().isIndirect() && passArgsOnStack) ? true: false;
893
894
int32_t parmAreaSize = argAreaSize(callNode);
895
896
int32_t start, stop, step;
897
if (rightToLeft || getProperties().passArgsRightToLeft())
898
{
899
start = lastArgument;
900
stop = firstArgument - 1;
901
step = -1;
902
TR_ASSERT(stop <= start, "Loop must terminate");
903
}
904
else
905
{
906
start = firstArgument;
907
stop = lastArgument + 1;
908
step = 1;
909
TR_ASSERT(stop >= start, "Loop must terminate");
910
}
911
912
// we're going to align the stack depend on the alignment property
913
// adjust = something
914
// allocateSize = parmAreaSize + adjust;
915
// then subtract stackpointer with allocateSize
916
uint32_t alignedParmAreaSize = parmAreaSize;
917
918
if (!getProperties().getReservesOutgoingArgsInPrologue() && !callNode->getSymbol()->castToMethodSymbol()->isHelper())
919
{
920
// Align the stack for alignment larger than 16 bytes(stack is aligned to the multiple of sizeOfJavaPointer by default, which is 4 bytes on 32-bit, and 8 bytes on 64-bit)
921
// Basically, we're aligning the start of next frame (after the return address) to a multiple of staceFrameAlignmentInBytes
922
// Stack frame alignment is needed for stack allocated object alignment
923
uint32_t stackFrameAlignment = cg()->fej9()->getLocalObjectAlignmentInBytes();
924
if (stackFrameAlignment > TR::Compiler->om.sizeofReferenceAddress())
925
{
926
uint32_t frameSize = parmAreaSize + cg()->getFrameSizeInBytes() + getProperties().getRetAddressWidth();
927
frameSize += stackFrameAlignment - (frameSize % stackFrameAlignment);
928
alignedParmAreaSize = frameSize - cg()->getFrameSizeInBytes() - getProperties().getRetAddressWidth();
929
930
if (comp()->getOption(TR_TraceCG))
931
{
932
traceMsg(comp(), "parm area size was %d, and is aligned to %d\n", parmAreaSize, alignedParmAreaSize);
933
}
934
}
935
if (alignedParmAreaSize > 0)
936
generateRegImmInstruction((alignedParmAreaSize <= 127 ? TR::InstOpCode::SUBRegImms() : TR::InstOpCode::SUBRegImm4()), callNode, stackPointer, alignedParmAreaSize, cg());
937
}
938
939
int32_t i;
940
for (i = start; i != stop; i += step)
941
{
942
TR::Node *child = callNode->getChild(i);
943
TR::DataType type = child->getType();
944
TR::RealRegister::RegNum rregIndex = noReg;
945
TR::DataType dt = type.getDataType();
946
947
switch (dt)
948
{
949
case TR::Float:
950
case TR::Double:
951
rregIndex =
952
(numFloatArgs < NUM_FLOAT_LINKAGE_REGS) && (!passArgsOnStack || createDummyLinkageRegisters)
953
? getProperties().getFloatArgumentRegister(numFloatArgs)
954
: noReg
955
;
956
numFloatArgs++;
957
break;
958
case TR::Address:
959
default:
960
{
961
if (i == firstArgument && !callNode->getSymbolReference()->isUnresolved())
962
{
963
// TODO:JSR292: This should really be in the front-end
964
TR::MethodSymbol *sym = callNode->getSymbol()->castToMethodSymbol();
965
switch (sym->getMandatoryRecognizedMethod())
966
{
967
case TR::java_lang_invoke_ComputedCalls_dispatchJ9Method:
968
rregIndex = getProperties().getJ9MethodArgumentRegister();
969
numSpecialArgs++;
970
break;
971
case TR::java_lang_invoke_ComputedCalls_dispatchVirtual:
972
case TR::com_ibm_jit_JITHelpers_dispatchVirtual:
973
rregIndex = getProperties().getVTableIndexArgumentRegister();
974
numSpecialArgs++;
975
break;
976
default:
977
break;
978
}
979
}
980
if (rregIndex == noReg)
981
{
982
rregIndex =
983
(numIntArgs < NUM_INTEGER_LINKAGE_REGS) && (!passArgsOnStack || createDummyLinkageRegisters)
984
? getProperties().getIntegerArgumentRegister(numIntArgs)
985
: noReg
986
;
987
numIntArgs++;
988
}
989
break;
990
}
991
}
992
993
bool willKeepConstRegLiveAcrossCall = true;
994
rcount_t oldRefCount = child->getReferenceCount();
995
TR::Register *childReg = child->getRegister();
996
if (child->getOpCode().isLoadConst() &&
997
!childReg &&
998
(callNode->getSymbol()->castToMethodSymbol()->isStatic() ||
999
callNode->getChild(callNode->getFirstArgumentIndex()) != child))
1000
{
1001
child->setReferenceCount(1);
1002
willKeepConstRegLiveAcrossCall = false;
1003
}
1004
1005
TR::Register *vreg = NULL;
1006
if ((rregIndex != noReg) ||
1007
(child->getOpCodeValue() != TR::iconst) ||
1008
(child->getOpCodeValue() == TR::iconst && childReg))
1009
vreg = cg()->evaluate(child);
1010
1011
if (rregIndex != noReg)
1012
{
1013
bool needsZeroExtension = (child->getDataType() == TR::Int32) && !vreg->areUpperBitsZero();
1014
TR::Register *preCondReg;
1015
1016
if (/*!child->getOpCode().isLoadConst() &&*/ (child->getReferenceCount() > 1))
1017
{
1018
preCondReg = cg()->allocateRegister(vreg->getKind());
1019
if (vreg->containsCollectedReference())
1020
preCondReg->setContainsCollectedReference();
1021
1022
generateRegRegInstruction(needsZeroExtension? TR::InstOpCode::MOVZXReg8Reg4 : TR::Linkage::movOpcodes(RegReg, movType(child->getDataType())), child, preCondReg, vreg, cg());
1023
vreg = preCondReg;
1024
1025
TR_ASSERT(numDupedArgRegs < NUM_INTEGER_LINKAGE_REGS + NUM_FLOAT_LINKAGE_REGS, "assertion failure");
1026
dupedArgRegs[numDupedArgRegs++] = preCondReg;
1027
}
1028
else
1029
{
1030
preCondReg = vreg;
1031
if (needsZeroExtension)
1032
generateRegRegInstruction(TR::InstOpCode::MOVZXReg8Reg4, child, preCondReg, vreg, cg());
1033
}
1034
1035
dependencies->addPreCondition(preCondReg, rregIndex, cg());
1036
}
1037
1038
offset += child->getRoundedSize() * ((DOUBLE_SIZED_ARGS && dt != TR::Address) ? 2 : 1);
1039
1040
if ((rregIndex == noReg) || (rregIndex != noReg && createDummyLinkageRegisters))
1041
{
1042
if (vreg)
1043
generateMemRegInstruction(
1044
TR::Linkage::movOpcodes(MemReg, fullRegisterMovType(vreg)),
1045
child,
1046
generateX86MemoryReference(stackPointer, parmAreaSize-offset, cg()),
1047
vreg,
1048
cg()
1049
);
1050
else
1051
{
1052
int32_t konst = child->getInt();
1053
generateMemImmInstruction(
1054
TR::InstOpCode::S8MemImm4,
1055
child,
1056
generateX86MemoryReference(stackPointer, parmAreaSize-offset, cg()),
1057
konst,
1058
cg()
1059
);
1060
}
1061
}
1062
1063
if (vreg)
1064
{
1065
cg()->decReferenceCount(child);
1066
////if (child->getOpCode().isLoadConst() && !childReg)
1067
if (!willKeepConstRegLiveAcrossCall)
1068
{
1069
child->setReferenceCount(oldRefCount-1);
1070
child->setRegister(NULL);
1071
}
1072
}
1073
else
1074
child->setReferenceCount(oldRefCount-1);
1075
}
1076
1077
1078
// Now that we're finished making the precondition, all the interferences
1079
// are established, and we can stopUsing these regs.
1080
//
1081
for (i = 0; i < numDupedArgRegs; i++)
1082
{
1083
cg()->stopUsingRegister(dupedArgRegs[i]);
1084
}
1085
1086
// Sanity check
1087
//
1088
TR_ASSERT(numIntArgs + numFloatArgs + numSpecialArgs == callNode->getNumChildren() - callNode->getFirstArgumentIndex(), "assertion failure");
1089
TR_ASSERT(offset == parmAreaSize, "assertion failure");
1090
1091
return alignedParmAreaSize;
1092
}
1093
1094
1095
static TR_AtomicRegion X86PicSlotAtomicRegion[] =
1096
{
1097
{ 0x0, 8 }, // Maximum instruction size that can be patched atomically.
1098
{ 0,0 }
1099
};
1100
1101
1102
TR::Instruction *J9::X86::AMD64::PrivateLinkage::buildPICSlot(TR::X86PICSlot picSlot, TR::LabelSymbol *mismatchLabel, TR::LabelSymbol *doneLabel, TR::X86CallSite &site)
1103
{
1104
TR::Register *cachedAddressRegister = cg()->allocateRegister();
1105
1106
TR::Node *node = site.getCallNode();
1107
uint64_t addrToBeCompared = (uint64_t) picSlot.getClassAddress();
1108
TR::Instruction *firstInstruction;
1109
if (picSlot.getMethodAddress())
1110
{
1111
addrToBeCompared = (uint64_t) picSlot.getMethodAddress();
1112
firstInstruction = generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, node, cachedAddressRegister, addrToBeCompared, cg());
1113
}
1114
else
1115
firstInstruction = generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, node, cachedAddressRegister, (uint64_t)picSlot.getClassAddress(), cg());
1116
1117
1118
firstInstruction->setNeedsGCMap(site.getPreservedRegisterMask());
1119
1120
if (!site.getFirstPICSlotInstruction())
1121
site.setFirstPICSlotInstruction(firstInstruction);
1122
1123
if (picSlot.needsPicSlotAlignment())
1124
{
1125
generateBoundaryAvoidanceInstruction(
1126
X86PicSlotAtomicRegion,
1127
8,
1128
8,
1129
firstInstruction,
1130
cg());
1131
}
1132
1133
TR::Register *vftReg = site.evaluateVFT();
1134
1135
// The ordering of these registers in this compare instruction is important in order to get the
1136
// VFT register into the ModRM r/m field of the generated instruction.
1137
//
1138
if (picSlot.getMethodAddress())
1139
generateMemRegInstruction(TR::InstOpCode::CMP8MemReg, node,
1140
generateX86MemoryReference(vftReg, picSlot.getSlot(), cg()), cachedAddressRegister, cg());
1141
else
1142
generateRegRegInstruction(TR::InstOpCode::CMP8RegReg, node, cachedAddressRegister, vftReg, cg());
1143
1144
cg()->stopUsingRegister(cachedAddressRegister);
1145
1146
if (picSlot.needsJumpOnNotEqual())
1147
{
1148
if (picSlot.needsLongConditionalBranch())
1149
{
1150
generateLongLabelInstruction(TR::InstOpCode::JNE4, node, mismatchLabel, cg());
1151
}
1152
else
1153
{
1154
TR::InstOpCode::Mnemonic op = picSlot.needsShortConditionalBranch() ? TR::InstOpCode::JNE1 : TR::InstOpCode::JNE4;
1155
generateLabelInstruction(op, node, mismatchLabel, cg());
1156
}
1157
}
1158
else if (picSlot.needsJumpOnEqual())
1159
{
1160
if (picSlot.needsLongConditionalBranch())
1161
{
1162
generateLongLabelInstruction(TR::InstOpCode::JE4, node, mismatchLabel, cg());
1163
}
1164
else
1165
{
1166
TR::InstOpCode::Mnemonic op = picSlot.needsShortConditionalBranch() ? TR::InstOpCode::JE1 : TR::InstOpCode::JE4;
1167
generateLabelInstruction(op, node, mismatchLabel, cg());
1168
}
1169
}
1170
1171
TR::Instruction *instr;
1172
if (picSlot.getMethod())
1173
{
1174
TR::SymbolReference * callSymRef = comp()->getSymRefTab()->findOrCreateMethodSymbol(
1175
node->getSymbolReference()->getOwningMethodIndex(), -1, picSlot.getMethod(), TR::MethodSymbol::Virtual);
1176
1177
instr = generateImmSymInstruction(TR::InstOpCode::CALLImm4, node, (intptr_t)picSlot.getMethod()->startAddressForJittedMethod(), callSymRef, cg());
1178
}
1179
else if (picSlot.getHelperMethodSymbolRef())
1180
{
1181
TR::MethodSymbol *helperMethod = picSlot.getHelperMethodSymbolRef()->getSymbol()->castToMethodSymbol();
1182
instr = generateImmSymInstruction(TR::InstOpCode::CALLImm4, node, (uint32_t)(uintptr_t)helperMethod->getMethodAddress(), picSlot.getHelperMethodSymbolRef(), cg());
1183
}
1184
else
1185
{
1186
instr = generateImmInstruction(TR::InstOpCode::CALLImm4, node, 0, cg());
1187
}
1188
1189
instr->setNeedsGCMap(site.getPreservedRegisterMask());
1190
1191
// Put a GC map on this label, since the instruction after it may provide
1192
// the return address in this stack frame while PicBuilder is active
1193
//
1194
// TODO: Can we get rid of some of these? Maybe they don't cost anything.
1195
//
1196
if (picSlot.needsJumpToDone())
1197
{
1198
instr = generateLabelInstruction(TR::InstOpCode::JMP4, node, doneLabel, cg());
1199
instr->setNeedsGCMap(site.getPreservedRegisterMask());
1200
}
1201
1202
if (picSlot.generateNextSlotLabelInstruction())
1203
{
1204
generateLabelInstruction(TR::InstOpCode::label, node, mismatchLabel, cg());
1205
}
1206
1207
return firstInstruction;
1208
}
1209
1210
1211
static TR_AtomicRegion amd64IPicAtomicRegions[] =
1212
{
1213
{ 0x02, 8 }, // Class test immediate 1
1214
{ 0x1a, 8 }, // Class test immediate 2
1215
{ 0x3b, 4 }, // IPICInit call displacement
1216
{ 0,0 } // (null terminator)
1217
1218
// Some other important regions
1219
//{ 0x10, 4 }, // Call displacement 1 (no race here)
1220
//{ 0x28, 4 }, // Call displacement 2 (no race here)
1221
};
1222
1223
void J9::X86::AMD64::PrivateLinkage::buildIPIC(TR::X86CallSite &site, TR::LabelSymbol *entryLabel, TR::LabelSymbol *doneLabel, uint8_t *thunk)
1224
{
1225
TR_J9VMBase *fej9 = (TR_J9VMBase *)(cg()->fe());
1226
int32_t numIPICs = 0;
1227
1228
TR_ASSERT(doneLabel, "a doneLabel is required for PIC dispatches");
1229
1230
if (entryLabel)
1231
generateLabelInstruction(TR::InstOpCode::label, site.getCallNode(), entryLabel, cg());
1232
1233
int32_t numIPicSlots = IPicParameters.defaultNumberOfSlots;
1234
1235
TR::SymbolReference *callHelperSymRef =
1236
cg()->symRefTab()->findOrCreateRuntimeHelper(TR_X86populateIPicSlotCall, true, true, false);
1237
1238
static char *interfaceDispatchUsingLastITableStr = feGetEnv("TR_interfaceDispatchUsingLastITable");
1239
static char *numIPicSlotsStr = feGetEnv("TR_numIPicSlots");
1240
static char *numIPicSlotsBeforeLastITable = feGetEnv("TR_numIPicSlotsBeforeLastITable");
1241
static char *breakBeforeIPICUsingLastITable = feGetEnv("TR_breakBeforeIPICUsingLastITable");
1242
1243
if (numIPicSlotsStr)
1244
numIPicSlots = atoi(numIPicSlotsStr);
1245
1246
bool useLastITableCache = site.useLastITableCache() || interfaceDispatchUsingLastITableStr;
1247
if (useLastITableCache)
1248
{
1249
if (numIPicSlotsBeforeLastITable)
1250
{
1251
numIPicSlots = atoi(numIPicSlotsBeforeLastITable);
1252
}
1253
if (breakBeforeIPICUsingLastITable)
1254
{
1255
// This will occasionally put a break before an IPIC that does not use
1256
// the lastITable cache, because we might still change our mind
1257
// below. However, this is where we want the break instruction, so
1258
// let's just lay it down. It's likely not worth the effort to get
1259
// this exactly right in all cases.
1260
//
1261
generateInstruction(TR::InstOpCode::INT3, site.getCallNode(), cg());
1262
}
1263
}
1264
1265
1266
if (numIPicSlots > 1)
1267
{
1268
TR::X86PICSlot emptyPicSlot = TR::X86PICSlot(IPicParameters.defaultSlotAddress, NULL);
1269
emptyPicSlot.setNeedsShortConditionalBranch();
1270
emptyPicSlot.setJumpOnNotEqual();
1271
emptyPicSlot.setNeedsPicSlotAlignment();
1272
emptyPicSlot.setHelperMethodSymbolRef(callHelperSymRef);
1273
emptyPicSlot.setGenerateNextSlotLabelInstruction();
1274
1275
// Generate all slots except the last
1276
// (short branch to next slot, jump to doneLabel)
1277
//
1278
for (int32_t i = 1; i < numIPicSlots; i++)
1279
{
1280
TR::LabelSymbol *nextSlotLabel = generateLabelSymbol(cg());
1281
buildPICSlot(emptyPicSlot, nextSlotLabel, doneLabel, site);
1282
}
1283
}
1284
1285
// Configure the last slot
1286
//
1287
TR::LabelSymbol *lookupDispatchSnippetLabel = generateLabelSymbol(cg());
1288
TR::X86PICSlot lastPicSlot = TR::X86PICSlot(IPicParameters.defaultSlotAddress, NULL, false);
1289
lastPicSlot.setJumpOnNotEqual();
1290
lastPicSlot.setNeedsPicSlotAlignment();
1291
lastPicSlot.setHelperMethodSymbolRef(callHelperSymRef);
1292
TR::Instruction *slotPatchInstruction = NULL;
1293
1294
TR::Method *method = site.getMethodSymbol()->getMethod();
1295
TR_OpaqueClassBlock *declaringClass = NULL;
1296
uintptr_t itableIndex;
1297
if ( useLastITableCache
1298
&& (declaringClass = site.getSymbolReference()->getOwningMethod(comp())->getResolvedInterfaceMethod(site.getSymbolReference()->getCPIndex(), &itableIndex))
1299
&& performTransformation(comp(), "O^O useLastITableCache for n%dn itableIndex=%d: %.*s.%.*s%.*s\n",
1300
site.getCallNode()->getGlobalIndex(), (int)itableIndex,
1301
method->classNameLength(), method->classNameChars(),
1302
method->nameLength(), method->nameChars(),
1303
method->signatureLength(), method->signatureChars()))
1304
{
1305
buildInterfaceDispatchUsingLastITable (site, numIPicSlots, lastPicSlot, slotPatchInstruction, doneLabel, lookupDispatchSnippetLabel, declaringClass, itableIndex);
1306
}
1307
else
1308
{
1309
// Last PIC slot is long branch to lookup snippet, fall through to doneLabel
1310
lastPicSlot.setNeedsLongConditionalBranch();
1311
slotPatchInstruction = buildPICSlot(lastPicSlot, lookupDispatchSnippetLabel, NULL, site);
1312
}
1313
1314
TR::Instruction *startOfPicInstruction = site.getFirstPICSlotInstruction();
1315
TR::X86PicDataSnippet *snippet = new (trHeapMemory()) TR::X86PicDataSnippet(
1316
numIPicSlots,
1317
startOfPicInstruction,
1318
lookupDispatchSnippetLabel,
1319
doneLabel,
1320
site.getSymbolReference(),
1321
slotPatchInstruction,
1322
site.getThunkAddress(),
1323
true,
1324
cg());
1325
1326
snippet->gcMap().setGCRegisterMask(site.getPreservedRegisterMask());
1327
cg()->addSnippet(snippet);
1328
1329
cg()->incPicSlotCountBy(IPicParameters.defaultNumberOfSlots);
1330
numIPICs = IPicParameters.defaultNumberOfSlots;
1331
1332
cg()->reserveNTrampolines(numIPICs);
1333
}
1334
1335
void J9::X86::AMD64::PrivateLinkage::buildVirtualOrComputedCall(TR::X86CallSite &site, TR::LabelSymbol *entryLabel, TR::LabelSymbol *doneLabel, uint8_t *thunk)
1336
{
1337
TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp()->fe());
1338
if (entryLabel)
1339
generateLabelInstruction(TR::InstOpCode::label, site.getCallNode(), entryLabel, cg());
1340
1341
TR::SymbolReference *methodSymRef = site.getSymbolReference();
1342
if (comp()->getOption(TR_TraceCG))
1343
{
1344
traceMsg(comp(), "buildVirtualOrComputedCall(%p), isComputed=%d\n", site.getCallNode(), methodSymRef->getSymbol()->castToMethodSymbol()->isComputed());
1345
}
1346
1347
bool evaluateVftEarly = methodSymRef->isUnresolved()
1348
|| !fej9->isResolvedVirtualDispatchGuaranteed(comp());
1349
1350
if (methodSymRef->getSymbol()->castToMethodSymbol()->isComputed())
1351
{
1352
buildVFTCall(site, TR::InstOpCode::CALLReg, site.evaluateVFT(), NULL);
1353
}
1354
else if (evaluateVftEarly)
1355
{
1356
site.evaluateVFT(); // We must evaluate the VFT here to avoid a later evaluation that pollutes the VPic shape.
1357
buildVPIC(site, entryLabel, doneLabel);
1358
}
1359
else if (site.resolvedVirtualShouldUseVFTCall())
1360
{
1361
// Call through VFT
1362
//
1363
if (comp()->compileRelocatableCode())
1364
{
1365
// Non-SVM AOT still has to force unresolved virtual dispatch, which
1366
// works there because it won't transform other things into virtual
1367
// calls, e.g. invokeinterface of an Object method.
1368
TR_ASSERT_FATAL(
1369
comp()->getOption(TR_UseSymbolValidationManager),
1370
"resolved virtual dispatch in AOT requires SVM");
1371
1372
void *thunk = site.getThunkAddress();
1373
TR_OpaqueMethodBlock *method = methodSymRef
1374
->getSymbol()
1375
->castToResolvedMethodSymbol()
1376
->getResolvedMethod()
1377
->getPersistentIdentifier();
1378
1379
comp()->getSymbolValidationManager()
1380
->addJ2IThunkFromMethodRecord(thunk, method);
1381
}
1382
1383
buildVFTCall(site, TR::InstOpCode::CALLMem, NULL, generateX86MemoryReference(site.evaluateVFT(), methodSymRef->getOffset(), cg()));
1384
}
1385
else
1386
{
1387
site.evaluateVFT(); // We must evaluate the VFT here to avoid a later evaluation that pollutes the VPic shape.
1388
buildVPIC(site, entryLabel, doneLabel);
1389
}
1390
}
1391
1392
TR::Register *J9::X86::AMD64::PrivateLinkage::buildJNIDispatch(TR::Node *callNode)
1393
{
1394
TR_ASSERT(0, "AMD64 implements JNI dispatch using a system linkage");
1395
return NULL;
1396
}
1397
1398
#endif
1399
1400