Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/compiler/x/amd64/codegen/AMD64JNILinkage.cpp
6004 views
1
/*******************************************************************************
2
* Copyright (c) 2000, 2021 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/AMD64JNILinkage.hpp"
26
27
#include <algorithm>
28
#include "codegen/CodeGenerator.hpp"
29
#include "codegen/Linkage_inlines.hpp"
30
#include "codegen/Machine.hpp"
31
#include "codegen/MemoryReference.hpp"
32
#include "codegen/RealRegister.hpp"
33
#include "codegen/Register.hpp"
34
#include "codegen/RegisterDependency.hpp"
35
#include "codegen/Snippet.hpp"
36
#include "compile/ResolvedMethod.hpp"
37
#include "env/CompilerEnv.hpp"
38
#include "env/jittypes.h"
39
#include "il/LabelSymbol.hpp"
40
#include "il/MethodSymbol.hpp"
41
#include "il/Node.hpp"
42
#include "il/Node_inlines.hpp"
43
#include "il/RegisterMappedSymbol.hpp"
44
#include "il/ResolvedMethodSymbol.hpp"
45
#include "il/StaticSymbol.hpp"
46
#include "il/Symbol.hpp"
47
#include "il/SymbolReference.hpp"
48
#include "infra/Assert.hpp"
49
#include "env/VMJ9.h"
50
#include "runtime/Runtime.hpp"
51
#include "x/codegen/CheckFailureSnippet.hpp"
52
#include "x/codegen/HelperCallSnippet.hpp"
53
#include "x/codegen/X86Instruction.hpp"
54
#include "x/codegen/J9LinkageUtils.hpp"
55
56
TR::Register *J9::X86::AMD64::JNILinkage::processJNIReferenceArg(TR::Node *child)
57
{
58
TR::Register *refReg;
59
60
if (child->getOpCodeValue() == TR::loadaddr)
61
{
62
TR::SymbolReference *symRef = child->getSymbolReference();
63
TR::StaticSymbol *staticSym = symRef->getSymbol()->getStaticSymbol();
64
bool needsNullParameterCheck = false;
65
66
if (staticSym)
67
{
68
// Address taken of static.
69
//
70
refReg = cg()->evaluate(child);
71
if (!staticSym->isAddressOfClassObject())
72
needsNullParameterCheck = true;
73
}
74
else
75
{
76
// Address taken of a parm or local.
77
//
78
if (child->pointsToNull())
79
{
80
refReg = cg()->allocateRegister();
81
generateRegRegInstruction(TR::InstOpCode::XORRegReg(), child, refReg, refReg, cg());
82
// TODO (81564): We need to kill the scratch register to prevent an
83
// assertion error, but is this the right place to do so?
84
cg()->stopUsingRegister(refReg);
85
}
86
else
87
{
88
refReg = cg()->evaluate(child);
89
if (!child->pointsToNonNull())
90
needsNullParameterCheck = true;
91
}
92
}
93
94
if (needsNullParameterCheck)
95
{
96
generateMemImmInstruction(TR::InstOpCode::CMPMemImms(), child, generateX86MemoryReference(refReg, 0, cg()), 0, cg());
97
generateRegMemInstruction(TR::InstOpCode::CMOVERegMem(), child, refReg, generateX86MemoryReference(cg()->findOrCreateConstantDataSnippet<intptr_t>(child, 0), cg()), cg());
98
}
99
}
100
else
101
{
102
refReg = cg()->evaluate(child);
103
}
104
105
return refReg;
106
}
107
108
109
int32_t J9::X86::AMD64::JNILinkage::computeMemoryArgSize(
110
TR::Node *callNode,
111
int32_t first,
112
int32_t last,
113
bool passThread)
114
{
115
int32_t size= 0;
116
int32_t numFloatArgs = 0;
117
int32_t numIntArgs = 0;
118
119
int32_t slotSize = TR::Compiler->om.sizeofReferenceAddress();
120
int32_t slotSizeMinus1 = slotSize-1;
121
122
// For JNI dispatch, count the JNI env pointer first. It is
123
// assumed that for the 64-bit register passing linkages for JNI
124
// that the evaluation order is left-to-right and that the JNI env
125
// pointer will be in a register as the first argument.
126
//
127
if (passThread)
128
{
129
TR_ASSERT(!_systemLinkage->getProperties().passArgsRightToLeft(), "JNI argument evaluation expected to be left-to-right");
130
TR_ASSERT(_systemLinkage->getProperties().getNumIntegerArgumentRegisters() > 0, "JNI env pointer expected to be passed in a register.");
131
numIntArgs++;
132
133
if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())
134
numFloatArgs++;
135
}
136
137
bool allocateOnCallerFrame = false;
138
139
for (int32_t i = first; i != last; i++)
140
{
141
TR::Node *child = callNode->getChild(i);
142
TR::DataType type = child->getDataType();
143
144
switch (type)
145
{
146
case TR::Float:
147
case TR::Double:
148
if (numFloatArgs >= _systemLinkage->getProperties().getNumFloatArgumentRegisters())
149
allocateOnCallerFrame = true;
150
numFloatArgs++;
151
152
if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())
153
numIntArgs++;
154
155
break;
156
157
case TR::Address:
158
default:
159
if (numIntArgs >= _systemLinkage->getProperties().getNumIntegerArgumentRegisters())
160
allocateOnCallerFrame = true;
161
numIntArgs++;
162
163
if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())
164
numFloatArgs++;
165
166
break;
167
}
168
169
if (allocateOnCallerFrame)
170
{
171
int32_t roundedSize = (child->getSize()+slotSizeMinus1)&(~slotSizeMinus1);
172
size += roundedSize ? roundedSize : slotSize;
173
allocateOnCallerFrame = false;
174
}
175
}
176
177
// Always reserve space on the caller's frame for the maximum number of linkage registers.
178
//
179
if (_systemLinkage->getProperties().getCallerFrameAllocatesSpaceForLinkageRegisters())
180
{
181
// TODO: this is Win64 Fastcall-specific at the moment. It could be generalized if need be.
182
//
183
size += std::max(_systemLinkage->getProperties().getNumIntegerArgumentRegisters(),
184
_systemLinkage->getProperties().getNumFloatArgumentRegisters()) * TR::Compiler->om.sizeofReferenceAddress();
185
}
186
187
return size;
188
}
189
190
191
// Build arguments for system linkage dispatch.
192
//
193
int32_t J9::X86::AMD64::JNILinkage::buildArgs(
194
TR::Node *callNode,
195
TR::RegisterDependencyConditions *deps,
196
bool passThread,
197
bool passReceiver)
198
{
199
TR::SymbolReference *methodSymRef = callNode->getSymbolReference();
200
TR::MethodSymbol *methodSymbol = methodSymRef->getSymbol()->castToMethodSymbol();
201
TR::Machine *machine = cg()->machine();
202
TR::RealRegister::RegNum noReg = TR::RealRegister::NoReg;
203
TR::RealRegister *espReal = machine->getRealRegister(TR::RealRegister::esp);
204
int32_t firstNodeArgument = callNode->getFirstArgumentIndex();
205
int32_t lastNodeArgument = callNode->getNumChildren() - 1;
206
int32_t offset = 0;
207
uint16_t numIntArgs = 0,
208
numFloatArgs = 0;
209
int32_t first, last;
210
211
int32_t numCopiedRegs = 0;
212
TR::Register *copiedRegs[TR::X86LinkageProperties::MaxArgumentRegisters];
213
214
TR_ASSERT(!_systemLinkage->getProperties().passArgsRightToLeft(), "JNI dispatch expected to be left-to-right.");
215
216
if (!passReceiver)
217
first = firstNodeArgument + 1;
218
else first = firstNodeArgument;
219
last = lastNodeArgument + 1;
220
221
// We need to know how many arguments will be passed on the stack in order to add the right
222
// amount of slush first for proper alignment.
223
//
224
int32_t memoryArgSize = computeMemoryArgSize(callNode, first, last, passThread);
225
226
// For JNI callout there may be some extra information (such as the VMThread pointer)
227
// that has been preserved on the stack that must be accounted for in this calculation.
228
//
229
int32_t adjustedMemoryArgSize = memoryArgSize + _JNIDispatchInfo.argSize;
230
231
if (adjustedMemoryArgSize > 0)
232
{
233
// The C stack pointer (RSP) is 16-aligned before building any memory arguments (guaranteed by the VM).
234
// Following the last memory argument the stack alignment must still be 16.
235
//
236
if ((adjustedMemoryArgSize % 16) != 0)
237
memoryArgSize += 8;
238
239
TR::InstOpCode::Mnemonic op = (memoryArgSize >= -128 && memoryArgSize <= 127) ? TR::InstOpCode::SUBRegImms() : TR::InstOpCode::SUBRegImm4();
240
generateRegImmInstruction(op, callNode, espReal, memoryArgSize, cg());
241
}
242
243
// Evaluate the JNI env pointer first.
244
//
245
if (passThread)
246
{
247
TR::RealRegister::RegNum rregIndex = _systemLinkage->getProperties().getIntegerArgumentRegister(0);
248
TR::Register *argReg = cg()->allocateRegister();
249
generateRegRegInstruction(TR::Linkage::movOpcodes(RegReg, movType(TR::Address)), callNode, argReg, cg()->getMethodMetaDataRegister(), cg());
250
deps->addPreCondition(argReg, rregIndex, cg());
251
cg()->stopUsingRegister(argReg);
252
numIntArgs++;
253
254
if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())
255
numFloatArgs++;
256
257
if (_systemLinkage->getProperties().getCallerFrameAllocatesSpaceForLinkageRegisters())
258
{
259
int32_t slotSize = TR::Compiler->om.sizeofReferenceAddress();
260
int32_t slotSizeMinus1 = slotSize-1;
261
262
int32_t roundedSize = (slotSize+slotSizeMinus1)&(~slotSizeMinus1);
263
offset += roundedSize ? roundedSize : slotSize;
264
}
265
}
266
267
// if fastJNI do not pass the receiver just evaluate the first child
268
if (!passReceiver)
269
{
270
TR::Node *child = callNode->getChild(first-1);
271
cg()->evaluate(child);
272
cg()->decReferenceCount(child);
273
}
274
275
int32_t i;
276
for (i = first; i != last; i++)
277
{
278
TR::Node *child = callNode->getChild(i);
279
TR::DataType type = child->getDataType();
280
TR::RealRegister::RegNum rregIndex = noReg;
281
282
switch (type)
283
{
284
case TR::Float:
285
case TR::Double:
286
rregIndex =
287
(numFloatArgs < _systemLinkage->getProperties().getNumFloatArgumentRegisters()) ? _systemLinkage->getProperties().getFloatArgumentRegister(numFloatArgs) : noReg;
288
numFloatArgs++;
289
290
if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())
291
numIntArgs++;
292
293
break;
294
295
case TR::Address:
296
// Deliberate fall through for TR::Address types.
297
//
298
default:
299
rregIndex =
300
(numIntArgs < _systemLinkage->getProperties().getNumIntegerArgumentRegisters()) ? _systemLinkage->getProperties().getIntegerArgumentRegister(numIntArgs) : noReg;
301
numIntArgs++;
302
303
if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())
304
numFloatArgs++;
305
306
break;
307
}
308
309
TR::Register *vreg;
310
if (type == TR::Address)
311
vreg = processJNIReferenceArg(child);
312
else
313
vreg = cg()->evaluate(child);
314
315
bool needsStackOffsetUpdate = false;
316
if (rregIndex != noReg)
317
{
318
// For NULL JNI reference parameters, it is possible that the NULL value will be evaluated into
319
// a different register than the child. In that case it is not necessary to copy the temporary scratch
320
// register across the call.
321
//
322
if ((child->getReferenceCount() > 1) &&
323
(vreg == child->getRegister()))
324
{
325
TR::Register *argReg = cg()->allocateRegister();
326
if (vreg->containsCollectedReference())
327
argReg->setContainsCollectedReference();
328
generateRegRegInstruction(TR::Linkage::movOpcodes(RegReg, movType(child->getDataType())), child, argReg, vreg, cg());
329
vreg = argReg;
330
copiedRegs[numCopiedRegs++] = vreg;
331
}
332
333
deps->addPreCondition(vreg, rregIndex, cg());
334
335
if (_systemLinkage->getProperties().getCallerFrameAllocatesSpaceForLinkageRegisters())
336
needsStackOffsetUpdate = true;
337
}
338
else
339
{
340
// Ideally, we would like to push rather than move
341
generateMemRegInstruction(TR::Linkage::movOpcodes(MemReg, fullRegisterMovType(vreg)),
342
child,
343
generateX86MemoryReference(espReal, offset, cg()),
344
vreg,
345
cg());
346
347
needsStackOffsetUpdate = true;
348
}
349
350
if (needsStackOffsetUpdate)
351
{
352
int32_t slotSize = TR::Compiler->om.sizeofReferenceAddress();
353
int32_t slotSizeMinus1 = slotSize-1;
354
355
int32_t roundedSize = (child->getSize()+slotSizeMinus1)&(~slotSizeMinus1);
356
offset += roundedSize ? roundedSize : slotSize;
357
}
358
359
cg()->decReferenceCount(child);
360
}
361
362
// Now that we're finished making the preconditions, all the interferences
363
// are established and we can kill these regs.
364
//
365
for (i = 0; i < numCopiedRegs; i++)
366
cg()->stopUsingRegister(copiedRegs[i]);
367
368
deps->stopAddingPreConditions();
369
370
return memoryArgSize;
371
}
372
373
374
TR::Register *
375
J9::X86::AMD64::JNILinkage::buildVolatileAndReturnDependencies(
376
TR::Node *callNode,
377
TR::RegisterDependencyConditions *deps,
378
bool omitDedicatedFrameRegister)
379
{
380
TR_ASSERT(deps != NULL, "expected register dependencies");
381
382
// Figure out which is the return register.
383
//
384
TR::RealRegister::RegNum returnRegIndex;
385
TR_RegisterKinds returnKind;
386
387
switch (callNode->getDataType())
388
{
389
default:
390
TR_ASSERT(0, "Unrecognized call node data type: #%d", (int)callNode->getDataType());
391
// deliberate fall-through
392
case TR::NoType:
393
returnRegIndex = TR::RealRegister::NoReg;
394
returnKind = TR_NoRegister;
395
break;
396
case TR::Int8:
397
case TR::Int16:
398
case TR::Int32:
399
case TR::Int64:
400
case TR::Address:
401
returnRegIndex = _systemLinkage->getProperties().getIntegerReturnRegister();
402
returnKind = TR_GPR;
403
break;
404
case TR::Float:
405
case TR::Double:
406
returnRegIndex = _systemLinkage->getProperties().getFloatReturnRegister();
407
returnKind = TR_FPR;
408
break;
409
}
410
411
// Kill all non-preserved int and float regs besides the return register.
412
//
413
int32_t i;
414
TR::RealRegister::RegNum scratchIndex = _systemLinkage->getProperties().getIntegerScratchRegister(1);
415
for (i=0; i<_systemLinkage->getProperties().getNumVolatileRegisters(); i++)
416
{
417
TR::RealRegister::RegNum regIndex = _systemLinkage->getProperties()._volatileRegisters[i];
418
419
if (regIndex != returnRegIndex)
420
{
421
if (!omitDedicatedFrameRegister || (regIndex != _JNIDispatchInfo.dedicatedFrameRegisterIndex))
422
{
423
TR_RegisterKinds rk = (i < _systemLinkage->getProperties()._numberOfVolatileGPRegisters) ? TR_GPR : TR_FPR;
424
TR::Register *dummy = cg()->allocateRegister(rk);
425
deps->addPostCondition(dummy, regIndex, cg());
426
427
// Note that we don't setPlaceholderReg here. If this volatile reg is also volatile
428
// in the caller's linkage, then that flag doesn't matter much anyway. If it's preserved
429
// in the caller's linkage, then we don't want to set that flag because we want this
430
// use of the register to count as a "real" use, thereby motivating the prologue to
431
// preserve the regsiter.
432
433
// A scratch register is necessary to call the native without a trampoline.
434
//
435
if (regIndex != scratchIndex)
436
cg()->stopUsingRegister(dummy);
437
}
438
}
439
}
440
441
// Add a VMThread register dependence.
442
//
443
deps->addPostCondition(cg()->getVMThreadRegister(), TR::RealRegister::ebp, cg());
444
445
// Now that everything is dead, we can allocate the return register without
446
// interference
447
//
448
TR::Register *returnRegister;
449
if (returnRegIndex)
450
{
451
TR_ASSERT(returnKind != TR_NoRegister, "assertion failure");
452
453
if (callNode->getDataType() == TR::Address)
454
returnRegister = cg()->allocateCollectedReferenceRegister();
455
else
456
{
457
returnRegister = cg()->allocateRegister(returnKind);
458
if (callNode->getDataType() == TR::Float)
459
returnRegister->setIsSinglePrecision();
460
}
461
462
deps->addPostCondition(returnRegister, returnRegIndex, cg());
463
}
464
else
465
returnRegister = NULL;
466
467
deps->stopAddingPostConditions();
468
469
return returnRegister;
470
}
471
472
473
void J9::X86::AMD64::JNILinkage::buildJNICallOutFrame(
474
TR::Node *callNode,
475
TR::LabelSymbol *returnAddrLabel)
476
{
477
TR::ResolvedMethodSymbol *callSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
478
TR_ResolvedMethod *resolvedMethod = callSymbol->getResolvedMethod();
479
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
480
TR::Register *scratchReg = NULL;
481
TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);
482
483
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
484
485
// Mask out the magic bit that indicates JIT frames below.
486
//
487
generateMemImmInstruction(
488
TR::InstOpCode::SMemImm4(),
489
callNode,
490
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaFrameFlagsOffset(), cg()),
491
0,
492
cg());
493
494
// Grab 5 slots in the frame.
495
//
496
// 4: tag bits (savedA0)
497
// 3: empty (savedPC)
498
// 2: return address in this frame (savedCP)
499
// 1: frame flags
500
// 0: RAM method
501
//
502
503
// Build stack frame in Java stack. Tag current bp.
504
//
505
uintptr_t tagBits = 0;
506
507
// If the current method is simply a wrapper for the JNI call, hide the call-out stack frame.
508
//
509
static_assert(IS_32BIT_SIGNED(J9SF_A0_INVISIBLE_TAG), "J9SF_A0_INVISIBLE_TAG must fit in immediate");
510
if (resolvedMethod == comp()->getCurrentMethod())
511
tagBits |= J9SF_A0_INVISIBLE_TAG;
512
513
// Push tag bits (savedA0 slot).
514
//
515
generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, tagBits, cg());
516
517
// (skip savedPC slot).
518
//
519
generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, 0, cg());
520
521
// Push return address in this frame (savedCP slot).
522
//
523
if (!scratchReg)
524
scratchReg = cg()->allocateRegister();
525
526
TR::AMD64RegImm64SymInstruction* returnAddressInstr =
527
generateRegImm64SymInstruction(
528
TR::InstOpCode::MOV8RegImm64,
529
callNode,
530
scratchReg,
531
0,
532
new (trHeapMemory()) TR::SymbolReference(comp()->getSymRefTab(), returnAddrLabel),
533
cg());
534
535
returnAddressInstr->setReloKind(TR_AbsoluteMethodAddress);
536
537
generateRegInstruction(TR::InstOpCode::PUSHReg, callNode, scratchReg, cg());
538
539
// Push frame flags.
540
//
541
static_assert(IS_32BIT_SIGNED(J9_SSF_JIT_JNI_CALLOUT), "J9_SSF_JIT_JNI_CALLOUT must fit in immediate");
542
generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, J9_SSF_JIT_JNI_CALLOUT, cg());
543
544
// Push the RAM method for the native.
545
//
546
auto tempMR = generateX86MemoryReference(espReal, 0, cg());
547
uintptr_t methodAddr = (uintptr_t) resolvedMethod->resolvedMethodAddress();
548
if (IS_32BIT_SIGNED(methodAddr) && !TR::Compiler->om.nativeAddressesCanChangeSize())
549
{
550
generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, methodAddr, cg());
551
}
552
else
553
{
554
if (!scratchReg)
555
scratchReg = cg()->allocateRegister();
556
557
static const TR_ExternalRelocationTargetKind reloTypes[] = { TR_VirtualRamMethodConst, TR_NoRelocation /*Interfaces*/, TR_StaticRamMethodConst, TR_SpecialRamMethodConst };
558
int reloType = callSymbol->getMethodKind() - 1; //method kinds are 1-based!!
559
TR_ASSERT(reloTypes[reloType] != TR_NoRelocation, "There shouldn't be direct JNI interface calls!");
560
generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg, methodAddr, cg(), reloTypes[reloType]);
561
generateRegInstruction(TR::InstOpCode::PUSHReg, callNode, scratchReg, cg());
562
}
563
564
// Store out pc and literals values indicating the callout frame.
565
//
566
static_assert(IS_32BIT_SIGNED(J9SF_FRAME_TYPE_JIT_JNI_CALLOUT), "J9SF_FRAME_TYPE_JIT_JNI_CALLOUT must fit in immediate");
567
generateMemImmInstruction(TR::InstOpCode::SMemImm4(), callNode, generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaPCOffset(), cg()), J9SF_FRAME_TYPE_JIT_JNI_CALLOUT, cg());
568
569
if (scratchReg)
570
cg()->stopUsingRegister(scratchReg);
571
572
generateMemImmInstruction(TR::InstOpCode::SMemImm4(),
573
callNode,
574
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaLiteralsOffset(), cg()),
575
0,
576
cg());
577
}
578
579
void
580
J9::X86::AMD64::JNILinkage::buildJNIMergeLabelDependencies(TR::Node *callNode, bool killNonVolatileGPRs)
581
{
582
TR::RegisterDependencyConditions *deps = _JNIDispatchInfo.mergeLabelPostDeps;
583
584
// Allocate the actual register that the JNI result will be returned in. This may be different
585
// than the linkage return register on the call because of register usage constraints after the
586
// call (e.g., re-acquiring the VM).
587
//
588
TR::RealRegister::RegNum returnRegIndex;
589
TR::Register *linkageReturnRegister = _JNIDispatchInfo.linkageReturnRegister;
590
TR::Register *JNIReturnRegister;
591
592
if (linkageReturnRegister)
593
{
594
JNIReturnRegister = cg()->allocateRegister(linkageReturnRegister->getKind());
595
if (linkageReturnRegister->containsCollectedReference())
596
JNIReturnRegister->setContainsCollectedReference();
597
else if (linkageReturnRegister->isSinglePrecision())
598
JNIReturnRegister->setIsSinglePrecision();
599
600
if (JNIReturnRegister->getKind() == TR_GPR)
601
returnRegIndex = TR::RealRegister::ecx;
602
else
603
returnRegIndex = _systemLinkage->getProperties().getFloatReturnRegister();
604
605
deps->addPostCondition(JNIReturnRegister, returnRegIndex, cg());
606
}
607
else
608
{
609
JNIReturnRegister = NULL;
610
returnRegIndex = TR::RealRegister::NoReg;
611
}
612
613
_JNIDispatchInfo.JNIReturnRegister = JNIReturnRegister;
614
615
// Kill all registers across the JNI callout sequence.
616
//
617
int32_t i;
618
for (i=0; i<_systemLinkage->getProperties().getNumVolatileRegisters(); i++)
619
{
620
TR::RealRegister::RegNum regIndex = _systemLinkage->getProperties()._volatileRegisters[i];
621
622
if (regIndex != returnRegIndex)
623
{
624
TR_RegisterKinds rk = (i < _systemLinkage->getProperties()._numberOfVolatileGPRegisters) ? TR_GPR : TR_FPR;
625
TR::Register *dummy = cg()->allocateRegister(rk);
626
627
// Don't setPlaceholderReg here in case the caller's linkage lists this one as a preserved reg
628
//
629
deps->addPostCondition(dummy, regIndex, cg());
630
cg()->stopUsingRegister(dummy);
631
}
632
}
633
634
if (killNonVolatileGPRs)
635
{
636
for (i=0; i<_systemLinkage->getProperties().getNumPreservedRegisters(); i++)
637
{
638
TR::RealRegister::RegNum regIndex = _systemLinkage->getProperties()._preservedRegisters[i];
639
640
if (regIndex != returnRegIndex)
641
{
642
TR_RegisterKinds rk = (i < _systemLinkage->getProperties()._numberOfPreservedGPRegisters) ? TR_GPR : TR_FPR;
643
TR::Register *dummy = cg()->allocateRegister(rk);
644
645
// Don't setPlaceholderReg here. We release VM access around the
646
// native call, so even though the callee's linkage won't alter a
647
// given register, we still have a problem if a stack walk needs to
648
// inspect/modify that register because once we're in C-land, we have
649
// no idea where that regsiter's value is located. Therefore, we need
650
// to spill even the callee-saved registers around the call.
651
//
652
// In principle, we only need to do this for registers that contain
653
// references. However, at this location in the code, we don't yet
654
// know which real registers those would be. Tragically, this causes
655
// us to save/restore ALL preserved registers in any method containing
656
// a JNI call.
657
658
deps->addPostCondition(dummy, regIndex, cg());
659
cg()->stopUsingRegister(dummy);
660
}
661
}
662
}
663
664
// Add a VMThread register dependence.
665
//
666
deps->addPostCondition(cg()->getVMThreadRegister(), TR::RealRegister::ebp, cg());
667
deps->stopAddingPostConditions();
668
}
669
670
void J9::X86::AMD64::JNILinkage::buildOutgoingJNIArgsAndDependencies(
671
TR::Node *callNode,
672
bool passThread,
673
bool passReceiver,
674
bool killNonVolatileGPRs)
675
{
676
// Allocate adequate register dependencies. The pre and post conditions are distinguished because they will
677
// appear on different instructions.
678
//
679
// callPre = number of argument registers
680
// callPost = number of volatile + VMThread + return register
681
// labelPost = number of volatile + preserved + VMThread + return register
682
//
683
uint32_t callPre = _systemLinkage->getProperties().getNumIntegerArgumentRegisters() + _systemLinkage->getProperties().getNumFloatArgumentRegisters();
684
uint32_t callPost = _systemLinkage->getProperties().getNumVolatileRegisters() + 1 + (callNode->getDataType() == TR::NoType ? 0 : 1);
685
686
// This is overly pessimistic. In reality, only the linkage preserved registers that contain collected references
687
// need to be evicted across the JNI call. There currently isn't a convenient mechanism to specify this...
688
//
689
uint32_t labelPost = _systemLinkage->getProperties().getNumVolatileRegisters() +
690
_systemLinkage->getProperties().getNumPreservedRegisters() + 1 +
691
(callNode->getDataType() == TR::NoType ? 0 : 1);
692
693
_JNIDispatchInfo.callPostDeps = generateRegisterDependencyConditions(callPre, callPost, cg());
694
_JNIDispatchInfo.mergeLabelPostDeps = generateRegisterDependencyConditions(0, labelPost, cg());
695
696
// Evaluate outgoing arguments on the system stack and build pre-conditions.
697
//
698
_JNIDispatchInfo.argSize += buildArgs(callNode, _JNIDispatchInfo.callPostDeps, passThread, passReceiver);
699
700
// Build post-conditions.
701
//
702
// Omit the dedicated frame register because it will be in Locked state by the time
703
// the register assigner reaches the call instruction, and the assignment won't happen.
704
//
705
_JNIDispatchInfo.linkageReturnRegister = buildVolatileAndReturnDependencies(callNode, _JNIDispatchInfo.callPostDeps, true);
706
707
for (int32_t i=0; i<callPost; i++)
708
{
709
TR::RegisterDependency *dep = _JNIDispatchInfo.callPostDeps->getPostConditions()->getRegisterDependency(i);
710
if (dep->getRealRegister() == _systemLinkage->getProperties().getIntegerScratchRegister(1))
711
{
712
_JNIDispatchInfo.dispatchTrampolineRegister = dep->getRegister();
713
break;
714
}
715
}
716
717
buildJNIMergeLabelDependencies(callNode, killNonVolatileGPRs);
718
}
719
720
TR::Instruction *
721
J9::X86::AMD64::JNILinkage::generateMethodDispatch(
722
TR::Node *callNode,
723
bool isJNIGCPoint,
724
uintptr_t targetAddress)
725
{
726
TR::ResolvedMethodSymbol *callSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
727
TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);
728
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
729
intptr_t argSize = _JNIDispatchInfo.argSize;
730
TR::SymbolReference *methodSymRef= callNode->getSymbolReference();
731
TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp()->fe());
732
733
if (methodSymRef->getReferenceNumber()>=TR_AMD64numRuntimeHelpers)
734
{
735
fej9->reserveTrampolineIfNecessary(comp(), methodSymRef, false);
736
}
737
738
// Load machine bp esp + offsetof(J9CInterpreterStackFrame, machineBP) + argSize
739
//
740
generateRegMemInstruction(TR::InstOpCode::LRegMem(),
741
callNode,
742
vmThreadReg,
743
generateX86MemoryReference(espReal, offsetof(J9CInterpreterStackFrame, machineBP) + argSize, cg()),
744
cg());
745
746
// Dispatch JNI method directly.
747
//
748
TR_ASSERT(_JNIDispatchInfo.dispatchTrampolineRegister, "No trampoline scratch register available for directJNI dispatch.");
749
750
// TODO: Need an AOT relocation here.
751
//
752
// The code below is disabled because of a problem with direct call to JNI methods
753
// through trampoline. The problem is that at the time of generation of the trampoline
754
// we don't know if we did directToJNI call or we are calling the native thunk. In case
755
// we are calling the thunk the code below won't work and it will give the caller address
756
// of the C native and we crash.
757
//
758
759
760
static const TR_ExternalRelocationTargetKind reloTypes[] = {TR_JNIVirtualTargetAddress, TR_NoRelocation /*Interfaces*/, TR_JNIStaticTargetAddress, TR_JNISpecialTargetAddress};
761
int reloType = callSymbol->getMethodKind()-1; //method kinds are 1-based!!
762
763
TR_ASSERT(reloTypes[reloType] != TR_NoRelocation, "There shouldn't be direct JNI interface calls!");
764
765
TR::X86RegInstruction *patchedInstr=
766
generateRegImm64Instruction(
767
TR::InstOpCode::MOV8RegImm64,
768
callNode,
769
_JNIDispatchInfo.dispatchTrampolineRegister,
770
targetAddress,
771
cg(), reloTypes[reloType]);
772
773
TR::X86RegInstruction *instr = generateRegInstruction(
774
TR::InstOpCode::CALLReg,
775
callNode,
776
_JNIDispatchInfo.dispatchTrampolineRegister,
777
_JNIDispatchInfo.callPostDeps,
778
cg());
779
cg()->getJNICallSites().push_front(new (trHeapMemory()) TR_Pair<TR_ResolvedMethod, TR::Instruction>(callSymbol->getResolvedMethod(), patchedInstr));
780
781
if (isJNIGCPoint)
782
instr->setNeedsGCMap(_systemLinkage->getProperties().getPreservedRegisterMapForGC());
783
784
if (_JNIDispatchInfo.dispatchTrampolineRegister)
785
cg()->stopUsingRegister(_JNIDispatchInfo.dispatchTrampolineRegister);
786
787
// Clean up argument area if not callee cleanup linkage.
788
//
789
// The total C stack argument size includes the push of the VMThread register plus
790
// the memory arguments for the call. Only clean up the memory argument portion.
791
//
792
if (!cg()->getJNILinkageCalleeCleanup())
793
{
794
intptr_t cleanUpSize = argSize - TR::Compiler->om.sizeofReferenceAddress();
795
796
if (comp()->target().is64Bit())
797
TR_ASSERT(cleanUpSize <= 0x7fffffff, "Caller cleanup argument size too large for one instruction on AMD64.");
798
799
800
if (cleanUpSize != 0)
801
{
802
TR::InstOpCode::Mnemonic op = (cleanUpSize >= -128 && cleanUpSize <= 127) ? TR::InstOpCode::ADDRegImms() : TR::InstOpCode::ADDRegImm4();
803
generateRegImmInstruction(op, callNode, espReal, cleanUpSize, cg());
804
}
805
}
806
807
return instr;
808
}
809
810
811
void J9::X86::AMD64::JNILinkage::releaseVMAccess(TR::Node *callNode)
812
{
813
// Release VM access (spin lock).
814
//
815
// mov scratch1, [rbp+publicFlags]
816
// loopHead:
817
// mov scratch2, scratch1
818
// test scratch1, constReleaseVMAccessOutOfLineMask
819
// jne longReleaseSnippet
820
// and scratch2, constReleaseVMAccessMask
821
// [l]cmpxchg [rbp+publicFlags], scratch2
822
// jne pauseSnippet OR loopHead
823
// longReleaseRestart:
824
// scratch1 <-> RAX
825
// scratch2 <-> NoReg
826
// scratch3 <-> NoReg
827
//
828
TR::InstOpCode::Mnemonic op;
829
830
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
831
TR::Register *scratchReg1 = cg()->allocateRegister();
832
TR::Register *scratchReg2 = cg()->allocateRegister();
833
TR::Register *scratchReg3 = NULL;
834
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
835
836
generateRegMemInstruction(TR::InstOpCode::LRegMem(),
837
callNode,
838
scratchReg1,
839
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),
840
cg());
841
842
TR::LabelSymbol *loopHeadLabel = generateLabelSymbol(cg());
843
844
// Loop head
845
//
846
generateLabelInstruction(TR::InstOpCode::label, callNode, loopHeadLabel, cg());
847
generateRegRegInstruction(TR::InstOpCode::MOVRegReg(), callNode, scratchReg2, scratchReg1, cg());
848
849
TR::LabelSymbol *longReleaseSnippetLabel = generateLabelSymbol(cg());
850
TR::LabelSymbol *longReleaseRestartLabel = generateLabelSymbol(cg());
851
852
uintptr_t mask = fej9->constReleaseVMAccessOutOfLineMask();
853
854
if (comp()->target().is64Bit() && (mask > 0x7fffffff))
855
{
856
if (!scratchReg3)
857
scratchReg3 = cg()->allocateRegister();
858
859
generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg3, mask, cg());
860
generateRegRegInstruction(TR::InstOpCode::TEST8RegReg, callNode, scratchReg1, scratchReg3, cg());
861
}
862
else
863
{
864
op = (mask <= 255) ? TR::InstOpCode::TEST1RegImm1 : TR::InstOpCode::TEST4RegImm4;
865
generateRegImmInstruction(op, callNode, scratchReg1, mask, cg());
866
}
867
generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longReleaseSnippetLabel, cg());
868
869
{
870
TR_OutlinedInstructionsGenerator og(longReleaseSnippetLabel, callNode, cg());
871
auto helper = comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(comp()->getMethodSymbol());
872
generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());
873
generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longReleaseRestartLabel, cg());
874
og.endOutlinedInstructionSequence();
875
}
876
877
mask = fej9->constReleaseVMAccessMask();
878
879
if (comp()->target().is64Bit() && (mask > 0x7fffffff))
880
{
881
if (!scratchReg3)
882
scratchReg3 = cg()->allocateRegister();
883
884
generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg3, mask, cg());
885
generateRegRegInstruction(TR::InstOpCode::AND8RegReg, callNode, scratchReg2, scratchReg3, cg());
886
}
887
else
888
{
889
op = (mask <= 255) ? TR::InstOpCode::AND1RegImm1 : TR::InstOpCode::AND4RegImm4;
890
generateRegImmInstruction(op, callNode, scratchReg2, mask, cg());
891
}
892
893
op = comp()->target().isSMP() ? TR::InstOpCode::LCMPXCHGMemReg() : TR::InstOpCode::CMPXCHGMemReg(cg());
894
generateMemRegInstruction(
895
op,
896
callNode,
897
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),
898
scratchReg2,
899
cg());
900
901
generateLabelInstruction(TR::InstOpCode::JNE4, callNode, loopHeadLabel, cg());
902
903
int8_t numDeps = scratchReg3 ? 3 : 2;
904
TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions(numDeps, numDeps, cg());
905
deps->addPreCondition(scratchReg1, TR::RealRegister::eax, cg());
906
deps->addPostCondition(scratchReg1, TR::RealRegister::eax, cg());
907
cg()->stopUsingRegister(scratchReg1);
908
909
deps->addPreCondition(scratchReg2, TR::RealRegister::NoReg, cg());
910
deps->addPostCondition(scratchReg2, TR::RealRegister::NoReg, cg());
911
cg()->stopUsingRegister(scratchReg2);
912
913
if (scratchReg3)
914
{
915
deps->addPreCondition(scratchReg3, TR::RealRegister::NoReg, cg());
916
deps->addPostCondition(scratchReg3, TR::RealRegister::NoReg, cg());
917
cg()->stopUsingRegister(scratchReg3);
918
}
919
920
deps->stopAddingConditions();
921
922
generateLabelInstruction(TR::InstOpCode::label, callNode, longReleaseRestartLabel, deps, cg());
923
}
924
925
926
void J9::X86::AMD64::JNILinkage::acquireVMAccess(TR::Node *callNode)
927
{
928
// Re-acquire VM access.
929
//
930
// xor scratch1, scratch1
931
// mov rdi, constAcquireVMAccessOutOfLineMask
932
// [l]cmpxchg [rbp + publicFlags], rdi
933
// jne longReacquireSnippetLabel
934
// longReacquireRestartLabel:
935
//
936
937
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
938
TR::Register *scratchReg1 = cg()->allocateRegister();
939
TR::Register *scratchReg2 = cg()->allocateRegister();
940
941
generateRegRegInstruction(TR::InstOpCode::XORRegReg(), callNode, scratchReg1, scratchReg1, cg());
942
943
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
944
uintptr_t mask = fej9->constAcquireVMAccessOutOfLineMask();
945
946
if (comp()->target().is64Bit() && (mask > 0x7fffffff))
947
generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg2, mask, cg());
948
else
949
generateRegImmInstruction(TR::InstOpCode::MOV4RegImm4, callNode, scratchReg2, mask, cg());
950
951
TR::LabelSymbol *longReacquireSnippetLabel = generateLabelSymbol(cg());
952
TR::LabelSymbol *longReacquireRestartLabel = generateLabelSymbol(cg());
953
954
TR::InstOpCode::Mnemonic op = comp()->target().isSMP() ? TR::InstOpCode::LCMPXCHGMemReg() : TR::InstOpCode::CMPXCHGMemReg(cg());
955
generateMemRegInstruction(
956
op,
957
callNode,
958
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),
959
scratchReg2,
960
cg());
961
generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longReacquireSnippetLabel, cg());
962
963
// TODO: ecx may hold a reference across this snippet
964
// If the return type is address something needs to be represented in the
965
// register map for the snippet.
966
//
967
{
968
TR_OutlinedInstructionsGenerator og(longReacquireSnippetLabel, callNode, cg());
969
auto helper = comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp()->getMethodSymbol());
970
generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());
971
generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longReacquireRestartLabel, cg());
972
og.endOutlinedInstructionSequence();
973
}
974
TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions(2, 2, cg());
975
deps->addPreCondition(scratchReg1, TR::RealRegister::eax, cg());
976
deps->addPostCondition(scratchReg1, TR::RealRegister::eax, cg());
977
cg()->stopUsingRegister(scratchReg1);
978
979
deps->addPreCondition(scratchReg2, TR::RealRegister::NoReg, cg());
980
deps->addPostCondition(scratchReg2, TR::RealRegister::NoReg, cg());
981
cg()->stopUsingRegister(scratchReg2);
982
983
deps->stopAddingConditions();
984
985
generateLabelInstruction(TR::InstOpCode::label, callNode, longReacquireRestartLabel, deps, cg());
986
}
987
988
989
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
990
void J9::X86::AMD64::JNILinkage::releaseVMAccessAtomicFree(TR::Node *callNode)
991
{
992
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
993
994
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
995
996
generateMemImmInstruction(TR::InstOpCode::S8MemImm4,
997
callNode,
998
generateX86MemoryReference(vmThreadReg, offsetof(struct J9VMThread, inNative), cg()),
999
1,
1000
cg());
1001
1002
#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH)
1003
TR::MemoryReference *mr = generateX86MemoryReference(cg()->machine()->getRealRegister(TR::RealRegister::esp), intptr_t(0), cg());
1004
mr->setRequiresLockPrefix();
1005
generateMemImmInstruction(TR::InstOpCode::OR4MemImms, callNode, mr, 0, cg());
1006
#endif /* !J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */
1007
1008
TR::LabelSymbol *longReleaseSnippetLabel = generateLabelSymbol(cg());
1009
TR::LabelSymbol *longReleaseRestartLabel = generateLabelSymbol(cg());
1010
1011
static_assert(IS_32BIT_SIGNED(J9_PUBLIC_FLAGS_VM_ACCESS), "J9_PUBLIC_FLAGS_VM_ACCESS must fit in immediate");
1012
generateMemImmInstruction(J9_PUBLIC_FLAGS_VM_ACCESS < 128 ? TR::InstOpCode::CMP4MemImms : TR::InstOpCode::CMP4MemImm4,
1013
callNode,
1014
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),
1015
J9_PUBLIC_FLAGS_VM_ACCESS,
1016
cg());
1017
generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longReleaseSnippetLabel, cg());
1018
generateLabelInstruction(TR::InstOpCode::label, callNode, longReleaseRestartLabel, cg());
1019
1020
TR_OutlinedInstructionsGenerator og(longReleaseSnippetLabel, callNode, cg());
1021
auto helper = comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(comp()->getMethodSymbol());
1022
generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());
1023
generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longReleaseRestartLabel, cg());
1024
og.endOutlinedInstructionSequence();
1025
}
1026
1027
1028
void J9::X86::AMD64::JNILinkage::acquireVMAccessAtomicFree(TR::Node *callNode)
1029
{
1030
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
1031
1032
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
1033
1034
generateMemImmInstruction(TR::InstOpCode::S8MemImm4,
1035
callNode,
1036
generateX86MemoryReference(vmThreadReg, offsetof(struct J9VMThread, inNative), cg()),
1037
0,
1038
cg());
1039
1040
#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH)
1041
TR::MemoryReference *mr = generateX86MemoryReference(cg()->machine()->getRealRegister(TR::RealRegister::esp), intptr_t(0), cg());
1042
mr->setRequiresLockPrefix();
1043
generateMemImmInstruction(TR::InstOpCode::OR4MemImms, callNode, mr, 0, cg());
1044
#endif /* !J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */
1045
1046
TR::LabelSymbol *longAcquireSnippetLabel = generateLabelSymbol(cg());
1047
TR::LabelSymbol *longAcquireRestartLabel = generateLabelSymbol(cg());
1048
1049
static_assert(IS_32BIT_SIGNED(J9_PUBLIC_FLAGS_VM_ACCESS), "J9_PUBLIC_FLAGS_VM_ACCESS must fit in immediate");
1050
generateMemImmInstruction(J9_PUBLIC_FLAGS_VM_ACCESS < 128 ? TR::InstOpCode::CMP4MemImms : TR::InstOpCode::CMP4MemImm4,
1051
callNode,
1052
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),
1053
J9_PUBLIC_FLAGS_VM_ACCESS,
1054
cg());
1055
generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longAcquireSnippetLabel, cg());
1056
generateLabelInstruction(TR::InstOpCode::label, callNode, longAcquireRestartLabel, cg());
1057
1058
TR_OutlinedInstructionsGenerator og(longAcquireSnippetLabel, callNode, cg());
1059
auto helper = comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp()->getMethodSymbol());
1060
generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());
1061
generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longAcquireRestartLabel, cg());
1062
og.endOutlinedInstructionSequence();
1063
}
1064
1065
1066
#endif /* J9VM_INTERP_ATOMIC_FREE_JNI */
1067
1068
void J9::X86::AMD64::JNILinkage::cleanupReturnValue(
1069
TR::Node *callNode,
1070
TR::Register *linkageReturnReg,
1071
TR::Register *targetReg)
1072
{
1073
if (!callNode->getOpCode().isFloatingPoint())
1074
{
1075
// Native and JNI methods may not return a full register in some cases so we need to get the declared
1076
// type so that we sign and zero extend the narrower integer return types properly.
1077
//
1078
TR::InstOpCode::Mnemonic op;
1079
TR::SymbolReference *callSymRef = callNode->getSymbolReference();
1080
TR::ResolvedMethodSymbol *callSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
1081
TR_ResolvedMethod *resolvedMethod = callSymbol->getResolvedMethod();
1082
1083
bool isUnsigned = resolvedMethod->returnTypeIsUnsigned();
1084
1085
switch (resolvedMethod->returnType())
1086
{
1087
case TR::Int8:
1088
if (comp()->getSymRefTab()->isReturnTypeBool(callSymRef))
1089
{
1090
// For bool return type, must check whether value returned by
1091
// JNI is zero (false) or non-zero (true) to yield Java result
1092
generateRegRegInstruction(TR::InstOpCode::TEST1RegReg, callNode,
1093
linkageReturnReg, linkageReturnReg, cg());
1094
generateRegInstruction(TR::InstOpCode::SETNE1Reg, callNode, linkageReturnReg, cg());
1095
op = comp()->target().is64Bit() ? TR::InstOpCode::MOVZXReg8Reg1 : TR::InstOpCode::MOVZXReg4Reg1;
1096
}
1097
else if (isUnsigned)
1098
{
1099
op = comp()->target().is64Bit() ? TR::InstOpCode::MOVZXReg8Reg1 : TR::InstOpCode::MOVZXReg4Reg1;
1100
}
1101
else
1102
{
1103
op = comp()->target().is64Bit() ? TR::InstOpCode::MOVSXReg8Reg1 : TR::InstOpCode::MOVSXReg4Reg1;
1104
}
1105
break;
1106
case TR::Int16:
1107
if (isUnsigned)
1108
{
1109
op = comp()->target().is64Bit() ? TR::InstOpCode::MOVZXReg8Reg2 : TR::InstOpCode::MOVZXReg4Reg2;
1110
}
1111
else
1112
{
1113
op = comp()->target().is64Bit() ? TR::InstOpCode::MOVSXReg8Reg2 : TR::InstOpCode::MOVSXReg4Reg2;
1114
}
1115
break;
1116
default:
1117
// TR::Address, TR_[US]Int64, TR_[US]Int32
1118
//
1119
op = (linkageReturnReg != targetReg) ? TR::InstOpCode::MOVRegReg() : TR::InstOpCode::bad;
1120
break;
1121
}
1122
1123
if (op != TR::InstOpCode::bad)
1124
generateRegRegInstruction(op, callNode, targetReg, linkageReturnReg, cg());
1125
}
1126
}
1127
1128
1129
void J9::X86::AMD64::JNILinkage::checkForJNIExceptions(TR::Node *callNode)
1130
{
1131
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
1132
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
1133
1134
// Check exceptions.
1135
//
1136
generateMemImmInstruction(TR::InstOpCode::CMPMemImms(), callNode, generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetCurrentExceptionOffset(), cg()), 0, cg());
1137
1138
TR::LabelSymbol *snippetLabel = generateLabelSymbol(cg());
1139
TR::Instruction *instr = generateLabelInstruction(TR::InstOpCode::JNE4, callNode, snippetLabel, cg());
1140
1141
uint32_t gcMap = _systemLinkage->getProperties().getPreservedRegisterMapForGC();
1142
if (comp()->target().is32Bit())
1143
{
1144
gcMap |= (_JNIDispatchInfo.argSize<<14);
1145
}
1146
instr->setNeedsGCMap(gcMap);
1147
1148
TR::Snippet *snippet =
1149
new (trHeapMemory()) TR::X86CheckFailureSnippet(cg(),
1150
cg()->symRefTab()->findOrCreateRuntimeHelper(TR_throwCurrentException),
1151
snippetLabel,
1152
instr,
1153
_JNIDispatchInfo.requiresFPstackPop);
1154
cg()->addSnippet(snippet);
1155
}
1156
1157
1158
void J9::X86::AMD64::JNILinkage::cleanupJNIRefPool(TR::Node *callNode)
1159
{
1160
// Must check to see if the ref pool was used and clean them up if so--or we
1161
// leave a bunch of pinned garbage behind that screws up the gc quality forever.
1162
//
1163
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
1164
static_assert(IS_32BIT_SIGNED(J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS), "J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS must fit in immediate");
1165
1166
TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);
1167
1168
TR::LabelSymbol *refPoolSnippetLabel = generateLabelSymbol(cg());
1169
TR::LabelSymbol *refPoolRestartLabel = generateLabelSymbol(cg());
1170
1171
generateMemImmInstruction(J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS <= 255 ? TR::InstOpCode::TEST1MemImm1 : TR::InstOpCode::TESTMemImm4(),
1172
callNode,
1173
generateX86MemoryReference(espReal, fej9->constJNICallOutFrameFlagsOffset(), cg()),
1174
J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS,
1175
cg());
1176
1177
generateLabelInstruction(TR::InstOpCode::JNE4, callNode, refPoolSnippetLabel, cg());
1178
generateLabelInstruction(TR::InstOpCode::label, callNode, refPoolRestartLabel, cg());
1179
1180
TR_OutlinedInstructionsGenerator og(refPoolSnippetLabel, callNode, cg());
1181
generateHelperCallInstruction(callNode, TR_AMD64jitCollapseJNIReferenceFrame, NULL, cg());
1182
generateLabelInstruction(TR::InstOpCode::JMP4, callNode, refPoolRestartLabel, cg());
1183
og.endOutlinedInstructionSequence();
1184
}
1185
1186
1187
TR::Register *J9::X86::AMD64::JNILinkage::buildDirectDispatch(
1188
TR::Node *callNode,
1189
bool spillFPRegs)
1190
{
1191
TR::SymbolReference *callSymRef = callNode->getSymbolReference();
1192
TR::MethodSymbol *callSymbol = callSymRef->getSymbol()->castToMethodSymbol();
1193
1194
bool isGPUHelper = callSymbol->isHelper() && (callSymRef->getReferenceNumber() == TR_estimateGPU ||
1195
callSymRef->getReferenceNumber() == TR_getStateGPU ||
1196
callSymRef->getReferenceNumber() == TR_regionEntryGPU ||
1197
callSymRef->getReferenceNumber() == TR_allocateGPUKernelParms ||
1198
callSymRef->getReferenceNumber() == TR_copyToGPU ||
1199
callSymRef->getReferenceNumber() == TR_launchGPUKernel ||
1200
callSymRef->getReferenceNumber() == TR_copyFromGPU ||
1201
callSymRef->getReferenceNumber() == TR_invalidateGPU ||
1202
callSymRef->getReferenceNumber() == TR_flushGPU ||
1203
callSymRef->getReferenceNumber() == TR_regionExitGPU);
1204
if (callSymbol->isJNI() || isGPUHelper)
1205
{
1206
return buildDirectJNIDispatch(callNode);
1207
}
1208
1209
TR_ASSERT(false, "call through TR::AMD64SystemLinkage::buildDirectDispatch instead.\n");
1210
return NULL;
1211
}
1212
1213
1214
void
1215
J9::X86::AMD64::JNILinkage::populateJNIDispatchInfo()
1216
{
1217
_JNIDispatchInfo.numJNIFrameSlotsPushed = 5;
1218
1219
_JNIDispatchInfo.JNIReturnRegister = NULL;
1220
_JNIDispatchInfo.linkageReturnRegister = NULL;
1221
1222
_JNIDispatchInfo.callPreDeps = NULL;
1223
_JNIDispatchInfo.callPostDeps = NULL;
1224
_JNIDispatchInfo.mergeLabelPostDeps = NULL;
1225
_JNIDispatchInfo.dispatchTrampolineRegister = NULL;
1226
1227
_JNIDispatchInfo.argSize = 0;
1228
1229
_JNIDispatchInfo.requiresFPstackPop = false;
1230
1231
_JNIDispatchInfo.dedicatedFrameRegisterIndex = _systemLinkage->getProperties().getIntegerScratchRegister(0);
1232
}
1233
1234
1235
TR::Register *J9::X86::AMD64::JNILinkage::buildDirectJNIDispatch(TR::Node *callNode)
1236
{
1237
#ifdef DEBUG
1238
if (debug("reportJNI"))
1239
{
1240
printf("AMD64 JNI Dispatch: %s calling %s\n", comp()->signature(), comp()->getDebug()->getName(callNode->getSymbolReference()));
1241
}
1242
#endif
1243
TR::SymbolReference *callSymRef = callNode->getSymbolReference();
1244
TR::MethodSymbol *callSymbol = callSymRef->getSymbol()->castToMethodSymbol();
1245
1246
bool isGPUHelper = callSymbol->isHelper() && (callSymRef->getReferenceNumber() == TR_estimateGPU ||
1247
callSymRef->getReferenceNumber() == TR_getStateGPU ||
1248
callSymRef->getReferenceNumber() == TR_regionEntryGPU ||
1249
callSymRef->getReferenceNumber() == TR_allocateGPUKernelParms ||
1250
callSymRef->getReferenceNumber() == TR_copyToGPU ||
1251
callSymRef->getReferenceNumber() == TR_launchGPUKernel ||
1252
callSymRef->getReferenceNumber() == TR_copyFromGPU ||
1253
callSymRef->getReferenceNumber() == TR_invalidateGPU ||
1254
callSymRef->getReferenceNumber() == TR_flushGPU ||
1255
callSymRef->getReferenceNumber() == TR_regionExitGPU);
1256
1257
static bool keepVMDuringGPUHelper = feGetEnv("TR_KeepVMDuringGPUHelper") ? true : false;
1258
1259
TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();
1260
TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);
1261
1262
TR::ResolvedMethodSymbol *resolvedMethodSymbol;
1263
TR_ResolvedMethod *resolvedMethod;
1264
TR::SymbolReference *gpuHelperSymRef;
1265
TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());
1266
1267
bool dropVMAccess;
1268
bool isJNIGCPoint;
1269
bool killNonVolatileGPRs;
1270
bool checkExceptions;
1271
bool createJNIFrame;
1272
bool tearDownJNIFrame;
1273
bool wrapRefs;
1274
bool passReceiver;
1275
bool passThread;
1276
1277
if (!isGPUHelper)
1278
{
1279
resolvedMethodSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
1280
resolvedMethod = resolvedMethodSymbol->getResolvedMethod();
1281
dropVMAccess = !fej9->jniRetainVMAccess(resolvedMethod);
1282
isJNIGCPoint = !fej9->jniNoGCPoint(resolvedMethod);
1283
killNonVolatileGPRs = isJNIGCPoint;
1284
checkExceptions = !fej9->jniNoExceptionsThrown(resolvedMethod);
1285
createJNIFrame = !fej9->jniNoNativeMethodFrame(resolvedMethod);
1286
tearDownJNIFrame = !fej9->jniNoSpecialTeardown(resolvedMethod);
1287
wrapRefs = !fej9->jniDoNotWrapObjects(resolvedMethod);
1288
passReceiver = !fej9->jniDoNotPassReceiver(resolvedMethod);
1289
passThread = !fej9->jniDoNotPassThread(resolvedMethod);
1290
}
1291
else
1292
{
1293
gpuHelperSymRef = comp()->getSymRefTab()->methodSymRefFromName(comp()->getMethodSymbol(), "com/ibm/jit/JITHelpers", "GPUHelper", "()V", TR::MethodSymbol::Static);
1294
resolvedMethodSymbol = gpuHelperSymRef->getSymbol()->castToResolvedMethodSymbol();
1295
resolvedMethod = resolvedMethodSymbol->getResolvedMethod();
1296
1297
if (keepVMDuringGPUHelper || (callSymRef->getReferenceNumber() == TR_copyToGPU || callSymRef->getReferenceNumber() == TR_copyFromGPU || callSymRef->getReferenceNumber() == TR_flushGPU || callSymRef->getReferenceNumber() == TR_regionExitGPU || callSymRef->getReferenceNumber() == TR_estimateGPU))
1298
dropVMAccess = false; //TR_copyToGPU, TR_copyFromGPU, TR_regionExitGPU (and all others if keepVMDuringGPUHelper is true)
1299
else
1300
dropVMAccess = true; //TR_regionEntryGPU, TR_launchGPUKernel, TR_estimateGPU, TR_allocateGPUKernelParms, ... (only if keepVMDuringGPUHelper is false)
1301
1302
isJNIGCPoint = true;
1303
killNonVolatileGPRs = isJNIGCPoint;
1304
checkExceptions = false;
1305
createJNIFrame = true;
1306
tearDownJNIFrame = true;
1307
wrapRefs = false; //unused for this code path
1308
passReceiver = true;
1309
passThread = false;
1310
}
1311
1312
populateJNIDispatchInfo();
1313
1314
static char * disablePureFn = feGetEnv("TR_DISABLE_PURE_FUNC_RECOGNITION");
1315
if (!isGPUHelper)
1316
{
1317
if (resolvedMethodSymbol->canDirectNativeCall())
1318
{
1319
dropVMAccess = false;
1320
killNonVolatileGPRs = false;
1321
isJNIGCPoint = false;
1322
checkExceptions = false;
1323
createJNIFrame = false;
1324
tearDownJNIFrame = false;
1325
}
1326
else if (callNode->getSymbol()->castToResolvedMethodSymbol()->isPureFunction() && (disablePureFn == NULL))
1327
{
1328
dropVMAccess = false;
1329
isJNIGCPoint = false;
1330
checkExceptions = false;
1331
}
1332
}
1333
1334
// Anchor the Java frame here to establish the top of the frame. The reason this must occur here
1335
// is because the subsequent manual adjustments of the stack pointer confuse the vfp logic.
1336
// This should be fixed in a subsquent revision of that code.
1337
//
1338
TR::X86VFPDedicateInstruction *vfpDedicateInstruction =
1339
generateVFPDedicateInstruction(machine()->getRealRegister(_JNIDispatchInfo.dedicatedFrameRegisterIndex), callNode, cg());
1340
1341
// First, build a JNI callout frame on the Java stack.
1342
//
1343
TR::LabelSymbol *returnAddrLabel = generateLabelSymbol(cg());
1344
if (createJNIFrame)
1345
{
1346
if (isGPUHelper)
1347
callNode->setSymbolReference(gpuHelperSymRef);
1348
1349
buildJNICallOutFrame(callNode, returnAddrLabel);
1350
1351
if (isGPUHelper)
1352
callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards
1353
}
1354
1355
// Switch from the Java stack to the C stack:
1356
TR::J9LinkageUtils::switchToMachineCStack(callNode, cg());
1357
1358
// Preserve the VMThread pointer on the C stack.
1359
// Adjust the argSize to include the just pushed VMThread pointer.
1360
//
1361
generateRegInstruction(TR::InstOpCode::PUSHReg, callNode, vmThreadReg, cg());
1362
if (passThread || isGPUHelper)
1363
{
1364
_JNIDispatchInfo.argSize = TR::Compiler->om.sizeofReferenceAddress();
1365
}
1366
1367
TR::LabelSymbol *startJNISequence = generateLabelSymbol(cg());
1368
startJNISequence->setStartInternalControlFlow();
1369
generateLabelInstruction(TR::InstOpCode::label, callNode, startJNISequence, cg());
1370
1371
if (isGPUHelper)
1372
callNode->setSymbolReference(gpuHelperSymRef);
1373
1374
buildOutgoingJNIArgsAndDependencies(callNode, passThread, passReceiver, killNonVolatileGPRs);
1375
1376
if (isGPUHelper)
1377
callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards
1378
1379
if (dropVMAccess)
1380
{
1381
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
1382
releaseVMAccessAtomicFree(callNode);
1383
#else
1384
releaseVMAccess(callNode);
1385
#endif
1386
}
1387
1388
uintptr_t targetAddress;
1389
1390
if (isGPUHelper)
1391
{
1392
callNode->setSymbolReference(gpuHelperSymRef);
1393
targetAddress = (uintptr_t)callSymbol->getMethodAddress();
1394
}
1395
else
1396
{
1397
TR::ResolvedMethodSymbol *callSymbol1 = callNode->getSymbol()->castToResolvedMethodSymbol();
1398
targetAddress = (uintptr_t)callSymbol1->getResolvedMethod()->startAddressForJNIMethod(comp());
1399
}
1400
1401
TR::Instruction *callInstr = generateMethodDispatch(callNode, isJNIGCPoint, targetAddress);
1402
1403
if (isGPUHelper)
1404
callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards
1405
1406
// TODO: will need an AOT relocation for this one at some point.
1407
// Lay down a label for the frame push to reference.
1408
//
1409
generateLabelInstruction(callInstr, TR::InstOpCode::label, returnAddrLabel, cg());
1410
1411
if (_JNIDispatchInfo.JNIReturnRegister)
1412
{
1413
if (isGPUHelper)
1414
callNode->setSymbolReference(gpuHelperSymRef);
1415
1416
cleanupReturnValue(callNode, _JNIDispatchInfo.linkageReturnRegister, _JNIDispatchInfo.JNIReturnRegister);
1417
1418
if (isGPUHelper)
1419
callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards
1420
1421
if (_JNIDispatchInfo.linkageReturnRegister != _JNIDispatchInfo.JNIReturnRegister)
1422
cg()->stopUsingRegister(_JNIDispatchInfo.linkageReturnRegister);
1423
}
1424
1425
// Restore the VMThread back from the C stack.
1426
//
1427
generateRegInstruction(TR::InstOpCode::POPReg, callNode, vmThreadReg, cg());
1428
1429
if (dropVMAccess)
1430
{
1431
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
1432
acquireVMAccessAtomicFree(callNode);
1433
#else
1434
acquireVMAccess(callNode);
1435
#endif
1436
}
1437
1438
if (resolvedMethod->returnType() == TR::Address && wrapRefs)
1439
{
1440
// Unless NULL, need to indirect once to get the real Java reference.
1441
//
1442
// This MUST be done AFTER VM access has been re-acquired!
1443
//
1444
TR::Register *targetReg = _JNIDispatchInfo.JNIReturnRegister;
1445
TR::LabelSymbol *nullLabel = generateLabelSymbol(cg());
1446
generateRegRegInstruction(TR::InstOpCode::TESTRegReg(), callNode, targetReg, targetReg, cg());
1447
generateLabelInstruction(TR::InstOpCode::JE4, callNode, nullLabel, cg());
1448
1449
generateRegMemInstruction(
1450
TR::InstOpCode::LRegMem(),
1451
callNode,
1452
targetReg,
1453
generateX86MemoryReference(targetReg, 0, cg()),
1454
cg());
1455
1456
generateLabelInstruction(TR::InstOpCode::label, callNode, nullLabel, cg());
1457
}
1458
1459
// 1) Store out the machine sp into the vm thread. It has to be done as sometimes
1460
// it gets tromped on by call backs.
1461
generateMemRegInstruction(
1462
TR::InstOpCode::SMemReg(),
1463
callNode,
1464
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetMachineSPOffset(), cg()),
1465
espReal,
1466
cg());
1467
1468
TR::J9LinkageUtils::switchToJavaStack(callNode, cg());
1469
1470
if (createJNIFrame)
1471
{
1472
generateRegMemInstruction(
1473
TR::InstOpCode::ADDRegMem(),
1474
callNode,
1475
espReal,
1476
generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaLiteralsOffset(), cg()),
1477
cg());
1478
}
1479
1480
if (createJNIFrame && tearDownJNIFrame)
1481
{
1482
cleanupJNIRefPool(callNode);
1483
}
1484
1485
// Clean up JNI callout frame.
1486
//
1487
if (createJNIFrame)
1488
{
1489
TR::X86RegImmInstruction *instr = generateRegImmInstruction(
1490
TR::InstOpCode::ADDRegImms(),
1491
callNode,
1492
espReal,
1493
_JNIDispatchInfo.numJNIFrameSlotsPushed * TR::Compiler->om.sizeofReferenceAddress(),
1494
cg());
1495
}
1496
1497
if (checkExceptions)
1498
{
1499
checkForJNIExceptions(callNode);
1500
}
1501
1502
generateVFPReleaseInstruction(vfpDedicateInstruction, callNode, cg());
1503
1504
TR::LabelSymbol *restartLabel = generateLabelSymbol(cg());
1505
restartLabel->setEndInternalControlFlow();
1506
generateLabelInstruction(TR::InstOpCode::label, callNode, restartLabel, _JNIDispatchInfo.mergeLabelPostDeps, cg());
1507
1508
return _JNIDispatchInfo.JNIReturnRegister;
1509
}
1510
1511
1512
#endif
1513
1514