Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/compiler/aarch64/codegen/ARM64JNILinkage.cpp
6004 views
1
/*******************************************************************************
2
* Copyright (c) 2019, 2022 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
#include "codegen/ARM64JNILinkage.hpp"
24
25
#include <algorithm>
26
#include "codegen/ARM64HelperCallSnippet.hpp"
27
#include "codegen/CodeGeneratorUtils.hpp"
28
#include "codegen/GenerateInstructions.hpp"
29
#include "codegen/Linkage_inlines.hpp"
30
#include "codegen/MemoryReference.hpp"
31
#include "codegen/RegisterDependency.hpp"
32
#include "codegen/Relocation.hpp"
33
#include "env/StackMemoryRegion.hpp"
34
#include "env/VMJ9.h"
35
#include "il/Node.hpp"
36
#include "il/Node_inlines.hpp"
37
#include "il/StaticSymbol.hpp"
38
#include "il/SymbolReference.hpp"
39
#include "infra/Assert.hpp"
40
41
#if defined(OSX)
42
#include "env/j9method.h"
43
#endif
44
45
J9::ARM64::JNILinkage::JNILinkage(TR::CodeGenerator *cg)
46
: J9::ARM64::PrivateLinkage(cg)
47
{
48
_systemLinkage = cg->getLinkage(TR_System);
49
}
50
51
int32_t J9::ARM64::JNILinkage::buildArgs(TR::Node *callNode,
52
TR::RegisterDependencyConditions *dependencies)
53
{
54
TR_ASSERT(0, "Should call J9::ARM64::JNILinkage::buildJNIArgs instead.");
55
return 0;
56
}
57
58
TR::Register *J9::ARM64::JNILinkage::buildIndirectDispatch(TR::Node *callNode)
59
{
60
TR_ASSERT(0, "Calling J9::ARM64::JNILinkage::buildIndirectDispatch does not make sense.");
61
return NULL;
62
}
63
64
void J9::ARM64::JNILinkage::buildVirtualDispatch(
65
TR::Node *callNode,
66
TR::RegisterDependencyConditions *dependencies,
67
uint32_t argSize)
68
{
69
TR_ASSERT(0, "Calling J9::ARM64::JNILinkage::buildVirtualDispatch does not make sense.");
70
}
71
72
void J9::ARM64::JNILinkage::releaseVMAccess(TR::Node *callNode, TR::Register *vmThreadReg, TR::Register *scratchReg0, TR::Register *scratchReg1, TR::Register *scratchReg2, TR::Register *scratchReg3)
73
{
74
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
75
76
// Release VM access (spin lock)
77
//
78
// addimmx scratch0, vmThreadReg, #publicFlagsOffset
79
// movzx scratch1, constReleaseVMAccessOutOfLineMask
80
//
81
// dmb ishst
82
// loopHead:
83
// ldxrx scratch2, [scratch0]
84
// tst scratch2, scratch1
85
// b.ne releaseVMAccessSnippet
86
// andimmx scratch2, scratch2, constReleaseVMAccessMask
87
// stxrx scratch2, scratch2, [scratch0]
88
// cbnz scratch2, loopHead
89
// releaseVMRestart:
90
//
91
92
const int releaseVMAccessMask = fej9->constReleaseVMAccessMask();
93
94
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::addimmx, callNode, scratchReg0, vmThreadReg, fej9->thisThreadGetPublicFlagsOffset());
95
loadConstant64(cg(), callNode, fej9->constReleaseVMAccessOutOfLineMask(), scratchReg1);
96
// dmb ishst (Inner Shareable store barrier)
97
// Arm Architecture Reference Manual states:
98
// "This architecture assumes that all PEs that use the same operating system or hypervisor are in the same Inner Shareable shareability domain"
99
// thus, inner shareable dmb suffices
100
generateSynchronizationInstruction(cg(), TR::InstOpCode::dmb, callNode, 0xb);
101
102
TR::LabelSymbol *loopHead = generateLabelSymbol(cg());
103
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, loopHead);
104
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldxrx, callNode, scratchReg2, TR::MemoryReference::createWithDisplacement(cg(), scratchReg0, 0));
105
generateTestInstruction(cg(), callNode, scratchReg2, scratchReg1, true);
106
107
TR::LabelSymbol *releaseVMAccessSnippetLabel = generateLabelSymbol(cg());
108
TR::LabelSymbol *releaseVMAccessRestartLabel = generateLabelSymbol(cg());
109
TR::SymbolReference *relVMSymRef = comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(comp()->getJittedMethodSymbol());
110
111
TR::Snippet *releaseVMAccessSnippet = new (trHeapMemory()) TR::ARM64HelperCallSnippet(cg(), callNode, releaseVMAccessSnippetLabel, relVMSymRef, releaseVMAccessRestartLabel);
112
cg()->addSnippet(releaseVMAccessSnippet);
113
generateConditionalBranchInstruction(cg(), TR::InstOpCode::b_cond, callNode, releaseVMAccessSnippetLabel, TR::CC_NE);
114
releaseVMAccessSnippet->gcMap().setGCRegisterMask(0);
115
116
uint64_t mask = fej9->constReleaseVMAccessMask();
117
bool n;
118
uint32_t imm;
119
if (logicImmediateHelper(mask, true, n, imm))
120
{
121
generateLogicalImmInstruction(cg(), TR::InstOpCode::andimmx, callNode, scratchReg2, scratchReg2, n, imm);
122
}
123
else
124
{
125
loadConstant64(cg(), callNode, mask, scratchReg3);
126
generateTrg1Src2Instruction(cg(), TR::InstOpCode::andx, callNode, scratchReg2, scratchReg2, scratchReg3);
127
}
128
generateTrg1MemSrc1Instruction(cg(), TR::InstOpCode::stxrx, callNode, scratchReg2, TR::MemoryReference::createWithDisplacement(cg(), scratchReg0, 0), scratchReg2);
129
generateCompareBranchInstruction(cg(), TR::InstOpCode::cbnzx, callNode, scratchReg2, loopHead);
130
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, releaseVMAccessRestartLabel);
131
}
132
133
void J9::ARM64::JNILinkage::acquireVMAccess(TR::Node *callNode, TR::Register *vmThreadReg, TR::Register *scratchReg0, TR::Register *scratchReg1, TR::Register *scratchReg2)
134
{
135
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
136
137
// Re-acquire VM access.
138
// addimmx scratch0, vmThreadReg, #publicFlagsOffset
139
// movzx scratch1, constAcquireMAccessOutOfLineMask
140
//
141
// loopHead:
142
// ldxrx scratch2, [scratch0]
143
// cbnzx scratch2, reacquireVMAccessSnippet
144
// stxrx scratch2, scratch1, [scratch0]
145
// cbnz scratch2, loopHead
146
// dmb ishld
147
// reacquireVMAccessRestartLabel:
148
//
149
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::addimmx, callNode, scratchReg0, vmThreadReg, fej9->thisThreadGetPublicFlagsOffset());
150
loadConstant64(cg(), callNode, fej9->constAcquireVMAccessOutOfLineMask(), scratchReg1);
151
152
TR::LabelSymbol *loopHead = generateLabelSymbol(cg());
153
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, loopHead);
154
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldxrx, callNode, scratchReg2, TR::MemoryReference::createWithDisplacement(cg(), scratchReg0, 0));
155
156
TR::LabelSymbol *reacquireVMAccessSnippetLabel = generateLabelSymbol(cg());
157
TR::LabelSymbol *reacquireVMAccessRestartLabel = generateLabelSymbol(cg());
158
TR::SymbolReference *acqVMSymRef = comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp()->getJittedMethodSymbol());
159
160
TR::Snippet *reacquireVMAccessSnippet = new (trHeapMemory()) TR::ARM64HelperCallSnippet(cg(), callNode, reacquireVMAccessSnippetLabel, acqVMSymRef, reacquireVMAccessRestartLabel);
161
cg()->addSnippet(reacquireVMAccessSnippet);
162
generateCompareBranchInstruction(cg(), TR::InstOpCode::cbnzx, callNode, scratchReg2, reacquireVMAccessSnippetLabel);
163
reacquireVMAccessSnippet->gcMap().setGCRegisterMask(0);
164
165
generateTrg1MemSrc1Instruction(cg(), TR::InstOpCode::stxrx, callNode, scratchReg2, TR::MemoryReference::createWithDisplacement(cg(), scratchReg0, 0), scratchReg1);
166
generateCompareBranchInstruction(cg(), TR::InstOpCode::cbnzx, callNode, scratchReg2, loopHead);
167
// dmb ishld (Inner Shareable load barrier)
168
generateSynchronizationInstruction(cg(), TR::InstOpCode::dmb, callNode, 0x9);
169
170
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, reacquireVMAccessRestartLabel);
171
}
172
173
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
174
void J9::ARM64::JNILinkage::releaseVMAccessAtomicFree(TR::Node *callNode, TR::Register *vmThreadReg, TR::Register *scratchReg0, TR::Register *scratchReg1)
175
{
176
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
177
TR_Debug *debugObj = cg()->getDebug();
178
179
// Release VM access atomic free
180
//
181
// movzx scratch0, #1
182
// strimmx scratch0, [vmThreadReg, #inNativeOffset]
183
// ldrimmx scratch1, [vmThreadReg, #publicFlagsOffset]
184
// cmpimmx scratch1, #J9_PUBLIC_FLAGS_VM_ACCESS
185
// b.ne releaseVMAccessSnippet
186
// releaseVMAccessRestart:
187
//
188
static_assert(static_cast<uint64_t>(J9_PUBLIC_FLAGS_VM_ACCESS) < (1 << 12), "J9_PUBLIC_FLAGS_VM_ACCESS must fit in immediate");
189
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movzx, callNode, scratchReg0, 1);
190
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH
191
auto strInNativeInstr = generateMemSrc1Instruction(cg(), TR::InstOpCode::strimmx, callNode, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, offsetof(J9VMThread, inNative)), scratchReg0);
192
auto ldrPubliFlagsInstr = generateTrg1MemInstruction(cg(), TR::InstOpCode::ldrimmx, callNode, scratchReg1, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetPublicFlagsOffset()));
193
#else /* J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */
194
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::addimmx, callNode, scratchReg1, vmThreadReg, offsetof(J9VMThread, inNative));
195
auto strInNativeInstr = generateMemSrc1Instruction(cg(), TR::InstOpCode::stlrx, callNode, TR::MemoryReference::createWithDisplacement(cg(), scratchReg1, 0), scratchReg0);
196
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::addimmx, callNode, scratchReg0, vmThreadReg, fej9->thisThreadGetPublicFlagsOffset());
197
auto ldrPubliFlagsInstr = generateTrg1MemInstruction(cg(), TR::InstOpCode::ldarx, callNode, scratchReg1, TR::MemoryReference::createWithDisplacement(cg(), scratchReg0, 0));
198
#endif
199
if (debugObj)
200
{
201
debugObj->addInstructionComment(strInNativeInstr, "store 1 to vmThread->inNative");
202
debugObj->addInstructionComment(ldrPubliFlagsInstr, "load vmThread->publicFlags");
203
}
204
generateCompareImmInstruction(cg(), callNode, scratchReg1, J9_PUBLIC_FLAGS_VM_ACCESS, true);
205
206
TR::LabelSymbol *releaseVMAccessSnippetLabel = generateLabelSymbol(cg());
207
TR::LabelSymbol *releaseVMAccessRestartLabel = generateLabelSymbol(cg());
208
TR::SymbolReference *relVMSymRef = comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(comp()->getJittedMethodSymbol());
209
210
TR::Snippet *releaseVMAccessSnippet = new (trHeapMemory()) TR::ARM64HelperCallSnippet(cg(), callNode, releaseVMAccessSnippetLabel, relVMSymRef, releaseVMAccessRestartLabel);
211
cg()->addSnippet(releaseVMAccessSnippet);
212
generateConditionalBranchInstruction(cg(), TR::InstOpCode::b_cond, callNode, releaseVMAccessSnippetLabel, TR::CC_NE);
213
releaseVMAccessSnippet->gcMap().setGCRegisterMask(0);
214
215
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, releaseVMAccessRestartLabel);
216
}
217
218
void J9::ARM64::JNILinkage::acquireVMAccessAtomicFree(TR::Node *callNode, TR::Register *vmThreadReg, TR::Register *scratchReg0, TR::Register *zeroReg)
219
{
220
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
221
TR_Debug *debugObj = cg()->getDebug();
222
223
// Re-acquire VM access atomic free
224
// strimmx xzr, [vmThreadReg, #inNativeOffset]
225
// ldrimmx scratch0, [vmThreadReg, #publicFlagsOffset]
226
// cmpimmx scratch0, #J9_PUBLIC_FLAGS_VM_ACCESS
227
// b.ne acquireVMAccessSnippet
228
// reacquireVMAccessRestart:
229
//
230
static_assert(static_cast<uint64_t>(J9_PUBLIC_FLAGS_VM_ACCESS) < (1 << 12), "J9_PUBLIC_FLAGS_VM_ACCESS must fit in immediate");
231
auto strInNativeInstr = generateMemSrc1Instruction(cg(), TR::InstOpCode::strimmx, callNode, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, offsetof(J9VMThread, inNative)), zeroReg);
232
#ifndef J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH
233
generateSynchronizationInstruction(cg(), TR::InstOpCode::dmb, callNode, 0xb);
234
#endif
235
auto ldrPubliFlagsInstr = generateTrg1MemInstruction(cg(), TR::InstOpCode::ldrimmx, callNode, scratchReg0, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetPublicFlagsOffset()));
236
if (debugObj)
237
{
238
debugObj->addInstructionComment(strInNativeInstr, "store 0 to vmThread->inNative");
239
debugObj->addInstructionComment(ldrPubliFlagsInstr, "load vmThread->publicFlags");
240
}
241
generateCompareImmInstruction(cg(), callNode, scratchReg0, J9_PUBLIC_FLAGS_VM_ACCESS, true);
242
243
TR::LabelSymbol *reacquireVMAccessSnippetLabel = generateLabelSymbol(cg());
244
TR::LabelSymbol *reacquireVMAccessRestartLabel = generateLabelSymbol(cg());
245
TR::SymbolReference *acqVMSymRef = comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp()->getJittedMethodSymbol());
246
247
TR::Snippet *reacquireVMAccessSnippet = new (trHeapMemory()) TR::ARM64HelperCallSnippet(cg(), callNode, reacquireVMAccessSnippetLabel, acqVMSymRef, reacquireVMAccessRestartLabel);
248
cg()->addSnippet(reacquireVMAccessSnippet);
249
generateConditionalBranchInstruction(cg(), TR::InstOpCode::b_cond, callNode, reacquireVMAccessSnippetLabel, TR::CC_NE);
250
reacquireVMAccessSnippet->gcMap().setGCRegisterMask(0);
251
252
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, reacquireVMAccessRestartLabel);
253
}
254
#endif /* J9VM_INTERP_ATOMIC_FREE_JNI */
255
256
void J9::ARM64::JNILinkage::buildJNICallOutFrame(TR::Node *callNode, bool isWrapperForJNI, TR::LabelSymbol *returnAddrLabel,
257
TR::Register *vmThreadReg, TR::Register *javaStackReg, TR::Register *scratchReg0, TR::Register *scratchReg1)
258
{
259
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
260
261
// begin: mask out the magic bit that indicates JIT frames below
262
loadConstant64(cg(), callNode, 0, scratchReg1);
263
generateMemSrc1Instruction(cg(), TR::InstOpCode::strimmx, callNode, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetJavaFrameFlagsOffset()), scratchReg1);
264
265
// Grab 5 slots in the frame.
266
//
267
// 4: tag bits (savedA0)
268
// 3: empty (savedPC)
269
// 2: return address in this frame (savedCP)
270
// 1: frame flags
271
// 0: RAM method
272
//
273
// push tag bits (savedA0)
274
int32_t tagBits = fej9->constJNICallOutFrameSpecialTag();
275
// if the current method is simply a wrapper for the JNI call, hide the call-out stack frame
276
if (isWrapperForJNI)
277
tagBits |= fej9->constJNICallOutFrameInvisibleTag();
278
loadConstant64(cg(), callNode, tagBits, scratchReg0);
279
280
generateMemSrc1Instruction(cg(), TR::InstOpCode::strprex, callNode, TR::MemoryReference::createWithDisplacement(cg(), javaStackReg, -TR::Compiler->om.sizeofReferenceAddress()), scratchReg0);
281
282
// empty saved pc slot
283
generateMemSrc1Instruction(cg(), TR::InstOpCode::strprex, callNode, TR::MemoryReference::createWithDisplacement(cg(), javaStackReg, -TR::Compiler->om.sizeofReferenceAddress()), scratchReg1);
284
285
// push return address (savedCP)
286
generateTrg1ImmSymInstruction(cg(), TR::InstOpCode::adr, callNode, scratchReg0, 0, returnAddrLabel);
287
generateMemSrc1Instruction(cg(), TR::InstOpCode::strprex, callNode, TR::MemoryReference::createWithDisplacement(cg(), javaStackReg, -TR::Compiler->om.sizeofReferenceAddress()), scratchReg0);
288
289
// push flags
290
loadConstant64(cg(), callNode, fej9->constJNICallOutFrameFlags(), scratchReg0);
291
generateMemSrc1Instruction(cg(), TR::InstOpCode::strprex, callNode, TR::MemoryReference::createWithDisplacement(cg(), javaStackReg, -TR::Compiler->om.sizeofReferenceAddress()), scratchReg0);
292
293
TR::ResolvedMethodSymbol *resolvedMethodSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
294
TR_ResolvedMethod *resolvedMethod = resolvedMethodSymbol->getResolvedMethod();
295
uintptr_t methodAddr = reinterpret_cast<uintptr_t>(resolvedMethod->resolvedMethodAddress());
296
297
// push the RAM method for the native
298
if (fej9->needClassAndMethodPointerRelocations())
299
{
300
// load a 64-bit constant into a register with a fixed 4 instruction sequence
301
TR::Instruction *firstInstruction = generateTrg1ImmInstruction(cg(), TR::InstOpCode::movzx, callNode, scratchReg0, methodAddr & 0x0000ffff);
302
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, scratchReg0, ((methodAddr >> 16) & 0x0000ffff) | TR::MOV_LSL16);
303
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, scratchReg0, ((methodAddr >> 32) & 0x0000ffff) | TR::MOV_LSL32);
304
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, scratchReg0, (methodAddr >> 48) | TR::MOV_LSL48);
305
306
TR_ExternalRelocationTargetKind reloType;
307
if (resolvedMethodSymbol->isSpecial())
308
reloType = TR_SpecialRamMethodConst;
309
else if (resolvedMethodSymbol->isStatic())
310
reloType = TR_StaticRamMethodConst;
311
else if (resolvedMethodSymbol->isVirtual())
312
reloType = TR_VirtualRamMethodConst;
313
else
314
{
315
reloType = TR_NoRelocation;
316
TR_ASSERT(0, "JNI relocation not supported.");
317
}
318
cg()->addExternalRelocation(new (trHeapMemory()) TR::BeforeBinaryEncodingExternalRelocation(
319
firstInstruction,
320
reinterpret_cast<uint8_t *>(callNode->getSymbolReference()),
321
reinterpret_cast<uint8_t *>(callNode->getInlinedSiteIndex()),
322
reloType, cg()),
323
__FILE__,__LINE__, callNode);
324
325
}
326
else
327
{
328
loadConstant64(cg(), callNode, methodAddr, scratchReg0);
329
}
330
generateMemSrc1Instruction(cg(), TR::InstOpCode::strprex, callNode, TR::MemoryReference::createWithDisplacement(cg(), javaStackReg, -TR::Compiler->om.sizeofReferenceAddress()), scratchReg0);
331
332
// store out java sp
333
generateMemSrc1Instruction(cg(), TR::InstOpCode::strimmx, callNode, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetJavaSPOffset()), javaStackReg);
334
335
// store out pc and literals values indicating the callout frame
336
loadConstant64(cg(), callNode, fej9->constJNICallOutFrameType(), scratchReg0);
337
generateMemSrc1Instruction(cg(), TR::InstOpCode::strimmx, callNode, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetJavaPCOffset()), scratchReg0);
338
339
generateMemSrc1Instruction(cg(), TR::InstOpCode::strimmx, callNode, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetJavaLiteralsOffset()), scratchReg1);
340
341
}
342
343
void J9::ARM64::JNILinkage::restoreJNICallOutFrame(TR::Node *callNode, bool tearDownJNIFrame, TR::Register *vmThreadReg, TR::Register *javaStackReg, TR::Register *scratchReg)
344
{
345
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
346
347
// restore stack pointer: need to deal with growable stack -- stack may already be moved.
348
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldrimmx, callNode, scratchReg, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetJavaLiteralsOffset()));
349
generateTrg1MemInstruction(cg(),TR::InstOpCode::ldrimmx, callNode, javaStackReg, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetJavaSPOffset()));
350
generateTrg1Src2Instruction(cg(), TR::InstOpCode::addx, callNode, javaStackReg, scratchReg, javaStackReg);
351
352
if (tearDownJNIFrame)
353
{
354
// must check to see if the ref pool was used and clean them up if so--or we
355
// leave a bunch of pinned garbage behind that screws up the gc quality forever
356
TR::LabelSymbol *refPoolRestartLabel = generateLabelSymbol(cg());
357
TR::LabelSymbol *refPoolSnippetLabel = generateLabelSymbol(cg());
358
TR::SymbolReference *collapseSymRef = cg()->getSymRefTab()->findOrCreateRuntimeHelper(TR_ARM64jitCollapseJNIReferenceFrame);
359
TR::Snippet *snippet = new (trHeapMemory()) TR::ARM64HelperCallSnippet(cg(), callNode, refPoolSnippetLabel, collapseSymRef, refPoolRestartLabel);
360
cg()->addSnippet(snippet);
361
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldrimmx, callNode, scratchReg, TR::MemoryReference::createWithDisplacement(cg(), javaStackReg, fej9->constJNICallOutFrameFlagsOffset()));
362
363
TR_ASSERT_FATAL(fej9->constJNIReferenceFrameAllocatedFlags() == 0x30000, "constJNIReferenceFrameAllocatedFlags must be 0x30000");
364
generateTestImmInstruction(cg(), callNode, scratchReg, 0x401, false); // 0x401 is immr:imms for 0x30000
365
generateConditionalBranchInstruction(cg(), TR::InstOpCode::b_cond, callNode, refPoolSnippetLabel, TR::CC_NE);
366
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, refPoolRestartLabel);
367
}
368
369
// Restore the JIT frame
370
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::addimmx, callNode, javaStackReg, javaStackReg, 5*TR::Compiler->om.sizeofReferenceAddress());
371
}
372
373
374
size_t J9::ARM64::JNILinkage::buildJNIArgs(TR::Node *callNode, TR::RegisterDependencyConditions *dependencies, bool passThread, bool passReceiver, bool killNonVolatileGPRs)
375
{
376
const TR::ARM64LinkageProperties &properties = _systemLinkage->getProperties();
377
TR::ARM64MemoryArgument *pushToMemory = NULL;
378
TR::Register *argMemReg;
379
TR::Register *tempReg;
380
int32_t argIndex = 0;
381
int32_t numMemArgs = 0;
382
int32_t argOffset = 0;
383
int32_t numIntegerArgs = 0;
384
int32_t numFloatArgs = 0;
385
int32_t totalSize = 0;
386
int32_t i;
387
388
TR::Node *child;
389
TR::DataType childType;
390
TR::DataType resType = callNode->getType();
391
392
#if defined(OSX)
393
TR::SymbolReference *methodSymRef = callNode->getSymbolReference();
394
TR::MethodSymbol *methodSymbol = methodSymRef->getSymbol()->castToMethodSymbol();
395
TR::Method *method = methodSymbol->getMethod();
396
const char *sig = method->signatureChars();
397
int32_t sigLen = method->signatureLength();
398
char *sigCursor = (char *)sig;
399
#endif
400
401
uint32_t firstArgumentChild = callNode->getFirstArgumentIndex();
402
if (passThread)
403
{
404
numIntegerArgs += 1;
405
}
406
// if fastJNI do not pass the receiver just evaluate the first child
407
if (!passReceiver)
408
{
409
TR::Node *firstArgChild = callNode->getChild(firstArgumentChild);
410
cg()->evaluate(firstArgChild);
411
cg()->decReferenceCount(firstArgChild);
412
firstArgumentChild += 1;
413
}
414
/* Step 1 - figure out how many arguments are going to be spilled to memory i.e. not in registers */
415
for (i = firstArgumentChild; i < callNode->getNumChildren(); i++)
416
{
417
child = callNode->getChild(i);
418
childType = child->getDataType();
419
420
switch (childType)
421
{
422
case TR::Int8:
423
case TR::Int16:
424
case TR::Int32:
425
case TR::Int64:
426
case TR::Address:
427
if (numIntegerArgs >= properties.getNumIntArgRegs())
428
{
429
numMemArgs++;
430
#if defined(LINUX)
431
totalSize += 8;
432
#elif defined(OSX)
433
TR::DataType nativeType = OMR::Symbol::convertSigCharToType(*sigCursor);
434
int32_t nativeTypeSize = TR::DataType::getSize(nativeType);
435
if (nativeTypeSize > 1)
436
{
437
totalSize = (totalSize + nativeTypeSize - 1) & ~(nativeTypeSize - 1);
438
}
439
totalSize += nativeTypeSize;
440
#else
441
#error Unsupported platform
442
#endif
443
}
444
numIntegerArgs++;
445
break;
446
447
case TR::Float:
448
case TR::Double:
449
if (numFloatArgs >= properties.getNumFloatArgRegs())
450
{
451
numMemArgs++;
452
#if defined(LINUX)
453
totalSize += 8;
454
#elif defined(OSX)
455
if (childType == TR::Double)
456
{
457
totalSize = (totalSize + 7) & ~7; // adjust to 8-byte boundary
458
totalSize += 8;
459
}
460
else
461
{
462
totalSize += 4;
463
}
464
#else
465
#error Unsupported platform
466
#endif
467
}
468
numFloatArgs++;
469
break;
470
471
default:
472
TR_ASSERT(false, "Argument type %s is not supported\n", childType.toString());
473
}
474
475
#if defined(OSX)
476
sigCursor = nextSignatureArgument(sigCursor);
477
#endif
478
}
479
480
// From here, down, any new stack allocations will expire / die when the function returns
481
TR::StackMemoryRegion stackMemoryRegion(*trMemory());
482
/* End result of Step 1 - determined number of memory arguments! */
483
if (numMemArgs > 0)
484
{
485
pushToMemory = new (trStackMemory()) TR::ARM64MemoryArgument[numMemArgs];
486
487
argMemReg = cg()->allocateRegister();
488
}
489
490
// align to 16-byte boundary
491
totalSize = (totalSize + 15) & (~15);
492
493
numIntegerArgs = 0;
494
numFloatArgs = 0;
495
496
if (passThread)
497
{
498
// first argument is JNIenv
499
numIntegerArgs += 1;
500
}
501
502
#if defined(OSX)
503
sigCursor = (char *)sig;
504
#endif
505
506
for (i = firstArgumentChild; i < callNode->getNumChildren(); i++)
507
{
508
TR::Register *argRegister;
509
TR::InstOpCode::Mnemonic op;
510
bool checkSplit = true;
511
512
child = callNode->getChild(i);
513
childType = child->getDataType();
514
515
switch (childType)
516
{
517
case TR::Int8:
518
case TR::Int16:
519
case TR::Int32:
520
case TR::Int64:
521
case TR::Address:
522
if (childType == TR::Address)
523
{
524
argRegister = pushJNIReferenceArg(child);
525
checkSplit = false;
526
}
527
else if (childType == TR::Int64)
528
argRegister = pushLongArg(child);
529
else
530
argRegister = pushIntegerWordArg(child);
531
532
if (numIntegerArgs < properties.getNumIntArgRegs())
533
{
534
if (checkSplit && !cg()->canClobberNodesRegister(child, 0))
535
{
536
if (argRegister->containsCollectedReference())
537
tempReg = cg()->allocateCollectedReferenceRegister();
538
else
539
tempReg = cg()->allocateRegister();
540
generateMovInstruction(cg(), callNode, tempReg, argRegister);
541
argRegister = tempReg;
542
}
543
if (numIntegerArgs == 0 &&
544
(resType.isAddress() || resType.isInt32() || resType.isInt64()))
545
{
546
TR::Register *resultReg;
547
if (resType.isAddress())
548
resultReg = cg()->allocateCollectedReferenceRegister();
549
else
550
resultReg = cg()->allocateRegister();
551
552
dependencies->addPreCondition(argRegister, TR::RealRegister::x0);
553
dependencies->addPostCondition(resultReg, TR::RealRegister::x0);
554
}
555
else
556
{
557
TR::addDependency(dependencies, argRegister, properties.getIntegerArgumentRegister(numIntegerArgs), TR_GPR, cg());
558
}
559
}
560
else
561
{
562
// numIntegerArgs >= properties.getNumIntArgRegs()
563
int offsetInc;
564
#if defined(LINUX)
565
offsetInc = 8; // always 8-byte aligned
566
op = (childType == TR::Address || childType == TR::Int64) ?
567
TR::InstOpCode::strimmx : TR::InstOpCode::strimmw;
568
#elif defined(OSX)
569
TR::DataType nativeType = OMR::Symbol::convertSigCharToType(*sigCursor);
570
int32_t nativeTypeSize = TR::DataType::getSize(nativeType);
571
offsetInc = nativeTypeSize;
572
if (nativeTypeSize > 1)
573
{
574
argOffset = (argOffset + nativeTypeSize - 1) & ~(nativeTypeSize - 1);
575
}
576
switch (nativeTypeSize)
577
{
578
case 8:
579
op = TR::InstOpCode::strimmx;
580
break;
581
case 4:
582
op = TR::InstOpCode::strimmw;
583
break;
584
case 2:
585
op = TR::InstOpCode::strhimm;
586
break;
587
case 1:
588
op = TR::InstOpCode::strbimm;
589
break;
590
}
591
#else
592
#error Unsupported platform
593
#endif
594
getOutgoingArgumentMemRef(argMemReg, argOffset, argRegister, op, pushToMemory[argIndex++]);
595
argOffset += offsetInc;
596
}
597
numIntegerArgs++;
598
break;
599
600
case TR::Float:
601
case TR::Double:
602
if (childType == TR::Float)
603
argRegister = pushFloatArg(child);
604
else
605
argRegister = pushDoubleArg(child);
606
607
if (numFloatArgs < properties.getNumFloatArgRegs())
608
{
609
if (!cg()->canClobberNodesRegister(child, 0))
610
{
611
tempReg = cg()->allocateRegister(TR_FPR);
612
op = (childType == TR::Float) ? TR::InstOpCode::fmovs : TR::InstOpCode::fmovd;
613
generateTrg1Src1Instruction(cg(), op, callNode, tempReg, argRegister);
614
argRegister = tempReg;
615
}
616
if ((numFloatArgs == 0 && resType.isFloatingPoint()))
617
{
618
TR::Register *resultReg;
619
if (resType.getDataType() == TR::Float)
620
resultReg = cg()->allocateSinglePrecisionRegister();
621
else
622
resultReg = cg()->allocateRegister(TR_FPR);
623
624
dependencies->addPreCondition(argRegister, TR::RealRegister::v0);
625
dependencies->addPostCondition(resultReg, TR::RealRegister::v0);
626
}
627
else
628
{
629
TR::addDependency(dependencies, argRegister, properties.getFloatArgumentRegister(numFloatArgs), TR_FPR, cg());
630
}
631
}
632
else
633
{
634
// numFloatArgs >= properties.getNumFloatArgRegs()
635
int offsetInc;
636
if (childType == TR::Double)
637
{
638
op = TR::InstOpCode::vstrimmd;
639
offsetInc = 8;
640
#if defined(OSX)
641
argOffset = (argOffset + 7) & ~7; // adjust to 8-byte boundary
642
#endif
643
}
644
else
645
{
646
op = TR::InstOpCode::vstrimms;
647
#if defined(LINUX)
648
offsetInc = 8;
649
#elif defined(OSX)
650
offsetInc = 4;
651
#else
652
#error Unsupported platform
653
#endif
654
}
655
getOutgoingArgumentMemRef(argMemReg, argOffset, argRegister, op, pushToMemory[argIndex++]);
656
argOffset += offsetInc;
657
}
658
numFloatArgs++;
659
break;
660
} // end of switch
661
662
#if defined(OSX)
663
sigCursor = nextSignatureArgument(sigCursor);
664
#endif
665
} // end of for
666
667
for (int32_t i = TR::RealRegister::FirstGPR; i <= TR::RealRegister::LastGPR; ++i)
668
{
669
TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)i;
670
if (getProperties().getRegisterFlags(realReg) & ARM64_Reserved)
671
{
672
continue;
673
}
674
if (properties.getPreserved(realReg))
675
{
676
if (killNonVolatileGPRs)
677
{
678
// We release VM access around the native call,
679
// so even though the callee's linkage won't alter a
680
// given register, we still have a problem if a stack walk needs to
681
// inspect/modify that register because once we're in C-land, we have
682
// no idea where that regsiter's value is located. Therefore, we need
683
// to spill even the callee-saved registers around the call.
684
//
685
// In principle, we only need to do this for registers that contain
686
// references. However, at this location in the code, we don't yet
687
// know which real registers those would be. Tragically, this causes
688
// us to save/restore ALL preserved registers in any method containing
689
// a JNI call.
690
TR::addDependency(dependencies, NULL, realReg, TR_GPR, cg());
691
}
692
continue;
693
}
694
695
if (!dependencies->searchPreConditionRegister(realReg))
696
{
697
if (realReg == properties.getIntegerArgumentRegister(0) && callNode->getDataType() == TR::Address)
698
{
699
dependencies->addPreCondition(cg()->allocateRegister(), TR::RealRegister::x0);
700
dependencies->addPostCondition(cg()->allocateCollectedReferenceRegister(), TR::RealRegister::x0);
701
}
702
else
703
{
704
TR::addDependency(dependencies, NULL, realReg, TR_GPR, cg());
705
}
706
}
707
}
708
709
int32_t floatRegsUsed = std::min<int32_t>(numFloatArgs, properties.getNumFloatArgRegs());
710
for (i = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::v0 + floatRegsUsed); i <= TR::RealRegister::LastFPR; i++)
711
{
712
if (!properties.getPreserved((TR::RealRegister::RegNum)i))
713
{
714
// NULL dependency for non-preserved regs
715
TR::addDependency(dependencies, NULL, (TR::RealRegister::RegNum)i, TR_FPR, cg());
716
}
717
}
718
719
/* Spills all vector registers */
720
if (killsVectorRegisters())
721
{
722
TR::Register *tmpReg = cg()->allocateRegister();
723
dependencies->addPostCondition(tmpReg, TR::RealRegister::KillVectorRegs);
724
cg()->stopUsingRegister(tmpReg);
725
}
726
727
if (numMemArgs > 0)
728
{
729
TR::RealRegister *sp = cg()->machine()->getRealRegister(properties.getStackPointerRegister());
730
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::subimmx, callNode, argMemReg, sp, totalSize);
731
732
for (argIndex = 0; argIndex < numMemArgs; argIndex++)
733
{
734
TR::Register *aReg = pushToMemory[argIndex].argRegister;
735
generateMemSrc1Instruction(cg(), pushToMemory[argIndex].opCode, callNode, pushToMemory[argIndex].argMemory, aReg);
736
cg()->stopUsingRegister(aReg);
737
}
738
739
cg()->stopUsingRegister(argMemReg);
740
}
741
742
return totalSize;
743
}
744
745
TR::Register *J9::ARM64::JNILinkage::getReturnRegisterFromDeps(TR::Node *callNode, TR::RegisterDependencyConditions *deps)
746
{
747
const TR::ARM64LinkageProperties &pp = _systemLinkage->getProperties();
748
TR::Register *retReg;
749
750
switch(callNode->getOpCodeValue())
751
{
752
case TR::icall:
753
retReg = deps->searchPostConditionRegister(
754
pp.getIntegerReturnRegister());
755
break;
756
case TR::lcall:
757
case TR::acall:
758
retReg = deps->searchPostConditionRegister(
759
pp.getLongReturnRegister());
760
break;
761
case TR::fcall:
762
case TR::dcall:
763
retReg = deps->searchPostConditionRegister(
764
pp.getFloatReturnRegister());
765
break;
766
case TR::call:
767
retReg = NULL;
768
break;
769
default:
770
retReg = NULL;
771
TR_ASSERT_FATAL(false, "Unsupported direct call Opcode.");
772
}
773
return retReg;
774
}
775
776
TR::Register *J9::ARM64::JNILinkage::pushJNIReferenceArg(TR::Node *child)
777
{
778
TR::Register *pushRegister;
779
bool checkSplit = true;
780
781
if (child->getOpCodeValue() == TR::loadaddr)
782
{
783
TR::SymbolReference * symRef = child->getSymbolReference();
784
TR::StaticSymbol *sym = symRef->getSymbol()->getStaticSymbol();
785
if (sym)
786
{
787
if (sym->isAddressOfClassObject())
788
{
789
pushRegister = pushAddressArg(child);
790
}
791
else
792
{
793
TR::Register *addrReg = cg()->evaluate(child);
794
TR::MemoryReference *tmpMemRef = TR::MemoryReference::createWithDisplacement(cg(), addrReg, (int32_t)0);
795
TR::Register *whatReg = cg()->allocateCollectedReferenceRegister();
796
797
checkSplit = false;
798
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldrimmx, child, whatReg, tmpMemRef);
799
if (!cg()->canClobberNodesRegister(child))
800
{
801
// Since this is a static variable, it is non-collectable.
802
TR::Register *tempRegister = cg()->allocateRegister();
803
generateMovInstruction(cg(), child, tempRegister, addrReg);
804
pushRegister = tempRegister;
805
}
806
else
807
pushRegister = addrReg;
808
generateCompareImmInstruction(cg(), child, whatReg, 0, true);
809
generateCondTrg1Src2Instruction(cg(), TR::InstOpCode::cselx, child, pushRegister, pushRegister, whatReg, TR::CC_NE);
810
811
cg()->stopUsingRegister(whatReg);
812
cg()->decReferenceCount(child);
813
}
814
}
815
else // must be loadaddr of parm or local
816
{
817
if (child->pointsToNonNull())
818
{
819
pushRegister = pushAddressArg(child);
820
}
821
else if (child->pointsToNull())
822
{
823
checkSplit = false;
824
pushRegister = cg()->allocateRegister();
825
loadConstant64(cg(), child, 0, pushRegister);
826
cg()->decReferenceCount(child);
827
}
828
else
829
{
830
TR::Register *addrReg = cg()->evaluate(child);
831
TR::MemoryReference *tmpMemRef = TR::MemoryReference::createWithDisplacement(cg(), addrReg, (int32_t)0);
832
TR::Register *whatReg = cg()->allocateCollectedReferenceRegister();
833
834
checkSplit = false;
835
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldrimmx, child, whatReg, tmpMemRef);
836
if (!cg()->canClobberNodesRegister(child))
837
{
838
// Since this points at a parm or local location, it is non-collectable.
839
TR::Register *tempRegister = cg()->allocateRegister();
840
generateMovInstruction(cg(), child, tempRegister, addrReg);
841
pushRegister = tempRegister;
842
}
843
else
844
pushRegister = addrReg;
845
generateCompareImmInstruction(cg(), child, whatReg, 0, true);
846
generateCondTrg1Src2Instruction(cg(), TR::InstOpCode::cselx, child, pushRegister, pushRegister, whatReg, TR::CC_NE);
847
848
cg()->stopUsingRegister(whatReg);
849
cg()->decReferenceCount(child);
850
}
851
}
852
}
853
else
854
{
855
pushRegister = pushAddressArg(child);
856
}
857
858
if (checkSplit && !cg()->canClobberNodesRegister(child, 0))
859
{
860
TR::Register *tempReg = pushRegister->containsCollectedReference()?
861
cg()->allocateCollectedReferenceRegister():cg()->allocateRegister();
862
generateMovInstruction(cg(), child, tempReg, pushRegister);
863
pushRegister = tempReg;
864
}
865
return pushRegister;
866
}
867
868
void J9::ARM64::JNILinkage::adjustReturnValue(TR::Node *callNode, bool wrapRefs, TR::Register *returnRegister)
869
{
870
TR::SymbolReference *callSymRef = callNode->getSymbolReference();
871
TR::ResolvedMethodSymbol *callSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
872
TR_ResolvedMethod *resolvedMethod = callSymbol->getResolvedMethod();
873
874
// jni methods may not return a full register in some cases so need to get the declared
875
// type so that we sign and zero extend the narrower integer return types properly
876
TR::LabelSymbol *tempLabel = generateLabelSymbol(cg());
877
878
switch (resolvedMethod->returnType())
879
{
880
case TR::Address:
881
if (wrapRefs)
882
{
883
// unwrap when the returned object is non-null
884
generateCompareBranchInstruction(cg(),TR::InstOpCode::cbzx, callNode, returnRegister, tempLabel);
885
generateTrg1MemInstruction(cg(), TR::InstOpCode::ldrimmx, callNode, returnRegister, TR::MemoryReference::createWithDisplacement(cg(), returnRegister, 0));
886
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, tempLabel);
887
}
888
break;
889
case TR::Int8:
890
if (comp()->getSymRefTab()->isReturnTypeBool(callSymRef))
891
{
892
// For bool return type, must check whether value return by
893
// JNI is zero (false) or non-zero (true) to yield Java result
894
generateCompareImmInstruction(cg(), callNode, returnRegister, 0);
895
generateCSetInstruction(cg(), callNode, returnRegister, TR::CC_NE);
896
}
897
else if (resolvedMethod->returnTypeIsUnsigned())
898
{
899
// 7 in immr:imms means 0xff
900
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::andimmw, callNode, returnRegister, returnRegister, 7);
901
}
902
else
903
{
904
// sxtb (sign extend byte)
905
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::sbfmw, callNode, returnRegister, returnRegister, 7);
906
}
907
break;
908
case TR::Int16:
909
if (resolvedMethod->returnTypeIsUnsigned())
910
{
911
// 0xf in immr:imms means 0xffff
912
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::andimmw, callNode, returnRegister, returnRegister, 0xf);
913
}
914
else
915
{
916
// sxth (sign extend halfword)
917
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::sbfmw, callNode, returnRegister, returnRegister, 0xf);
918
}
919
break;
920
}
921
}
922
923
void J9::ARM64::JNILinkage::checkForJNIExceptions(TR::Node *callNode, TR::Register *vmThreadReg, TR::Register *scratchReg)
924
{
925
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
926
927
generateTrg1MemInstruction(cg(),TR::InstOpCode::ldrimmx, callNode, scratchReg, TR::MemoryReference::createWithDisplacement(cg(), vmThreadReg, fej9->thisThreadGetCurrentExceptionOffset()));
928
929
TR::SymbolReference *throwSymRef = cg()->getSymRefTab()->findOrCreateThrowCurrentExceptionSymbolRef(comp()->getJittedMethodSymbol());
930
TR::LabelSymbol *exceptionSnippetLabel = generateLabelSymbol(cg());
931
TR::Snippet *snippet = new (trHeapMemory()) TR::ARM64HelperCallSnippet(cg(), callNode, exceptionSnippetLabel, throwSymRef);
932
cg()->addSnippet(snippet);
933
TR::Instruction *gcPoint = generateCompareBranchInstruction(cg(),TR::InstOpCode::cbnzx, callNode, scratchReg, exceptionSnippetLabel);
934
// x0 may contain a reference returned by JNI method
935
snippet->gcMap().setGCRegisterMask(1);
936
}
937
938
TR::Instruction *J9::ARM64::JNILinkage::generateMethodDispatch(TR::Node *callNode, bool isJNIGCPoint,
939
TR::RegisterDependencyConditions *deps, uintptr_t targetAddress, TR::Register *scratchReg)
940
{
941
TR::ResolvedMethodSymbol *resolvedMethodSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
942
TR_ResolvedMethod *resolvedMethod = resolvedMethodSymbol->getResolvedMethod();
943
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
944
945
// load a 64-bit constant into a register with a fixed 4 instruction sequence
946
TR::Instruction *firstInstruction = generateTrg1ImmInstruction(cg(), TR::InstOpCode::movzx, callNode, scratchReg, targetAddress & 0x0000ffff);
947
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, scratchReg, ((targetAddress >> 16) & 0x0000ffff) | TR::MOV_LSL16);
948
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, scratchReg, ((targetAddress >> 32) & 0x0000ffff) | TR::MOV_LSL32);
949
generateTrg1ImmInstruction(cg(), TR::InstOpCode::movkx, callNode, scratchReg, (targetAddress >> 48) | TR::MOV_LSL48);
950
951
if (fej9->needClassAndMethodPointerRelocations())
952
{
953
TR_ExternalRelocationTargetKind reloType;
954
if (resolvedMethodSymbol->isSpecial())
955
reloType = TR_JNISpecialTargetAddress;
956
else if (resolvedMethodSymbol->isStatic())
957
reloType = TR_JNIStaticTargetAddress;
958
else if (resolvedMethodSymbol->isVirtual())
959
reloType = TR_JNIVirtualTargetAddress;
960
else
961
{
962
reloType = TR_NoRelocation;
963
TR_ASSERT(0, "JNI relocation not supported.");
964
}
965
966
TR_RelocationRecordInformation *info = new (comp()->trHeapMemory()) TR_RelocationRecordInformation();
967
info->data1 = 0;
968
info->data2 = reinterpret_cast<uintptr_t>(callNode->getSymbolReference());
969
info->data3 = static_cast<uintptr_t>(callNode->getInlinedSiteIndex());
970
971
cg()->addExternalRelocation(
972
new (trHeapMemory()) TR::BeforeBinaryEncodingExternalRelocation(
973
firstInstruction,
974
reinterpret_cast<uint8_t *>(info),
975
reloType,
976
cg()),
977
__FILE__,__LINE__, callNode);
978
}
979
980
// Add the first instruction of address materialization sequence to JNI call sites
981
cg()->getJNICallSites().push_front(new (trHeapMemory()) TR_Pair<TR_ResolvedMethod, TR::Instruction>(resolvedMethod, firstInstruction));
982
983
TR::Instruction *gcPoint = generateRegBranchInstruction(cg(), TR::InstOpCode::blr, callNode, scratchReg, deps);
984
if (isJNIGCPoint)
985
{
986
// preserved registers are killed
987
gcPoint->ARM64NeedsGCMap(cg(), 0);
988
}
989
return gcPoint;
990
}
991
992
TR::Register *J9::ARM64::JNILinkage::buildDirectDispatch(TR::Node *callNode)
993
{
994
TR::LabelSymbol *returnLabel = generateLabelSymbol(cg());
995
TR::SymbolReference *callSymRef = callNode->getSymbolReference();
996
TR::MethodSymbol *callSymbol = callSymRef->getSymbol()->castToMethodSymbol();
997
TR::ResolvedMethodSymbol *resolvedMethodSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();
998
TR_ResolvedMethod *resolvedMethod = resolvedMethodSymbol->getResolvedMethod();
999
uintptr_t targetAddress = reinterpret_cast<uintptr_t>(resolvedMethod->startAddressForJNIMethod(comp()));
1000
TR_J9VMBase *fej9 = reinterpret_cast<TR_J9VMBase *>(fe());
1001
1002
bool dropVMAccess = !fej9->jniRetainVMAccess(resolvedMethod);
1003
bool isJNIGCPoint = !fej9->jniNoGCPoint(resolvedMethod);
1004
bool killNonVolatileGPRs = isJNIGCPoint;
1005
bool checkExceptions = !fej9->jniNoExceptionsThrown(resolvedMethod);
1006
bool createJNIFrame = !fej9->jniNoNativeMethodFrame(resolvedMethod);
1007
bool tearDownJNIFrame = !fej9->jniNoSpecialTeardown(resolvedMethod);
1008
bool wrapRefs = !fej9->jniDoNotWrapObjects(resolvedMethod);
1009
bool passReceiver = !fej9->jniDoNotPassReceiver(resolvedMethod);
1010
bool passThread = !fej9->jniDoNotPassThread(resolvedMethod);
1011
1012
if (resolvedMethodSymbol->canDirectNativeCall())
1013
{
1014
dropVMAccess = false;
1015
killNonVolatileGPRs = false;
1016
isJNIGCPoint = false;
1017
checkExceptions = false;
1018
createJNIFrame = false;
1019
tearDownJNIFrame = false;
1020
}
1021
else if (resolvedMethodSymbol->isPureFunction())
1022
{
1023
dropVMAccess = false;
1024
isJNIGCPoint = false;
1025
checkExceptions = false;
1026
}
1027
1028
cg()->machine()->setLinkRegisterKilled(true);
1029
1030
const int maxRegisters = getProperties()._numAllocatableIntegerRegisters + getProperties()._numAllocatableFloatRegisters;
1031
// Extra post dependency for killing vector registers (see KillVectorRegs)
1032
const int extraPostReg = killsVectorRegisters() ? 1 : 0;
1033
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
1034
// Extra post dependency for xzr
1035
const int maxPostRegisters = maxRegisters + extraPostReg + 1;
1036
#else
1037
const int maxPostRegisters = maxRegisters + extraPostReg;
1038
#endif
1039
TR::RegisterDependencyConditions *deps = new (trHeapMemory()) TR::RegisterDependencyConditions(maxRegisters, maxPostRegisters, trMemory());
1040
1041
size_t spSize = buildJNIArgs(callNode, deps, passThread, passReceiver, killNonVolatileGPRs);
1042
TR::RealRegister *sp = machine()->getRealRegister(_systemLinkage->getProperties().getStackPointerRegister());
1043
1044
if (spSize > 0)
1045
{
1046
if (constantIsUnsignedImm12(spSize))
1047
{
1048
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::subimmx, callNode, sp, sp, spSize);
1049
}
1050
else
1051
{
1052
TR_ASSERT_FATAL(false, "Too many arguments.");
1053
}
1054
}
1055
1056
TR::Register *returnRegister = getReturnRegisterFromDeps(callNode, deps);
1057
1058
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
1059
TR::Register *zeroReg = cg()->allocateRegister();
1060
deps->addPostCondition(zeroReg, TR::RealRegister::xzr);
1061
#endif
1062
auto postLabelDeps = deps->clonePost(cg());
1063
TR::RealRegister *vmThreadReg = machine()->getRealRegister(getProperties().getMethodMetaDataRegister()); // x19
1064
TR::RealRegister *javaStackReg = machine()->getRealRegister(getProperties().getStackPointerRegister()); // x20
1065
TR::Register *x9Reg = deps->searchPreConditionRegister(TR::RealRegister::x9);
1066
TR::Register *x10Reg = deps->searchPreConditionRegister(TR::RealRegister::x10);
1067
TR::Register *x11Reg = deps->searchPreConditionRegister(TR::RealRegister::x11);
1068
TR::Register *x12Reg = deps->searchPreConditionRegister(TR::RealRegister::x12);
1069
1070
if (createJNIFrame)
1071
{
1072
buildJNICallOutFrame(callNode, (resolvedMethod == comp()->getCurrentMethod()), returnLabel, vmThreadReg, javaStackReg, x9Reg, x10Reg);
1073
}
1074
1075
if (passThread)
1076
{
1077
TR::RealRegister *vmThread = machine()->getRealRegister(getProperties().getMethodMetaDataRegister());
1078
TR::Register *x0Reg = deps->searchPreConditionRegister(TR::RealRegister::x0);
1079
generateMovInstruction(cg(), callNode, x0Reg, vmThread);
1080
}
1081
1082
if (dropVMAccess)
1083
{
1084
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
1085
releaseVMAccessAtomicFree(callNode, vmThreadReg, x9Reg, x10Reg);
1086
#else
1087
releaseVMAccess(callNode, vmThreadReg, x9Reg, x10Reg, x11Reg, x12Reg);
1088
#endif
1089
}
1090
1091
TR::Instruction *callInstr = generateMethodDispatch(callNode, isJNIGCPoint, deps, targetAddress, x9Reg);
1092
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, returnLabel, callInstr);
1093
1094
if (spSize > 0)
1095
{
1096
if (constantIsUnsignedImm12(spSize))
1097
{
1098
generateTrg1Src1ImmInstruction(cg(), TR::InstOpCode::addimmx, callNode, sp, sp, spSize);
1099
}
1100
else
1101
{
1102
TR_ASSERT_FATAL(false, "Too many arguments.");
1103
}
1104
}
1105
1106
if (dropVMAccess)
1107
{
1108
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
1109
acquireVMAccessAtomicFree(callNode, vmThreadReg, x9Reg, zeroReg);
1110
#else
1111
acquireVMAccess(callNode, vmThreadReg, x9Reg, x10Reg, x11Reg);
1112
#endif
1113
}
1114
1115
if (returnRegister != NULL)
1116
{
1117
adjustReturnValue(callNode, wrapRefs, returnRegister);
1118
}
1119
1120
if (createJNIFrame)
1121
{
1122
restoreJNICallOutFrame(callNode, tearDownJNIFrame, vmThreadReg, javaStackReg, x9Reg);
1123
}
1124
1125
if (checkExceptions)
1126
{
1127
checkForJNIExceptions(callNode, vmThreadReg, x9Reg);
1128
}
1129
1130
TR::LabelSymbol *depLabel = generateLabelSymbol(cg());
1131
generateLabelInstruction(cg(), TR::InstOpCode::label, callNode, depLabel, postLabelDeps);
1132
1133
callNode->setRegister(returnRegister);
1134
1135
#ifdef J9VM_INTERP_ATOMIC_FREE_JNI
1136
cg()->stopUsingRegister(zeroReg);
1137
#endif
1138
deps->stopUsingDepRegs(cg(), returnRegister);
1139
return returnRegister;
1140
}
1141
1142