From 7f373f914405df7efcc9b19397e60730b619fc4e Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 8 Apr 2022 16:28:30 -0700 Subject: [PATCH 1/3] Fix verifyReachableTruffleClasses --- .../hotspot/libgraal/LibGraalFeature.java | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java index c25db5bb8f4d..c7c235b5f797 100644 --- a/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal.hotspot.libgraal/src/com/oracle/svm/graal/hotspot/libgraal/LibGraalFeature.java @@ -42,10 +42,9 @@ import java.util.Collections; import java.util.Deque; import java.util.HashMap; -import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.function.BooleanSupplier; @@ -513,29 +512,41 @@ public void afterCompilation(AfterCompilationAccess access) { */ private static void verifyReachableTruffleClasses(AfterAnalysisAccess access) { AnalysisUniverse universe = ((FeatureImpl.AfterAnalysisAccessImpl) access).getUniverse(); - Set seen = new HashSet<>(); + Map seen = new LinkedHashMap<>(); for (AnalysisMethod analysisMethod : universe.getMethods()) { if (analysisMethod.isDirectRootMethod() && analysisMethod.isImplementationInvoked()) { - seen.add(analysisMethod); + seen.put(analysisMethod, "direct root"); } if (analysisMethod.isVirtualRootMethod()) { for (AnalysisMethod impl : analysisMethod.getImplementations()) { VMError.guarantee(impl.isImplementationInvoked()); - seen.add(analysisMethod); + seen.put(impl, "virtual root"); } } } - Deque todo = new ArrayDeque<>(seen); + Deque todo = new ArrayDeque<>(seen.keySet()); SortedSet disallowedTypes = new TreeSet<>(); while (!todo.isEmpty()) { AnalysisMethod m = todo.removeFirst(); String className = m.getDeclaringClass().toClassName(); if (!isAllowedType(className)) { - disallowedTypes.add(className); + StringBuilder msg = new StringBuilder(className); + Object reason = m; + while (true) { + msg.append("<-"); + if (reason instanceof ResolvedJavaMethod) { + msg.append(((ResolvedJavaMethod) reason).format("%H.%n(%p)")); + reason = seen.get(reason); + } else { + msg.append(reason); + break; + } + } + disallowedTypes.add(msg.toString()); } for (InvokeInfo invoke : m.getInvokes()) { for (AnalysisMethod callee : invoke.getCallees()) { - if (seen.add(callee)) { + if (seen.putIfAbsent(callee, m) == null) { todo.add(callee); } } From 8035867098313b2d8d9c759a9a61151404f35c96 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Mon, 11 Apr 2022 15:20:46 -0700 Subject: [PATCH 2/3] Introduce KnownOffsets --- .../aarch64/SubstrateAArch64Backend.java | 34 +++--- .../graal/amd64/SubstrateAMD64Backend.java | 33 +++--- .../svm/core/graal/llvm/NodeLLVMBuilder.java | 12 +- .../core/graal/llvm/SubstrateLLVMBackend.java | 2 +- .../svm/core/graal/meta/KnownOffsets.java | 105 ++++++++++++++++++ .../core/graal/meta/RuntimeConfiguration.java | 66 ----------- .../meta/SubstrateBasicLoweringProvider.java | 6 +- .../nodes/NullaryConstructorOffsetNode.java | 67 ----------- .../graal/snippets/NonSnippetLowerings.java | 7 +- .../svm/core/graal/snippets/TypeSnippets.java | 18 +-- .../oracle/svm/graal/hosted/GraalFeature.java | 3 - .../com/oracle/svm/hosted/FeatureImpl.java | 4 + .../svm/hosted/NativeImageGenerator.java | 4 +- .../SharedRuntimeConfigurationBuilder.java | 47 +------- .../svm/hosted/meta/KnownOffsetsFeature.java | 99 +++++++++++++++++ 15 files changed, 267 insertions(+), 240 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java delete mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NullaryConstructorOffsetNode.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java diff --git a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java index 29a8d662bf4c..33174096e629 100755 --- a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java @@ -130,7 +130,7 @@ import com.oracle.svm.core.graal.code.SubstrateLIRGenerator; import com.oracle.svm.core.graal.code.SubstrateNodeLIRBuilder; import com.oracle.svm.core.graal.lir.VerificationMarkerOp; -import com.oracle.svm.core.graal.meta.RuntimeConfiguration; +import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.graal.meta.SubstrateForeignCallLinkage; import com.oracle.svm.core.graal.meta.SubstrateRegisterConfig; import com.oracle.svm.core.graal.nodes.CGlobalDataLoadAddressNode; @@ -177,7 +177,6 @@ public SubstrateAArch64Backend(Providers providers) { public static class SubstrateAArch64DirectCallOp extends AArch64Call.DirectCallOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(SubstrateAArch64DirectCallOp.class); - private final RuntimeConfiguration runtimeConfiguration; private final int newThreadStatus; @Use({REG, OperandFlag.ILLEGAL}) private Value javaFrameAnchor; @@ -189,10 +188,9 @@ public static class SubstrateAArch64DirectCallOp extends AArch64Call.DirectCallO */ @Temp({REG}) private Value linkReg; - public SubstrateAArch64DirectCallOp(RuntimeConfiguration runtimeConfiguration, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state, + public SubstrateAArch64DirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state, Value javaFrameAnchor, int newThreadStatus, boolean destroysCallerSavedRegisters, Value exceptionTemp) { super(TYPE, callTarget, result, parameters, temps, state); - this.runtimeConfiguration = runtimeConfiguration; this.javaFrameAnchor = javaFrameAnchor; this.newThreadStatus = newThreadStatus; this.destroysCallerSavedRegisters = destroysCallerSavedRegisters; @@ -202,7 +200,7 @@ public SubstrateAArch64DirectCallOp(RuntimeConfiguration runtimeConfiguration, R @Override public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { - maybeTransitionToNative(crb, masm, runtimeConfiguration, javaFrameAnchor, state, newThreadStatus); + maybeTransitionToNative(crb, masm, javaFrameAnchor, state, newThreadStatus); AArch64Call.directCall(crb, masm, callTarget, null, state); } @@ -215,7 +213,6 @@ public boolean destroysCallerSavedRegisters() { @Opcode("CALL_INDIRECT") public static class SubstrateAArch64IndirectCallOp extends AArch64Call.IndirectCallOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(SubstrateAArch64IndirectCallOp.class); - private final RuntimeConfiguration runtimeConfiguration; private final int newThreadStatus; @Use({REG, OperandFlag.ILLEGAL}) private Value javaFrameAnchor; @@ -227,10 +224,9 @@ public static class SubstrateAArch64IndirectCallOp extends AArch64Call.IndirectC */ @Temp({REG}) private Value linkReg; - public SubstrateAArch64IndirectCallOp(RuntimeConfiguration runtimeConfiguration, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress, + public SubstrateAArch64IndirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress, LIRFrameState state, Value javaFrameAnchor, int newThreadStatus, boolean destroysCallerSavedRegisters, Value exceptionTemp) { super(TYPE, callTarget, result, parameters, temps, targetAddress, state); - this.runtimeConfiguration = runtimeConfiguration; this.javaFrameAnchor = javaFrameAnchor; this.newThreadStatus = newThreadStatus; this.destroysCallerSavedRegisters = destroysCallerSavedRegisters; @@ -240,7 +236,7 @@ public SubstrateAArch64IndirectCallOp(RuntimeConfiguration runtimeConfiguration, @Override public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { - maybeTransitionToNative(crb, masm, runtimeConfiguration, javaFrameAnchor, state, newThreadStatus); + maybeTransitionToNative(crb, masm, javaFrameAnchor, state, newThreadStatus); super.emitCode(crb, masm); } @@ -250,8 +246,7 @@ public boolean destroysCallerSavedRegisters() { } } - static void maybeTransitionToNative(CompilationResultBuilder crb, AArch64MacroAssembler masm, RuntimeConfiguration runtimeConfiguration, Value javaFrameAnchor, LIRFrameState state, - int newThreadStatus) { + static void maybeTransitionToNative(CompilationResultBuilder crb, AArch64MacroAssembler masm, Value javaFrameAnchor, LIRFrameState state, int newThreadStatus) { if (ValueUtil.isIllegal(javaFrameAnchor)) { /* Not a call that needs to set up a JavaFrameAnchor. */ assert newThreadStatus == StatusSupport.STATUS_ILLEGAL; @@ -269,17 +264,18 @@ static void maybeTransitionToNative(CompilationResultBuilder crb, AArch64MacroAs * metadata is registered for the end of the instruction just works. */ int startPos = masm.position(); + KnownOffsets knownOffsets = KnownOffsets.singleton(); try (ScratchRegister scratch = masm.getScratchRegister()) { Register tempRegister = scratch.getRegister(); // Save PC masm.adr(tempRegister, 4); // Read PC + 4 crb.recordIndirectCall(startPos, masm.position(), null, state); masm.str(64, tempRegister, - AArch64Address.createImmediateAddress(64, AArch64Address.AddressingMode.IMMEDIATE_UNSIGNED_SCALED, anchor, runtimeConfiguration.getJavaFrameAnchorLastIPOffset())); + AArch64Address.createImmediateAddress(64, AArch64Address.AddressingMode.IMMEDIATE_UNSIGNED_SCALED, anchor, knownOffsets.getJavaFrameAnchorLastIPOffset())); // Save SP masm.mov(64, tempRegister, sp); masm.str(64, tempRegister, - AArch64Address.createImmediateAddress(64, AArch64Address.AddressingMode.IMMEDIATE_UNSIGNED_SCALED, anchor, runtimeConfiguration.getJavaFrameAnchorLastSPOffset())); + AArch64Address.createImmediateAddress(64, AArch64Address.AddressingMode.IMMEDIATE_UNSIGNED_SCALED, anchor, knownOffsets.getJavaFrameAnchorLastSPOffset())); } if (SubstrateOptions.MultiThreaded.getValue()) { @@ -292,7 +288,7 @@ static void maybeTransitionToNative(CompilationResultBuilder crb, AArch64MacroAs Register statusValueRegister = scratch1.getRegister(); Register statusAddressRegister = scratch2.getRegister(); masm.mov(statusValueRegister, newThreadStatus); - masm.loadAlignedAddress(32, statusAddressRegister, ReservedRegisters.singleton().getThreadRegister(), runtimeConfiguration.getVMThreadStatusOffset()); + masm.loadAlignedAddress(32, statusAddressRegister, ReservedRegisters.singleton().getThreadRegister(), knownOffsets.getVMThreadStatusOffset()); masm.stlr(32, statusValueRegister, statusAddressRegister); } } @@ -394,7 +390,7 @@ protected Value emitIndirectForeignCallAddress(ForeignCallLinkage linkage) { Value codeOffsetInImage = emitConstant(wordKind, JavaConstant.forLong(targetMethod.getCodeOffsetInImage())); Value codeInfo = emitJavaConstant(SubstrateObjectConstant.forObject(CodeInfoTable.getImageCodeCache())); int size = wordKind.getPlatformKind().getSizeInBytes() * Byte.SIZE; - int codeStartFieldOffset = getRuntimeConfiguration().getImageCodeInfoCodeStartOffset(); + int codeStartFieldOffset = KnownOffsets.singleton().getImageCodeInfoCodeStartOffset(); Value codeStartField = AArch64AddressValue.makeAddress(wordKind, size, asAllocatable(codeInfo), codeStartFieldOffset); Value codeStart = getArithmetic().emitLoad(wordKind, codeStartField, null); return getArithmetic().emitAdd(codeStart, codeOffsetInImage, false); @@ -407,11 +403,11 @@ protected void emitForeignCallOp(ForeignCallLinkage linkage, Value targetAddress if (shouldEmitOnlyIndirectCalls()) { RegisterValue targetRegister = AArch64.lr.asValue(FrameAccess.getWordStamp().getLIRKind(getLIRKindTool())); emitMove(targetRegister, targetAddress); - append(new SubstrateAArch64IndirectCallOp(getRuntimeConfiguration(), targetMethod, result, arguments, temps, targetRegister, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, + append(new SubstrateAArch64IndirectCallOp(targetMethod, result, arguments, temps, targetRegister, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); } else { assert targetAddress == null; - append(new SubstrateAArch64DirectCallOp(getRuntimeConfiguration(), targetMethod, result, arguments, temps, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, + append(new SubstrateAArch64DirectCallOp(targetMethod, result, arguments, temps, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); } } @@ -579,7 +575,7 @@ private Value getExceptionTemp(CallTargetNode callTarget) { @Override protected void emitDirectCall(DirectCallTargetNode callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState callState) { ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - append(new SubstrateAArch64DirectCallOp(getRuntimeConfiguration(), targetMethod, result, parameters, temps, callState, setupJavaFrameAnchor(callTarget), + append(new SubstrateAArch64DirectCallOp(targetMethod, result, parameters, temps, callState, setupJavaFrameAnchor(callTarget), getNewThreadStatus(callTarget), getDestroysCallerSavedRegisters(targetMethod), getExceptionTemp(callTarget))); } @@ -590,7 +586,7 @@ protected void emitIndirectCall(IndirectCallTargetNode callTarget, Value result, AllocatableValue targetAddress = targetRegister.asValue(FrameAccess.getWordStamp().getLIRKind(getLIRGeneratorTool().getLIRKindTool())); gen.emitMove(targetAddress, operand(callTarget.computedAddress())); ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - append(new SubstrateAArch64IndirectCallOp(getRuntimeConfiguration(), targetMethod, result, parameters, temps, targetAddress, callState, setupJavaFrameAnchor(callTarget), + append(new SubstrateAArch64IndirectCallOp(targetMethod, result, parameters, temps, targetAddress, callState, setupJavaFrameAnchor(callTarget), getNewThreadStatus(callTarget), getDestroysCallerSavedRegisters(targetMethod), getExceptionTemp(callTarget))); } diff --git a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java index a76f4d491c68..514238c8c462 100644 --- a/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/SubstrateAMD64Backend.java @@ -136,7 +136,7 @@ import com.oracle.svm.core.graal.code.SubstrateLIRGenerator; import com.oracle.svm.core.graal.code.SubstrateNodeLIRBuilder; import com.oracle.svm.core.graal.lir.VerificationMarkerOp; -import com.oracle.svm.core.graal.meta.RuntimeConfiguration; +import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.graal.meta.SubstrateForeignCallLinkage; import com.oracle.svm.core.graal.meta.SubstrateRegisterConfig; import com.oracle.svm.core.graal.nodes.CGlobalDataLoadAddressNode; @@ -204,7 +204,6 @@ public static boolean runtimeToAOTIsAvxSseTransition(TargetDescription target) { public static class SubstrateAMD64DirectCallOp extends AMD64Call.DirectCallOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(SubstrateAMD64DirectCallOp.class); - private final RuntimeConfiguration runtimeConfiguration; private final int newThreadStatus; @Use({REG, OperandFlag.ILLEGAL}) private Value javaFrameAnchor; @Temp({REG, OperandFlag.ILLEGAL}) private Value javaFrameAnchorTemp; @@ -212,10 +211,9 @@ public static class SubstrateAMD64DirectCallOp extends AMD64Call.DirectCallOp { private final boolean destroysCallerSavedRegisters; @Temp({REG, OperandFlag.ILLEGAL}) private Value exceptionTemp; - public SubstrateAMD64DirectCallOp(RuntimeConfiguration runtimeConfiguration, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state, + public SubstrateAMD64DirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState state, Value javaFrameAnchor, Value javaFrameAnchorTemp, int newThreadStatus, boolean destroysCallerSavedRegisters, Value exceptionTemp) { super(TYPE, callTarget, result, parameters, temps, state); - this.runtimeConfiguration = runtimeConfiguration; this.newThreadStatus = newThreadStatus; this.javaFrameAnchor = javaFrameAnchor; this.javaFrameAnchorTemp = javaFrameAnchorTemp; @@ -227,7 +225,7 @@ public SubstrateAMD64DirectCallOp(RuntimeConfiguration runtimeConfiguration, Res @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { - maybeTransitionToNative(crb, masm, runtimeConfiguration, javaFrameAnchor, javaFrameAnchorTemp, state, newThreadStatus); + maybeTransitionToNative(crb, masm, javaFrameAnchor, javaFrameAnchorTemp, state, newThreadStatus); AMD64Call.directCall(crb, masm, callTarget, null, false, state); } @@ -241,7 +239,6 @@ public boolean destroysCallerSavedRegisters() { public static class SubstrateAMD64IndirectCallOp extends AMD64Call.IndirectCallOp { public static final LIRInstructionClass TYPE = LIRInstructionClass.create(SubstrateAMD64IndirectCallOp.class); - private final RuntimeConfiguration runtimeConfiguration; private final int newThreadStatus; @Use({REG, OperandFlag.ILLEGAL}) private Value javaFrameAnchor; @Temp({REG, OperandFlag.ILLEGAL}) private Value javaFrameAnchorTemp; @@ -249,10 +246,9 @@ public static class SubstrateAMD64IndirectCallOp extends AMD64Call.IndirectCallO private final boolean destroysCallerSavedRegisters; @Temp({REG, OperandFlag.ILLEGAL}) private Value exceptionTemp; - public SubstrateAMD64IndirectCallOp(RuntimeConfiguration runtimeConfiguration, ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress, + public SubstrateAMD64IndirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress, LIRFrameState state, Value javaFrameAnchor, Value javaFrameAnchorTemp, int newThreadStatus, boolean destroysCallerSavedRegisters, Value exceptionTemp) { super(TYPE, callTarget, result, parameters, temps, targetAddress, state); - this.runtimeConfiguration = runtimeConfiguration; this.newThreadStatus = newThreadStatus; this.javaFrameAnchor = javaFrameAnchor; this.javaFrameAnchorTemp = javaFrameAnchorTemp; @@ -264,7 +260,7 @@ public SubstrateAMD64IndirectCallOp(RuntimeConfiguration runtimeConfiguration, R @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { - maybeTransitionToNative(crb, masm, runtimeConfiguration, javaFrameAnchor, javaFrameAnchorTemp, state, newThreadStatus); + maybeTransitionToNative(crb, masm, javaFrameAnchor, javaFrameAnchorTemp, state, newThreadStatus); AMD64Call.indirectCall(crb, masm, asRegister(targetAddress), callTarget, state); } @@ -274,7 +270,7 @@ public boolean destroysCallerSavedRegisters() { } } - static void maybeTransitionToNative(CompilationResultBuilder crb, AMD64MacroAssembler masm, RuntimeConfiguration runtimeConfiguration, Value javaFrameAnchor, Value temp, LIRFrameState state, + static void maybeTransitionToNative(CompilationResultBuilder crb, AMD64MacroAssembler masm, Value javaFrameAnchor, Value temp, LIRFrameState state, int newThreadStatus) { if (ValueUtil.isIllegal(javaFrameAnchor)) { /* Not a call that needs to set up a JavaFrameAnchor. */ @@ -310,12 +306,13 @@ static void maybeTransitionToNative(CompilationResultBuilder crb, AMD64MacroAsse */ crb.recordIndirectCall(startPos, masm.position(), null, state); - masm.movq(new AMD64Address(anchor, runtimeConfiguration.getJavaFrameAnchorLastIPOffset()), lastJavaIP); - masm.movq(new AMD64Address(anchor, runtimeConfiguration.getJavaFrameAnchorLastSPOffset()), AMD64.rsp); + KnownOffsets knownOffsets = KnownOffsets.singleton(); + masm.movq(new AMD64Address(anchor, knownOffsets.getJavaFrameAnchorLastIPOffset()), lastJavaIP); + masm.movq(new AMD64Address(anchor, knownOffsets.getJavaFrameAnchorLastSPOffset()), AMD64.rsp); if (SubstrateOptions.MultiThreaded.getValue()) { /* Change the VMThread status from Java to Native. */ - masm.movl(new AMD64Address(ReservedRegisters.singleton().getThreadRegister(), runtimeConfiguration.getVMThreadStatusOffset()), newThreadStatus); + masm.movl(new AMD64Address(ReservedRegisters.singleton().getThreadRegister(), knownOffsets.getVMThreadStatusOffset()), newThreadStatus); } } @@ -466,7 +463,7 @@ protected Value emitIndirectForeignCallAddress(ForeignCallLinkage linkage) { Value codeOffsetInImage = emitConstant(getLIRKindTool().getWordKind(), JavaConstant.forLong(targetMethod.getCodeOffsetInImage())); Value codeInfo = emitJavaConstant(SubstrateObjectConstant.forObject(CodeInfoTable.getImageCodeCache())); - Value codeStartField = new AMD64AddressValue(getLIRKindTool().getWordKind(), asAllocatable(codeInfo), getRuntimeConfiguration().getImageCodeInfoCodeStartOffset()); + Value codeStartField = new AMD64AddressValue(getLIRKindTool().getWordKind(), asAllocatable(codeInfo), KnownOffsets.singleton().getImageCodeInfoCodeStartOffset()); Value codeStart = getArithmetic().emitLoad(getLIRKindTool().getWordKind(), codeStartField, null); return getArithmetic().emitAdd(codeStart, codeOffsetInImage, false); } @@ -480,11 +477,11 @@ protected void emitForeignCallOp(ForeignCallLinkage linkage, Value targetAddress if (shouldEmitOnlyIndirectCalls()) { AllocatableValue targetRegister = AMD64.rax.asValue(FrameAccess.getWordStamp().getLIRKind(getLIRKindTool())); emitMove(targetRegister, targetAddress); - append(new SubstrateAMD64IndirectCallOp(getRuntimeConfiguration(), targetMethod, result, arguments, temps, targetRegister, info, + append(new SubstrateAMD64IndirectCallOp(targetMethod, result, arguments, temps, targetRegister, info, Value.ILLEGAL, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); } else { assert targetAddress == null; - append(new SubstrateAMD64DirectCallOp(getRuntimeConfiguration(), targetMethod, result, arguments, temps, info, Value.ILLEGAL, + append(new SubstrateAMD64DirectCallOp(targetMethod, result, arguments, temps, info, Value.ILLEGAL, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, getDestroysCallerSavedRegisters(targetMethod), Value.ILLEGAL)); } } @@ -755,7 +752,7 @@ private Value getExceptionTemp(CallTargetNode callTarget) { protected void emitDirectCall(DirectCallTargetNode callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState callState) { ResolvedJavaMethod targetMethod = callTarget.targetMethod(); vzeroupperBeforeCall((SubstrateAMD64LIRGenerator) getLIRGeneratorTool(), parameters, callState, (SharedMethod) targetMethod); - append(new SubstrateAMD64DirectCallOp(getRuntimeConfiguration(), targetMethod, result, parameters, temps, callState, + append(new SubstrateAMD64DirectCallOp(targetMethod, result, parameters, temps, callState, setupJavaFrameAnchor(callTarget), setupJavaFrameAnchorTemp(callTarget), getNewThreadStatus(callTarget), getDestroysCallerSavedRegisters(targetMethod), getExceptionTemp(callTarget))); } @@ -772,7 +769,7 @@ protected void emitIndirectCall(IndirectCallTargetNode callTarget, Value result, gen.emitMove(targetAddress, operand(callTarget.computedAddress())); ResolvedJavaMethod targetMethod = callTarget.targetMethod(); vzeroupperBeforeCall((SubstrateAMD64LIRGenerator) getLIRGeneratorTool(), parameters, callState, (SharedMethod) targetMethod); - append(new SubstrateAMD64IndirectCallOp(getRuntimeConfiguration(), targetMethod, result, parameters, temps, targetAddress, callState, + append(new SubstrateAMD64IndirectCallOp(targetMethod, result, parameters, temps, targetAddress, callState, setupJavaFrameAnchor(callTarget), setupJavaFrameAnchorTemp(callTarget), getNewThreadStatus(callTarget), getDestroysCallerSavedRegisters(targetMethod), getExceptionTemp(callTarget))); } diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/NodeLLVMBuilder.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/NodeLLVMBuilder.java index 5550a5e05aa6..2e15e552ec51 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/NodeLLVMBuilder.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/NodeLLVMBuilder.java @@ -103,7 +103,7 @@ import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMPendingSpecialRegisterRead; import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMValueWrapper; import com.oracle.svm.core.graal.llvm.util.LLVMUtils.LLVMVariable; -import com.oracle.svm.core.graal.meta.RuntimeConfiguration; +import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.graal.nodes.CGlobalDataLoadAddressNode; import com.oracle.svm.core.graal.nodes.ForeignCallWithExceptionNode; import com.oracle.svm.core.nodes.SafepointCheckNode; @@ -130,7 +130,6 @@ public class NodeLLVMBuilder implements NodeLIRBuilderTool, SubstrateNodeLIRBuilder { private final LLVMGenerator gen; private final LLVMIRBuilder builder; - private final RuntimeConfiguration runtimeConfiguration; private final DebugInfoBuilder debugInfoBuilder; private Map valueMap = new HashMap<>(); @@ -138,10 +137,9 @@ public class NodeLLVMBuilder implements NodeLIRBuilderTool, SubstrateNodeLIRBuil private Map backwardsPhi = new HashMap<>(); private long nextCGlobalId = 0L; - protected NodeLLVMBuilder(StructuredGraph graph, LLVMGenerator gen, RuntimeConfiguration runtimeConfiguration) { + protected NodeLLVMBuilder(StructuredGraph graph, LLVMGenerator gen) { this.gen = gen; this.builder = gen.getBuilder(); - this.runtimeConfiguration = runtimeConfiguration; this.debugInfoBuilder = new SubstrateDebugInfoBuilder(graph, gen.getProviders().getMetaAccessExtensionProvider(), this); setCompilationResultMethod(gen.getCompilationResult(), graph); @@ -567,19 +565,19 @@ private LLVMValueRef emitCall(Invoke invoke, LoweredCallTargetNode callTarget, L LLVMValueRef anchor = llvmOperand(SubstrateBackend.getJavaFrameAnchor(callTarget)); anchor = builder.buildIntToPtr(anchor, builder.rawPointerType()); - LLVMValueRef lastSPAddr = builder.buildGEP(anchor, builder.constantInt(runtimeConfiguration.getJavaFrameAnchorLastSPOffset())); + LLVMValueRef lastSPAddr = builder.buildGEP(anchor, builder.constantInt(KnownOffsets.singleton().getJavaFrameAnchorLastSPOffset())); Register stackPointer = gen.getRegisterConfig().getFrameRegister(); builder.buildStore(builder.buildReadRegister(builder.register(stackPointer.name)), builder.buildBitcast(lastSPAddr, builder.pointerType(builder.wordType()))); if (SubstrateOptions.MultiThreaded.getValue()) { LLVMValueRef threadLocalArea = gen.getSpecialRegisterValue(SpecialRegister.ThreadPointer); - LLVMValueRef statusIndex = builder.constantInt(runtimeConfiguration.getVMThreadStatusOffset()); + LLVMValueRef statusIndex = builder.constantInt(KnownOffsets.singleton().getVMThreadStatusOffset()); LLVMValueRef statusAddress = builder.buildGEP(builder.buildIntToPtr(threadLocalArea, builder.rawPointerType()), statusIndex); LLVMValueRef newThreadStatus = builder.constantInt(SubstrateBackend.getNewThreadStatus(callTarget)); builder.buildVolatileStore(newThreadStatus, builder.buildBitcast(statusAddress, builder.pointerType(builder.intType())), Integer.BYTES); } - LLVMValueRef wrapper = gen.createJNIWrapper(callee, nativeABI, args.length, runtimeConfiguration.getJavaFrameAnchorLastIPOffset()); + LLVMValueRef wrapper = gen.createJNIWrapper(callee, nativeABI, args.length, KnownOffsets.singleton().getJavaFrameAnchorLastIPOffset()); LLVMValueRef[] newArgs = new LLVMValueRef[args.length + 2]; if (!nativeABI) { diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/SubstrateLLVMBackend.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/SubstrateLLVMBackend.java index f3bf25f98c31..ffc51dd73175 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/SubstrateLLVMBackend.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/SubstrateLLVMBackend.java @@ -139,7 +139,7 @@ private void emitLLVM(StructuredGraph graph, CompilationResult result) { } protected NodeLLVMBuilder newNodeLLVMBuilder(StructuredGraph graph, LLVMGenerator generator) { - return new NodeLLVMBuilder(graph, generator, getRuntimeConfiguration()); + return new NodeLLVMBuilder(graph, generator); } private static void generate(NodeLLVMBuilder nodeBuilder, StructuredGraph graph) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java new file mode 100644 index 000000000000..099c3524abe5 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/KnownOffsets.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.graal.meta; + +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.SubstrateOptions; + +public final class KnownOffsets { + private int vtableBaseOffset; + private int vtableEntrySize; + private int typeIDSlotsOffset; + private int componentHubOffset; + private int javaFrameAnchorLastSPOffset; + private int javaFrameAnchorLastIPOffset; + private int vmThreadStatusOffset; + private int imageCodeInfoCodeStartOffset; + + @Fold + public static KnownOffsets singleton() { + return ImageSingletons.lookup(KnownOffsets.class); + } + + @Platforms(Platform.HOSTED_ONLY.class) + public void setLazyState(int vtableBaseOffset, int vtableEntrySize, int typeIDSlotsOffset, int componentHubOffset, + int javaFrameAnchorLastSPOffset, int javaFrameAnchorLastIPOffset, int vmThreadStatusOffset, int imageCodeInfoCodeStartOffset) { + assert !isFullyInitialized(); + + this.vtableBaseOffset = vtableBaseOffset; + this.vtableEntrySize = vtableEntrySize; + this.typeIDSlotsOffset = typeIDSlotsOffset; + this.componentHubOffset = componentHubOffset; + this.javaFrameAnchorLastSPOffset = javaFrameAnchorLastSPOffset; + this.javaFrameAnchorLastIPOffset = javaFrameAnchorLastIPOffset; + this.vmThreadStatusOffset = vmThreadStatusOffset; + this.imageCodeInfoCodeStartOffset = imageCodeInfoCodeStartOffset; + + assert isFullyInitialized(); + } + + private boolean isFullyInitialized() { + return vtableEntrySize > 0; + } + + public int getVTableOffset(int vTableIndex) { + assert isFullyInitialized(); + return vtableBaseOffset + vTableIndex * vtableEntrySize; + } + + public int getTypeIDSlotsOffset() { + assert isFullyInitialized(); + return typeIDSlotsOffset; + } + + public int getComponentHubOffset() { + assert isFullyInitialized(); + return componentHubOffset; + } + + public int getJavaFrameAnchorLastSPOffset() { + assert isFullyInitialized(); + return javaFrameAnchorLastSPOffset; + } + + public int getJavaFrameAnchorLastIPOffset() { + assert isFullyInitialized(); + return javaFrameAnchorLastIPOffset; + } + + public int getVMThreadStatusOffset() { + assert isFullyInitialized(); + assert SubstrateOptions.MultiThreaded.getValue() && vmThreadStatusOffset != -1; + return vmThreadStatusOffset; + } + + public int getImageCodeInfoCodeStartOffset() { + assert isFullyInitialized(); + return imageCodeInfoCodeStartOffset; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/RuntimeConfiguration.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/RuntimeConfiguration.java index ea88d8db4c7e..7365183f40ce 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/RuntimeConfiguration.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/RuntimeConfiguration.java @@ -36,7 +36,6 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.graal.code.SubstrateBackend; import com.oracle.svm.core.graal.meta.SubstrateRegisterConfig.ConfigKind; import com.oracle.svm.core.meta.SharedMethod; @@ -54,15 +53,6 @@ public final class RuntimeConfiguration { private final Iterable debugHandlersFactories; private final WordTypes wordTypes; - private int vtableBaseOffset; - private int vtableEntrySize; - private int typeIDSlotsOffset; - private int componentHubOffset; - private int javaFrameAnchorLastSPOffset; - private int javaFrameAnchorLastIPOffset; - private int vmThreadStatusOffset; - private int imageCodeInfoCodeStartOffset; - @Platforms(Platform.HOSTED_ONLY.class) public RuntimeConfiguration(Providers providers, SnippetReflectionProvider snippetReflection, EnumMap backends, WordTypes wordTypes) { this.providers = providers; @@ -76,27 +66,6 @@ public RuntimeConfiguration(Providers providers, SnippetReflectionProvider snipp } } - @Platforms(Platform.HOSTED_ONLY.class) - public void setLazyState(int vtableBaseOffset, int vtableEntrySize, int typeIDSlotsOffset, int componentHubOffset, - int javaFrameAnchorLastSPOffset, int javaFrameAnchorLastIPOffset, int vmThreadStatusOffset, int imageCodeInfoCodeStartOffset) { - assert !isFullyInitialized(); - - this.vtableBaseOffset = vtableBaseOffset; - this.vtableEntrySize = vtableEntrySize; - this.typeIDSlotsOffset = typeIDSlotsOffset; - this.componentHubOffset = componentHubOffset; - this.javaFrameAnchorLastSPOffset = javaFrameAnchorLastSPOffset; - this.javaFrameAnchorLastIPOffset = javaFrameAnchorLastIPOffset; - this.vmThreadStatusOffset = vmThreadStatusOffset; - this.imageCodeInfoCodeStartOffset = imageCodeInfoCodeStartOffset; - - assert isFullyInitialized(); - } - - public boolean isFullyInitialized() { - return vtableEntrySize > 0; - } - public Iterable getDebugHandlersFactories() { return debugHandlersFactories; } @@ -121,41 +90,6 @@ public SubstrateBackend getBackendForNormalMethod() { return backends.get(ConfigKind.NORMAL); } - public int getVTableOffset(int vTableIndex) { - assert isFullyInitialized(); - return vtableBaseOffset + vTableIndex * vtableEntrySize; - } - - public int getTypeIDSlotsOffset() { - assert isFullyInitialized(); - return typeIDSlotsOffset; - } - - public int getComponentHubOffset() { - assert isFullyInitialized(); - return componentHubOffset; - } - - public int getJavaFrameAnchorLastSPOffset() { - assert isFullyInitialized(); - return javaFrameAnchorLastSPOffset; - } - - public int getJavaFrameAnchorLastIPOffset() { - assert isFullyInitialized(); - return javaFrameAnchorLastIPOffset; - } - - public int getVMThreadStatusOffset() { - assert SubstrateOptions.MultiThreaded.getValue() && vmThreadStatusOffset != -1; - return vmThreadStatusOffset; - } - - public int getImageCodeInfoCodeStartOffset() { - assert isFullyInitialized(); - return imageCodeInfoCodeStartOffset; - } - public SnippetReflectionProvider getSnippetReflection() { return snippetReflection; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java index c8fbdc2fb187..efbbd2006390 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/meta/SubstrateBasicLoweringProvider.java @@ -97,6 +97,7 @@ public abstract class SubstrateBasicLoweringProvider extends DefaultJavaLowering private final Map, NodeLoweringProvider> lowerings; private RuntimeConfiguration runtimeConfig; + private final KnownOffsets knownOffsets; private final AbstractObjectStamp hubStamp; @Platforms(Platform.HOSTED_ONLY.class) @@ -111,6 +112,7 @@ public SubstrateBasicLoweringProvider(MetaAccessProvider metaAccess, ForeignCall hubRefStamp = SubstrateNarrowOopStamp.compressed(hubRefStamp, ReferenceAccess.singleton().getCompressEncoding()); } hubStamp = hubRefStamp; + knownOffsets = KnownOffsets.singleton(); } @Override @@ -155,7 +157,7 @@ private void lowerLoadMethodNode(LoadMethodNode loadMethodNode) { } private ReadNode createReadVirtualMethod(StructuredGraph graph, ValueNode hub, SharedMethod method) { - int vtableEntryOffset = runtimeConfig.getVTableOffset(method.getVTableIndex()); + int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex()); assert vtableEntryOffset > 0; /* * Method pointer will always exist in the vtable due to the fact that all reachable methods @@ -189,7 +191,7 @@ private static ValueNode maybeUncompress(ValueNode node) { @Override protected ValueNode createReadArrayComponentHub(StructuredGraph graph, ValueNode arrayHub, boolean isKnownObjectArray, FixedNode anchor) { - ConstantNode componentHubOffset = ConstantNode.forIntegerKind(target.wordJavaKind, runtimeConfig.getComponentHubOffset(), graph); + ConstantNode componentHubOffset = ConstantNode.forIntegerKind(target.wordJavaKind, knownOffsets.getComponentHubOffset(), graph); AddressNode componentHubAddress = graph.unique(new OffsetAddressNode(arrayHub, componentHubOffset)); FloatingReadNode componentHubRef = graph.unique(new FloatingReadNode(componentHubAddress, NamedLocationIdentity.FINAL_LOCATION, null, hubStamp, null, BarrierType.NONE)); return maybeUncompress(componentHubRef); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NullaryConstructorOffsetNode.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NullaryConstructorOffsetNode.java deleted file mode 100644 index ccb449c8d310..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/NullaryConstructorOffsetNode.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.core.graal.nodes; - -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; - -import org.graalvm.compiler.graph.Node; -import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.nodes.spi.Canonicalizable; -import org.graalvm.compiler.nodes.spi.CanonicalizerTool; -import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.ConstantNode; -import org.graalvm.compiler.nodes.calc.FloatingNode; - -import com.oracle.svm.core.FrameAccess; -import com.oracle.svm.core.graal.meta.RuntimeConfiguration; - -/** - * The vtable offset is only available after static analysis. Parsing graphs that are put into the - * image happens during static analysis, so we need a separate node that is replaced with a constant - * once the vtable information is available. - */ -@NodeInfo(size = SIZE_IGNORED, cycles = CYCLES_IGNORED) -public final class NullaryConstructorOffsetNode extends FloatingNode implements Canonicalizable { - public static final NodeClass TYPE = NodeClass.create(NullaryConstructorOffsetNode.class); - - public static final int NULLARY_CONSTRUCTOR_OFFSET = 0; - - private final RuntimeConfiguration runtimeConfig; - - public NullaryConstructorOffsetNode(RuntimeConfiguration runtimeConfig) { - super(TYPE, FrameAccess.getWordStamp()); - this.runtimeConfig = runtimeConfig; - } - - @Override - public Node canonical(CanonicalizerTool tool) { - if (runtimeConfig.isFullyInitialized()) { - final int vtableEntryOffset = runtimeConfig.getVTableOffset(NULLARY_CONSTRUCTOR_OFFSET); - return ConstantNode.forIntegerKind(FrameAccess.getWordKind(), vtableEntryOffset); - } - return this; - } -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java index ebf6dba434e7..84f46e1c2173 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/NonSnippetLowerings.java @@ -84,6 +84,7 @@ import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.code.CodeInfoTable; import com.oracle.svm.core.graal.code.SubstrateBackend; +import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.graal.meta.RuntimeConfiguration; import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode; import com.oracle.svm.core.graal.nodes.ThrowBytecodeExceptionNode; @@ -108,6 +109,7 @@ public abstract class NonSnippetLowerings { NonSnippetLowerings.class, "reportVerifyTypesError", false, LocationIdentity.any()); private final RuntimeConfiguration runtimeConfig; + private final KnownOffsets knownOffsets; private final Predicate mustNotAllocatePredicate; final boolean verifyTypes = SubstrateOptions.VerifyTypes.getValue(); @@ -116,6 +118,7 @@ public abstract class NonSnippetLowerings { protected NonSnippetLowerings(RuntimeConfiguration runtimeConfig, Predicate mustNotAllocatePredicate, OptionValues options, Providers providers, Map, NodeLoweringProvider> lowerings) { this.runtimeConfig = runtimeConfig; + this.knownOffsets = KnownOffsets.singleton(); this.mustNotAllocatePredicate = mustNotAllocatePredicate; lowerings.put(BytecodeExceptionNode.class, new BytecodeExceptionLowering()); @@ -348,7 +351,7 @@ public void lower(FixedNode node, LoweringTool tool) { */ JavaConstant codeInfo = SubstrateObjectConstant.forObject(CodeInfoTable.getImageCodeCache()); ValueNode codeInfoConstant = ConstantNode.forConstant(codeInfo, tool.getMetaAccess(), graph); - ValueNode codeStartFieldOffset = ConstantNode.forIntegerKind(FrameAccess.getWordKind(), runtimeConfig.getImageCodeInfoCodeStartOffset(), graph); + ValueNode codeStartFieldOffset = ConstantNode.forIntegerKind(FrameAccess.getWordKind(), knownOffsets.getImageCodeInfoCodeStartOffset(), graph); AddressNode codeStartField = graph.unique(new OffsetAddressNode(codeInfoConstant, codeStartFieldOffset)); // Not constant because runtime code can be persisted and loaded in a // process where image code is located elsewhere: @@ -373,7 +376,7 @@ public void lower(FixedNode node, LoweringTool tool) { loweredCallTarget = createUnreachableCallTarget(tool, node, parameters, callTarget.returnStamp(), signature, method, callType, invokeKind); } else { - int vtableEntryOffset = runtimeConfig.getVTableOffset(method.getVTableIndex()); + int vtableEntryOffset = knownOffsets.getVTableOffset(method.getVTableIndex()); hub = graph.unique(new LoadHubNode(runtimeConfig.getProviders().getStampProvider(), graph.addOrUnique(PiNode.create(receiver, nullCheck)))); AddressNode address = graph.unique(new OffsetAddressNode(hub, ConstantNode.forIntegerKind(FrameAccess.getWordKind(), vtableEntryOffset, graph))); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/TypeSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/TypeSnippets.java index 757188b522c9..98ecf0036d92 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/TypeSnippets.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/TypeSnippets.java @@ -49,7 +49,7 @@ import org.graalvm.compiler.replacements.Snippets; import com.oracle.svm.core.annotate.DuplicatedInNativeCode; -import com.oracle.svm.core.graal.meta.RuntimeConfiguration; +import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.graal.word.DynamicHubAccess; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.SharedType; @@ -146,15 +146,15 @@ private static SubstrateIntrinsics.Any slotTypeCheck( } @SuppressWarnings("unused") - public static void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues options, Providers providers, Map, NodeLoweringProvider> lowerings) { - new TypeSnippets(options, runtimeConfig, providers, lowerings); + public static void registerLowerings(OptionValues options, Providers providers, Map, NodeLoweringProvider> lowerings) { + new TypeSnippets(options, providers, lowerings); } - final RuntimeConfiguration runtimeConfig; + final KnownOffsets knownOffsets; - private TypeSnippets(OptionValues options, RuntimeConfiguration runtimeConfig, Providers providers, Map, NodeLoweringProvider> lowerings) { + private TypeSnippets(OptionValues options, Providers providers, Map, NodeLoweringProvider> lowerings) { super(options, providers); - this.runtimeConfig = runtimeConfig; + this.knownOffsets = KnownOffsets.singleton(); lowerings.put(InstanceOfNode.class, new InstanceOfLowering(options, providers)); lowerings.put(InstanceOfDynamicNode.class, new InstanceOfDynamicLowering(options, providers)); @@ -205,7 +205,7 @@ protected SnippetTemplate.Arguments makeArguments(InstanceOfUsageReplacer replac args.add("start", hub.getTypeCheckStart()); args.add("range", hub.getTypeCheckRange()); args.add("slot", hub.getTypeCheckSlot()); - args.addConst("typeIDSlotOffset", runtimeConfig.getTypeIDSlotsOffset()); + args.addConst("typeIDSlotOffset", knownOffsets.getTypeIDSlotsOffset()); return args; } } @@ -247,7 +247,7 @@ protected SnippetTemplate.Arguments makeArguments(InstanceOfUsageReplacer replac args.add("trueValue", replacer.trueValue); args.add("falseValue", replacer.falseValue); args.addConst("allowsNull", node.allowsNull()); - args.addConst("typeIDSlotOffset", runtimeConfig.getTypeIDSlotsOffset()); + args.addConst("typeIDSlotOffset", knownOffsets.getTypeIDSlotsOffset()); return args; } } @@ -277,7 +277,7 @@ protected SnippetTemplate.Arguments makeArguments(InstanceOfUsageReplacer replac args.add("checkedHub", node.getOtherClass()); args.add("trueValue", replacer.trueValue); args.add("falseValue", replacer.falseValue); - args.addConst("typeIDSlotOffset", runtimeConfig.getTypeIDSlotsOffset()); + args.addConst("typeIDSlotOffset", knownOffsets.getTypeIDSlotsOffset()); return args; } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java index 509f98e9978e..08b25a68d9c1 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalFeature.java @@ -678,9 +678,6 @@ public void beforeCompilation(BeforeCompilationAccess c) { throw VMError.shouldNotReachHere("Number of methods for runtime compilation exceeds the allowed limit: " + methods.size() + " > " + maxMethods); } - HostedMetaAccess hMetaAccess = config.getMetaAccess(); - runtimeConfigBuilder.updateLazyState(hMetaAccess); - /* * Start fresh with a new GraphEncoder, since we are going to optimize all graphs now that * the static analysis results are available. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index 361345b2194b..dd1077bf0525 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -538,6 +538,10 @@ public static class CompilationAccessImpl extends FeatureAccessImpl implements F this.heap = heap; } + public NativeLibraries getNativeLibraries() { + return runtimeBuilder.getNativeLibraries(); + } + @Override public long objectFieldOffset(Field field) { return objectFieldOffset(getMetaAccess().lookupJavaField(field)); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 86b8210491ff..b7086e7a8d63 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -625,8 +625,6 @@ private void doRun(Map entryPoints, BeforeCompilationAccessImpl beforeCompilationConfig = new BeforeCompilationAccessImpl(featureHandler, loader, aUniverse, hUniverse, heap, debug, runtime); featureHandler.forEachFeature(feature -> feature.beforeCompilation(beforeCompilationConfig)); - runtime.updateLazyState(hMetaAccess); - NativeImageCodeCache codeCache; CompileQueue compileQueue; try (StopTimer t = TimerCollection.createTimerAndStart(TimerCollection.Registry.COMPILE_TOTAL)) { @@ -1247,7 +1245,7 @@ public static void registerReplacements(DebugContext debug, FeatureHandler featu Map, NodeLoweringProvider> lowerings = lowerer.getLowerings(); lowerer.setConfiguration(runtimeConfig, options, providers); - TypeSnippets.registerLowerings(runtimeConfig, options, providers, lowerings); + TypeSnippets.registerLowerings(options, providers, lowerings); ExceptionSnippets.registerLowerings(options, providers, lowerings); if (hosted) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SharedRuntimeConfigurationBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SharedRuntimeConfigurationBuilder.java index e7187aa085be..e6e7d4098f9c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SharedRuntimeConfigurationBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SharedRuntimeConfigurationBuilder.java @@ -44,7 +44,6 @@ import com.oracle.svm.core.FrameAccess; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.code.ImageCodeInfo; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.graal.GraalConfiguration; import com.oracle.svm.core.graal.code.SubstrateBackend; @@ -59,19 +58,9 @@ import com.oracle.svm.core.graal.meta.SubstrateStampProvider; import com.oracle.svm.core.graal.word.SubstrateWordTypes; import com.oracle.svm.core.heap.Heap; -import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.stack.JavaFrameAnchor; -import com.oracle.svm.core.thread.VMThreads; -import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.c.NativeLibraries; -import com.oracle.svm.hosted.c.info.AccessorInfo; -import com.oracle.svm.hosted.c.info.StructFieldInfo; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; -import com.oracle.svm.hosted.config.HybridLayout; -import com.oracle.svm.hosted.meta.HostedMetaAccess; -import com.oracle.svm.hosted.thread.VMThreadMTFeature; -import com.oracle.svm.util.ReflectionUtil; import jdk.vm.ci.code.CodeCacheProvider; import jdk.vm.ci.code.RegisterConfig; @@ -101,6 +90,10 @@ public SharedRuntimeConfigurationBuilder(OptionValues options, SVMHost hostVM, M this.originalLoopsDataProvider = originalLoopsDataProvider; } + public NativeLibraries getNativeLibraries() { + return nativeLibraries; + } + public SharedRuntimeConfigurationBuilder build() { EnumMap registerConfigs = new EnumMap<>(ConfigKind.class); for (ConfigKind config : ConfigKind.values()) { @@ -181,36 +174,4 @@ protected LoweringProvider createLoweringProvider(Providers p) { protected abstract Replacements createReplacements(Providers p, SnippetReflectionProvider snippetReflection); protected abstract CodeCacheProvider createCodeCacheProvider(RegisterConfig registerConfig); - - public void updateLazyState(HostedMetaAccess hMetaAccess) { - HybridLayout hubLayout = new HybridLayout<>(DynamicHub.class, ConfigurationValues.getObjectLayout(), hMetaAccess); - int vtableBaseOffset = hubLayout.getArrayBaseOffset(); - int vtableEntrySize = ConfigurationValues.getObjectLayout().sizeInBytes(hubLayout.getArrayElementStorageKind()); - int typeIDSlotsOffset = HybridLayout.getTypeIDSlotsFieldOffset(ConfigurationValues.getObjectLayout()); - - int componentHubOffset = hMetaAccess.lookupJavaField(ReflectionUtil.lookupField(DynamicHub.class, "componentType")).getLocation(); - - int javaFrameAnchorLastSPOffset = findStructOffset(JavaFrameAnchor.class, "getLastJavaSP"); - int javaFrameAnchorLastIPOffset = findStructOffset(JavaFrameAnchor.class, "getLastJavaIP"); - - int vmThreadStatusOffset = -1; - if (SubstrateOptions.MultiThreaded.getValue()) { - vmThreadStatusOffset = ImageSingletons.lookup(VMThreadMTFeature.class).offsetOf(VMThreads.StatusSupport.statusTL); - } - - int imageCodeInfoCodeStartOffset = hMetaAccess.lookupJavaField(ReflectionUtil.lookupField(ImageCodeInfo.class, "codeStart")).getLocation(); - - runtimeConfig.setLazyState(vtableBaseOffset, vtableEntrySize, typeIDSlotsOffset, componentHubOffset, - javaFrameAnchorLastSPOffset, javaFrameAnchorLastIPOffset, vmThreadStatusOffset, imageCodeInfoCodeStartOffset); - } - - private int findStructOffset(Class clazz, String accessorName) { - try { - AccessorInfo accessorInfo = (AccessorInfo) nativeLibraries.findElementInfo(metaAccess.lookupJavaMethod(clazz.getDeclaredMethod(accessorName))); - StructFieldInfo structFieldInfo = (StructFieldInfo) accessorInfo.getParent(); - return structFieldInfo.getOffsetInfo().getProperty(); - } catch (ReflectiveOperationException ex) { - throw VMError.shouldNotReachHere(ex); - } - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java new file mode 100644 index 000000000000..c79d3fcae7ab --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/KnownOffsetsFeature.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.meta; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.code.ImageCodeInfo; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.graal.meta.KnownOffsets; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.stack.JavaFrameAnchor; +import com.oracle.svm.core.thread.VMThreads; +import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl; +import com.oracle.svm.hosted.c.info.AccessorInfo; +import com.oracle.svm.hosted.c.info.StructFieldInfo; +import com.oracle.svm.hosted.config.HybridLayout; +import com.oracle.svm.hosted.thread.VMThreadMTFeature; +import com.oracle.svm.util.ReflectionUtil; + +@AutomaticFeature +final class KnownOffsetsFeature implements Feature { + @Override + public List> getRequiredFeatures() { + if (SubstrateOptions.MultiThreaded.getValue()) { + return Arrays.asList(VMThreadMTFeature.class); + } else { + return Collections.emptyList(); + } + } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(KnownOffsets.class, new KnownOffsets()); + } + + @Override + public void beforeCompilation(BeforeCompilationAccess a) { + BeforeCompilationAccessImpl access = (BeforeCompilationAccessImpl) a; + + HybridLayout hubLayout = new HybridLayout<>(DynamicHub.class, ConfigurationValues.getObjectLayout(), access.getMetaAccess()); + int vtableBaseOffset = hubLayout.getArrayBaseOffset(); + int vtableEntrySize = ConfigurationValues.getObjectLayout().sizeInBytes(hubLayout.getArrayElementStorageKind()); + int typeIDSlotsOffset = HybridLayout.getTypeIDSlotsFieldOffset(ConfigurationValues.getObjectLayout()); + + int componentHubOffset = findFieldOffset(access, DynamicHub.class, "componentType"); + + int javaFrameAnchorLastSPOffset = findStructOffset(access, JavaFrameAnchor.class, "getLastJavaSP"); + int javaFrameAnchorLastIPOffset = findStructOffset(access, JavaFrameAnchor.class, "getLastJavaIP"); + + int vmThreadStatusOffset = -1; + if (SubstrateOptions.MultiThreaded.getValue()) { + vmThreadStatusOffset = ImageSingletons.lookup(VMThreadMTFeature.class).offsetOf(VMThreads.StatusSupport.statusTL); + } + + int imageCodeInfoCodeStartOffset = findFieldOffset(access, ImageCodeInfo.class, "codeStart"); + + KnownOffsets.singleton().setLazyState(vtableBaseOffset, vtableEntrySize, typeIDSlotsOffset, componentHubOffset, + javaFrameAnchorLastSPOffset, javaFrameAnchorLastIPOffset, vmThreadStatusOffset, imageCodeInfoCodeStartOffset); + } + + private static int findFieldOffset(BeforeCompilationAccessImpl access, Class clazz, String fieldName) { + return access.getMetaAccess().lookupJavaField(ReflectionUtil.lookupField(clazz, fieldName)).getLocation(); + } + + private static int findStructOffset(BeforeCompilationAccessImpl access, Class clazz, String accessorName) { + AccessorInfo accessorInfo = (AccessorInfo) access.getNativeLibraries().findElementInfo(access.getMetaAccess().lookupJavaMethod(ReflectionUtil.lookupMethod(clazz, accessorName))); + StructFieldInfo structFieldInfo = (StructFieldInfo) accessorInfo.getParent(); + return structFieldInfo.getOffsetInfo().getProperty(); + } +} From 395e75be5428cc13c4af8fae8396863600417e45 Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Tue, 12 Apr 2022 14:04:18 -0700 Subject: [PATCH 3/3] Share reflection invocation stubs with same signature --- .../compiler/replacements/GraphKit.java | 3 +- .../graal/replacements/SubstrateGraphKit.java | 2 +- .../reflect/ReflectionAccessorHolder.java | 40 ++- .../svm/core/reflect/SubstrateAccessor.java | 71 ++++++ .../reflect/SubstrateConstructorAccessor.java | 26 +- .../core/reflect/SubstrateMethodAccessor.java | 125 +++++++-- .../svm/hosted/meta/UniverseBuilder.java | 42 +-- .../reflect/hosted/ReflectionDataBuilder.java | 6 +- .../ReflectionExpandSignatureMethod.java | 129 ++++++++++ .../svm/reflect/hosted/ReflectionFeature.java | 145 +++++++++-- .../reflect/hosted/ReflectionGraphKit.java | 20 +- .../hosted/ReflectiveInvokeMethod.java | 239 ------------------ 12 files changed, 502 insertions(+), 346 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java create mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionExpandSignatureMethod.java delete mode 100644 substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectiveInvokeMethod.java diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java index 616325481831..7ff3fde84812 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java @@ -50,6 +50,7 @@ import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.AbstractMergeNode; import org.graalvm.compiler.nodes.BeginNode; +import org.graalvm.compiler.nodes.CallTargetNode; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.EndNode; import org.graalvm.compiler.nodes.FixedNode; @@ -564,7 +565,7 @@ public InvokeWithExceptionNode startInvokeWithException(ResolvedJavaMethod metho return startInvokeWithException(callTarget, frameStateBuilder, invokeBci); } - public InvokeWithExceptionNode startInvokeWithException(MethodCallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci) { + public InvokeWithExceptionNode startInvokeWithException(CallTargetNode callTarget, FrameStateBuilder frameStateBuilder, int invokeBci) { ExceptionObjectNode exceptionObject = createExceptionObjectNode(frameStateBuilder, invokeBci); InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionObject, invokeBci)); AbstractBeginNode noExceptionEdge = graph.add(new BeginNode()); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java index 30b0aed060d7..3a10f6dc0af6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/replacements/SubstrateGraphKit.java @@ -173,7 +173,7 @@ public InvokeWithExceptionNode createJavaCallWithException(InvokeKind kind, Reso return startInvokeWithException(targetMethod, kind, frameState, bci(), arguments); } - public ValueNode createJavaCallWithExceptionAndUnwind(InvokeKind kind, ResolvedJavaMethod targetMethod, ValueNode... arguments) { + public InvokeWithExceptionNode createJavaCallWithExceptionAndUnwind(InvokeKind kind, ResolvedJavaMethod targetMethod, ValueNode... arguments) { return createInvokeWithExceptionAndUnwind(targetMethod, kind, frameState, bci(), arguments); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionAccessorHolder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionAccessorHolder.java index 27eb2aaccfda..f6a0ad2c4104 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionAccessorHolder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionAccessorHolder.java @@ -26,11 +26,16 @@ import java.lang.reflect.InvocationTargetException; +import org.graalvm.nativeimage.c.function.CFunctionPointer; + +import com.oracle.svm.core.annotate.InvokeJavaFunctionPointer; import com.oracle.svm.core.annotate.NeverInline; import com.oracle.svm.core.jdk.InternalVMMethod; -import com.oracle.svm.core.reflect.SubstrateMethodAccessor.MethodInvokeFunctionPointer; import com.oracle.svm.core.util.VMError; +import jdk.internal.reflect.ConstructorAccessor; +import jdk.internal.reflect.MethodAccessor; + /** * This class is used as the declaring class for reflection invocation methods. These methods have * manually created Graal IR, and are invoked via a function pointer call from @@ -44,22 +49,28 @@ public final class ReflectionAccessorHolder { * Signature prototype for invoking a method via a {@link SubstrateMethodAccessor}. Must match * the signature of {@link MethodInvokeFunctionPointer#invoke} */ - private static Object invokePrototype(boolean invokeSpecial, Object obj, Object[] args) { + private static Object invokePrototype(Object obj, Object[] args, CFunctionPointer invokedMethod) { throw VMError.shouldNotReachHere("Only used as a prototype for generated methods"); } + public interface MethodInvokeFunctionPointer extends CFunctionPointer { + /** Must match the signature of {@link ReflectionAccessorHolder#invokePrototype}. */ + @InvokeJavaFunctionPointer + Object invoke(Object obj, Object[] args, CFunctionPointer invokedMethod); + } + /* * Methods for throwing exceptions when a method or constructor is used in an illegal way. These * methods are invoked via function pointers, so must have the same signature as the prototype * above. */ - private static void methodHandleInvokeError(boolean invokeSpecial, Object obj, Object[] args) throws InvocationTargetException { + private static void methodHandleInvokeError(Object obj, Object[] args, CFunctionPointer invokedMethod) throws InvocationTargetException { /* The nested exceptions are required by the specification. */ throw new InvocationTargetException(new UnsupportedOperationException("MethodHandle.invoke() and MethodHandle.invokeExact() cannot be invoked through reflection")); } - private static Object newInstanceError(boolean invokeSpecial, Object obj, Object[] args) throws InstantiationException { + private static Object newInstanceError(Object obj, Object[] args, CFunctionPointer invokedMethod) throws InstantiationException { throw new InstantiationException("Only non-abstract instance classes can be instantiated using reflection"); } @@ -69,24 +80,29 @@ private static Object newInstanceError(boolean invokeSpecial, Object obj, Object */ @NeverInline("Exception slow path") - private static void throwIllegalArgumentExceptionWithReceiver(Object member, Object obj, Object[] args) { - throwIllegalArgumentException(member, true, obj, args); + private static void throwIllegalArgumentExceptionWithReceiver(Object obj, Object[] args) { + throw throwIllegalArgumentException(true, obj, args); } @NeverInline("Exception slow path") - private static void throwIllegalArgumentExceptionWithoutReceiver(Object member, Object[] args) { - throwIllegalArgumentException(member, false, null, args); + private static void throwIllegalArgumentExceptionWithoutReceiver(Object[] args) { + throw throwIllegalArgumentException(false, null, args); } /** * We do not know which check in the generated method caused the exception, so we cannot print - * detailed information about that. But printing the signature of the method and all the types - * of the actual arguments should make it obvious what the problem is. + * detailed information about that. We also do not know which member we are trying to invoke: + * {@link MethodAccessor#invoke} and {@link ConstructorAccessor#newInstance} do not propagate + * the invoked member into the accessor, and storing the member in + * {@link SubstrateMethodAccessor} and {@link SubstrateConstructorAccessor} would add a lot of + * objects to the image heap. + * + * But printing all the types of the actual arguments should help diagnose what the problem is. */ - private static void throwIllegalArgumentException(Object member, boolean withReceiver, Object obj, Object[] args) { + private static RuntimeException throwIllegalArgumentException(boolean withReceiver, Object obj, Object[] args) { String sep = System.lineSeparator(); StringBuilder msg = new StringBuilder(); - msg.append("Illegal arguments for invoking ").append(member); + msg.append("Illegal arguments for reflective invocation"); if (withReceiver) { msg.append(sep).append(" obj: ").append(obj == null ? "null" : obj.getClass().getTypeName()); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java new file mode 100644 index 000000000000..2bd339c97194 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateAccessor.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.reflect; + +import java.lang.reflect.Executable; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.c.function.CFunctionPointer; + +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.jdk.InternalVMMethod; + +@InternalVMMethod +public abstract class SubstrateAccessor { + /** + * We do not want unnecessary Method/Constructor objects in the image heap, so this field is + * only available at image build time to compute derived information (like the vtable offset), + * and to help debugging at image build time. + */ + @Platforms(Platform.HOSTED_ONLY.class) // + final Executable member; + + /** + * The first-level function that is invoked. It expands the boxed Object[] signature to the + * expanded real signature. + */ + final CFunctionPointer expandSignature; + /** + * The direct call target, if there is any. For non-virtual invokes, this is the second-level + * function that is invoked. For virtual invokes, this value is ignored and the actual target + * function is loaded from the vtable. + */ + final CFunctionPointer directTarget; + /** + * Class that needs to be initialized before invoking the target method. Null when no + * initialization is necessary, i.e., when invoking non-static methods or when the class is + * already initialized at image build time. + */ + final DynamicHub initializeBeforeInvoke; + + @Platforms(Platform.HOSTED_ONLY.class) + SubstrateAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, DynamicHub initializeBeforeInvoke) { + this.member = member; + this.expandSignature = expandSignature; + this.directTarget = directTarget; + this.initializeBeforeInvoke = initializeBeforeInvoke; + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java index 5171f2fecb0b..9c3c96273844 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java @@ -28,33 +28,25 @@ import org.graalvm.nativeimage.c.function.CFunctionPointer; +import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.InternalVMMethod; -import com.oracle.svm.core.reflect.SubstrateMethodAccessor.MethodInvokeFunctionPointer; -import com.oracle.svm.core.util.VMError; +import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer; import jdk.internal.reflect.ConstructorAccessor; @InternalVMMethod -public final class SubstrateConstructorAccessor implements ConstructorAccessor { +public final class SubstrateConstructorAccessor extends SubstrateAccessor implements ConstructorAccessor { - private final Executable member; - private final CFunctionPointer newInstanceFunctionPointer; - - public SubstrateConstructorAccessor(Executable member, CFunctionPointer newInstanceFunctionPointer) { - this.member = member; - this.newInstanceFunctionPointer = newInstanceFunctionPointer; + public SubstrateConstructorAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, DynamicHub initializeBeforeInvoke) { + super(member, expandSignature, directTarget, initializeBeforeInvoke); } @Override public Object newInstance(Object[] args) { - MethodInvokeFunctionPointer functionPointer = (MethodInvokeFunctionPointer) this.newInstanceFunctionPointer; - if (functionPointer.isNull()) { - throw newInstanceError(); + if (initializeBeforeInvoke != null) { + EnsureClassInitializedNode.ensureClassInitialized(DynamicHub.toClass(initializeBeforeInvoke)); } - return functionPointer.invoke(false, null, args); - } - - private RuntimeException newInstanceError() { - throw VMError.shouldNotReachHere("No SubstrateConstructorAccessor.newInstanceFunctionPointer for " + member); + return ((MethodInvokeFunctionPointer) expandSignature).invoke(null, args, directTarget); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java index 5d12c320ed01..04c0db33c536 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateMethodAccessor.java @@ -26,49 +26,128 @@ import java.lang.reflect.Executable; +import org.graalvm.compiler.nodes.NamedLocationIdentity; +import org.graalvm.compiler.word.BarrieredAccess; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunctionPointer; -import com.oracle.svm.core.annotate.InvokeJavaFunctionPointer; +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; +import com.oracle.svm.core.annotate.RecomputeFieldValue.ValueAvailability; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; +import com.oracle.svm.core.graal.meta.KnownOffsets; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.InternalVMMethod; +import com.oracle.svm.core.meta.SharedMethod; +import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer; import com.oracle.svm.core.util.VMError; import jdk.internal.reflect.MethodAccessor; +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaField; @InternalVMMethod -public final class SubstrateMethodAccessor implements MethodAccessor { +public final class SubstrateMethodAccessor extends SubstrateAccessor implements MethodAccessor { - interface MethodInvokeFunctionPointer extends CFunctionPointer { - /** Must match the signature of {@link ReflectionAccessorHolder#invokePrototype}. */ - @InvokeJavaFunctionPointer - Object invoke(boolean invokeSpecial, Object obj, Object[] args); - } + public static final int STATICALLY_BOUND = -1; + public static final int OFFSET_NOT_YET_COMPUTED = 0xdead0001; + + /** + * The expected receiver type, which is checked before invoking the {@link #expandSignature} + * method, or null for static methods. + */ + private final Class receiverType; + /** The actual value is computed after static analysis by {@link ComputeVTableOffset}. */ + int vtableOffset; - private final Executable member; - private final CFunctionPointer invokeFunctionPointer; + @Platforms(Platform.HOSTED_ONLY.class) + public SubstrateMethodAccessor(Executable member, Class receiverType, CFunctionPointer expandSignature, CFunctionPointer directTarget, int vtableOffset, DynamicHub initializeBeforeInvoke) { + super(member, expandSignature, directTarget, initializeBeforeInvoke); + this.receiverType = receiverType; + this.vtableOffset = vtableOffset; + } - public SubstrateMethodAccessor(Executable member, CFunctionPointer invokeFunctionPointer) { - this.member = member; - this.invokeFunctionPointer = invokeFunctionPointer; + private void preInvoke(Object obj) { + if (initializeBeforeInvoke != null) { + EnsureClassInitializedNode.ensureClassInitialized(DynamicHub.toClass(initializeBeforeInvoke)); + } + if (receiverType != null) { + if (obj == null) { + /* + * The specification explicitly demands a NullPointerException and not a + * IllegalArgumentException when the receiver of a non-static method is null + */ + throw new NullPointerException(); + } else if (!receiverType.isInstance(obj)) { + throw new IllegalArgumentException("Receiver type " + obj.getClass().getTypeName() + " is not an instance of the declaring class " + receiverType.getTypeName()); + } + } } @Override public Object invoke(Object obj, Object[] args) { - MethodInvokeFunctionPointer functionPointer = (MethodInvokeFunctionPointer) this.invokeFunctionPointer; - if (functionPointer.isNull()) { - throw invokeError(); + preInvoke(obj); + + /* + * In case we have both a vtableOffset and a directTarget, the vtable lookup wins. For such + * methods, the directTarget is only used when doing an invokeSpecial. + */ + CFunctionPointer target; + if (vtableOffset == OFFSET_NOT_YET_COMPUTED) { + throw VMError.shouldNotReachHere("Missed vtableOffset recomputation at image build time"); + } else if (vtableOffset != STATICALLY_BOUND) { + target = BarrieredAccess.readWord(obj.getClass(), vtableOffset, NamedLocationIdentity.FINAL_LOCATION); + } else { + target = directTarget; } - return functionPointer.invoke(false, obj, args); + return ((MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, target); } - private RuntimeException invokeError() { - throw VMError.shouldNotReachHere("No SubstrateMethodAccessor.invokeFunctionPointer for " + member); + public Object invokeSpecial(Object obj, Object[] args) { + preInvoke(obj); + + CFunctionPointer target = directTarget; + if (target.isNull()) { + throw new IllegalArgumentException("Cannot do invokespecial for an abstract method"); + } + return ((MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, target); } +} - public Object invokeSpecial(Object obj, Object[] args) { - MethodInvokeFunctionPointer functionPointer = (MethodInvokeFunctionPointer) this.invokeFunctionPointer; - if (functionPointer.isNull()) { - throw invokeError(); +/** + * The actual vtable offset is not available at the time the accessor is created, but only after + * static analysis when the layout of objects is known. Registering a field recomputation using an + * alias field is currently the only way to register a custom field value computer. + */ +@TargetClass(SubstrateMethodAccessor.class) +final class Target_com_oracle_svm_core_reflect_SubstrateMethodAccessor { + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ComputeVTableOffset.class) // + private int vtableOffset; +} + +final class ComputeVTableOffset implements CustomFieldValueComputer { + @Override + public ValueAvailability valueAvailability() { + return ValueAvailability.AfterAnalysis; + } + + @Override + public Class[] types() { + return new Class[]{int.class}; + } + + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + SubstrateMethodAccessor accessor = (SubstrateMethodAccessor) receiver; + if (accessor.vtableOffset == SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED) { + SharedMethod member = (SharedMethod) metaAccess.lookupJavaMethod(accessor.member); + return KnownOffsets.singleton().getVTableOffset(member.getVTableIndex()); + } else { + VMError.guarantee(accessor.vtableOffset == SubstrateMethodAccessor.STATICALLY_BOUND); + return accessor.vtableOffset; } - return functionPointer.invoke(true, obj, args); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index 1582764db14a..5c32a040e76b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -735,8 +735,11 @@ private void assignImplementations(HostedType type, Map 1) { + if (method.implementations.length > 1 || method.wrapped.isVirtualRootMethod()) { /* * Find a suitable vtable slot for the method, taking the existing vtable * assignments into account. @@ -802,27 +805,28 @@ private int findSlot(HostedMethod method, Map resultSlots = vtablesSlots.get(method.implementations[0]); - for (HostedMethod impl : method.implementations) { - Set implSlots = vtablesSlots.get(impl); - if (implSlots == null) { - resultSlots = null; - break; + if (method.implementations.length > 0) { + Set resultSlots = vtablesSlots.get(method.implementations[0]); + for (HostedMethod impl : method.implementations) { + Set implSlots = vtablesSlots.get(impl); + if (implSlots == null) { + resultSlots = null; + break; + } + resultSlots.retainAll(implSlots); } - resultSlots.retainAll(implSlots); - } - if (resultSlots != null && !resultSlots.isEmpty()) { - /* - * All implementations already have the same vtable slot assigned, so we can re-use - * that. If we have multiple candidates, we use the slot with the lowest number. - */ - int resultSlot = Integer.MAX_VALUE; - for (int slot : resultSlots) { - resultSlot = Math.min(resultSlot, slot); + if (resultSlots != null && !resultSlots.isEmpty()) { + /* + * All implementations already have the same vtable slot assigned, so we can re-use + * that. If we have multiple candidates, we use the slot with the lowest number. + */ + int resultSlot = Integer.MAX_VALUE; + for (int slot : resultSlots) { + resultSlot = Math.min(resultSlot, slot); + } + return resultSlot; } - return resultSlot; } - /* * No slot found, we need to compute a new one. Check the whole subtype hierarchy for * constraints using bitset union, and then use the lowest slot number that is available in diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java index acce1379bd37..0416d4056273 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java @@ -72,6 +72,7 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.RecordSupport; import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry; +import com.oracle.svm.core.reflect.SubstrateAccessor; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ConditionalConfigurationRegistry; @@ -290,7 +291,10 @@ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { * We must also generate the accessor for a method that was registered as queried * and then registered again as accessed */ - methodAccessors.putIfAbsent(method, ImageSingletons.lookup(ReflectionFeature.class).getOrCreateAccessor(method)); + SubstrateAccessor accessor = ImageSingletons.lookup(ReflectionFeature.class).getOrCreateAccessor(method); + if (methodAccessors.putIfAbsent(method, accessor) == null) { + access.rescanObject(accessor); + } } } for (AccessibleObject object : heapReflectionObjects) { diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionExpandSignatureMethod.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionExpandSignatureMethod.java new file mode 100644 index 000000000000..dfdaecf5438b --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionExpandSignatureMethod.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.reflect.hosted; + +import org.graalvm.compiler.core.common.type.StampFactory; +import org.graalvm.compiler.core.common.type.StampPair; +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.nodes.CallTargetNode; +import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; +import org.graalvm.compiler.nodes.IndirectCallTargetNode; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; + +import com.oracle.graal.pointsto.meta.HostedProviders; +import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; +import com.oracle.svm.core.reflect.ReflectionAccessorHolder.MethodInvokeFunctionPointer; +import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; +import com.oracle.svm.core.reflect.SubstrateMethodAccessor; +import com.oracle.svm.hosted.code.NonBytecodeStaticMethod; + +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; + +public class ReflectionExpandSignatureMethod extends NonBytecodeStaticMethod { + + private final boolean isStatic; + private final Class[] argTypes; + private final JavaKind returnKind; + + public ReflectionExpandSignatureMethod(String name, ResolvedJavaMethod prototype, boolean isStatic, Class[] argTypes, JavaKind returnKind) { + super(name, prototype.getDeclaringClass(), prototype.getSignature(), prototype.getConstantPool()); + this.isStatic = isStatic; + this.argTypes = argTypes; + this.returnKind = returnKind; + } + + /** + * Builds the graph that is invoked via {@link SubstrateMethodAccessor} and + * {@link SubstrateConstructorAccessor}. The signature is defined by + * {@link MethodInvokeFunctionPointer}. + * + * Based on the signature of the invoked method, the Object[] for the arguments is unpacked and + * the arguments are type checked. The target method is then invoked using a function pointer + * call. This allows sharing of these graphs for methods with the same signature. + * + * Note that there is no check at all performed for the receiver. It is assumed that the + * receiver was already null-checked and type-checked by the caller. This minimizes the size of + * the graph and allows sharing of these graphs for methods with the same signature but declared + * in different classes. + */ + @Override + public StructuredGraph buildGraph(DebugContext ctx, ResolvedJavaMethod m, HostedProviders providers, Purpose purpose) { + ReflectionGraphKit graphKit = new ReflectionGraphKit(ctx, providers, m); + + ValueNode receiver = graphKit.loadLocal(0, JavaKind.Object); + ValueNode argumentArray = graphKit.loadLocal(1, JavaKind.Object); + /* The invokedMethod is a Word type, so not yet a primitive in the signature. */ + ValueNode invokedMethod = graphKit.loadLocal(2, JavaKind.Object); + + /* Clear all locals, so that they are not alive and spilled at method calls. */ + graphKit.getFrameState().clearLocals(); + + int receiverOffset = isStatic ? 0 : 1; + ValueNode[] args = new ValueNode[argTypes.length + receiverOffset]; + ResolvedJavaType[] signature = new ResolvedJavaType[argTypes.length + receiverOffset]; + if (!isStatic) { + /* + * The receiver is already null-checked and type-checked at the call site in + * SubstrateMethodAccessor. + */ + signature[0] = providers.getMetaAccess().lookupJavaType(Object.class); + args[0] = receiver; + } + + graphKit.fillArgsArray(argumentArray, receiverOffset, args, argTypes); + for (int i = 0; i < argTypes.length; i++) { + signature[i + receiverOffset] = providers.getMetaAccess().lookupJavaType(argTypes[i]); + } + + CallTargetNode callTarget = graphKit.append(new IndirectCallTargetNode(invokedMethod, args, StampPair.createSingle(StampFactory.forKind(returnKind)), signature, null, + SubstrateCallingConventionKind.Java.toType(true), InvokeKind.Static)); + + InvokeWithExceptionNode invoke = graphKit.startInvokeWithException(callTarget, graphKit.getFrameState(), graphKit.bci()); + graphKit.exceptionPart(); + graphKit.branchToInvocationTargetException(graphKit.exceptionObject()); + graphKit.endInvokeWithException(); + + ValueNode returnValue; + if (returnKind == JavaKind.Void) { + returnValue = graphKit.createObject(null); + } else { + returnValue = invoke; + if (returnKind.isPrimitive()) { + ResolvedJavaType boxedRetType = graphKit.getMetaAccess().lookupJavaType(returnKind.toBoxedJavaClass()); + returnValue = graphKit.createBoxing(returnValue, returnKind, boxedRetType); + } + } + graphKit.createReturn(returnValue, JavaKind.Object); + + graphKit.emitIllegalArgumentException(isStatic ? null : receiver, argumentArray); + graphKit.emitInvocationTargetException(); + + return graphKit.finalizeGraph(); + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java index b633de3c3cfc..f8a374c615c6 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java @@ -25,10 +25,13 @@ package com.oracle.svm.reflect.hosted; import java.lang.invoke.MethodHandle; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; @@ -49,8 +52,10 @@ import com.oracle.svm.core.configure.ConfigurationFiles; import com.oracle.svm.core.configure.ReflectionConfigurationParser; import com.oracle.svm.core.graal.GraalFeature; +import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.reflect.ReflectionAccessorHolder; +import com.oracle.svm.core.reflect.SubstrateAccessor; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; import com.oracle.svm.core.reflect.SubstrateMethodAccessor; import com.oracle.svm.core.util.VMError; @@ -59,12 +64,14 @@ import com.oracle.svm.hosted.FeatureImpl.DuringSetupAccessImpl; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.analysis.Inflation; +import com.oracle.svm.hosted.code.FactoryMethodSupport; import com.oracle.svm.hosted.config.ConfigurationParserUtils; import com.oracle.svm.hosted.snippets.ReflectionPlugins; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; @AutomaticFeature @@ -77,16 +84,20 @@ public class ReflectionFeature implements GraalFeature { private AnalysisUniverse aUniverse; private int loadedConfigurations; - final Map accessors = new ConcurrentHashMap<>(); + final Map accessors = new ConcurrentHashMap<>(); + private final Map expandSignatureMethods = new ConcurrentHashMap<>(); - private static final Method invokePrototype = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "invokePrototype", boolean.class, Object.class, Object[].class); - private static final Method methodHandleInvokeErrorMethod = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "methodHandleInvokeError", boolean.class, Object.class, Object[].class); - private static final Method newInstanceErrorMethod = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "newInstanceError", boolean.class, Object.class, Object[].class); + private static final Method invokePrototype = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "invokePrototype", + Object.class, Object[].class, CFunctionPointer.class); + private static final Method methodHandleInvokeErrorMethod = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "methodHandleInvokeError", + Object.class, Object[].class, CFunctionPointer.class); + private static final Method newInstanceErrorMethod = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "newInstanceError", + Object.class, Object[].class, CFunctionPointer.class); FeatureImpl.BeforeAnalysisAccessImpl analysisAccess; - Object getOrCreateAccessor(Executable member) { - Object existing = accessors.get(member); + SubstrateAccessor getOrCreateAccessor(Executable member) { + SubstrateAccessor existing = accessors.get(member); if (existing != null) { return existing; } @@ -94,7 +105,7 @@ Object getOrCreateAccessor(Executable member) { if (analysisAccess == null) { throw VMError.shouldNotReachHere("New Method or Constructor found as reachable after static analysis: " + member); } - return accessors.computeIfAbsent(member, m -> createAccessor(m)); + return accessors.computeIfAbsent(member, this::createAccessor); } /** @@ -103,29 +114,54 @@ Object getOrCreateAccessor(Executable member) { * instances use function pointer calls to invocation stubs. The invocation stubs unpack the * Object[] array arguments and invoke the actual method. * - * The stubs are methods with manually created Graal IR: {@link ReflectiveInvokeMethod}. Since - * they are only invoked via function pointers and never at a normal call site, they need to be - * registered for compilation manually. From the point of view of the static analysis, they are - * root methods. + * The stubs are methods with manually created Graal IR: + * {@link ReflectionExpandSignatureMethod}. Since they are only invoked via function pointers + * and never at a normal call site, they need to be registered for compilation manually. From + * the point of view of the static analysis, they are root methods. + * + * The stubs then perform another function pointer call to the actual target method. This is + * either a direct target stored in the accessor, or a virtual target loaded from the vtable. * * {@link ConcurrentHashMap#computeIfAbsent} guarantees that this method is called only once per * member, so no further synchronization is necessary. */ - private Object createAccessor(Executable member) { - String name = SubstrateUtil.uniqueShortName(member); - ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(invokePrototype).getWrapped(); + private SubstrateAccessor createAccessor(Executable member) { + MethodPointer expandSignature; + MethodPointer directTarget = null; + DynamicHub initializeBeforeInvoke = null; if (member instanceof Method) { - ResolvedJavaMethod invokeMethod; + int vtableOffset = SubstrateMethodAccessor.STATICALLY_BOUND; + Class receiverType = null; + if (member.getDeclaringClass() == MethodHandle.class && (member.getName().equals("invoke") || member.getName().equals("invokeExact"))) { /* Method handles must not be invoked via reflection. */ - invokeMethod = analysisAccess.getMetaAccess().lookupJavaMethod(methodHandleInvokeErrorMethod); + expandSignature = register(analysisAccess.getMetaAccess().lookupJavaMethod(methodHandleInvokeErrorMethod)); } else { - invokeMethod = new ReflectiveInvokeMethod(name, prototype, member); + expandSignature = createExpandSignatureMethod(member); + AnalysisMethod targetMethod = analysisAccess.getMetaAccess().lookupJavaMethod(member); + /* + * The SubstrateMethodAccessor is also used for the implementation of MethodHandle + * that are created to do an invokespecial. So non-abstract instance methods have + * both a directTarget and a vtableOffset. + */ + if (!targetMethod.isAbstract()) { + directTarget = register(targetMethod); + } + if (!targetMethod.canBeStaticallyBound()) { + vtableOffset = SubstrateMethodAccessor.OFFSET_NOT_YET_COMPUTED; + analysisAccess.registerAsRoot(targetMethod, false); + } + VMError.guarantee(directTarget != null || vtableOffset != SubstrateMethodAccessor.STATICALLY_BOUND, "Must have either a directTarget or a vtableOffset"); + if (!targetMethod.isStatic()) { + receiverType = member.getDeclaringClass(); + } + if (targetMethod.isStatic() && !targetMethod.getDeclaringClass().isInitialized()) { + initializeBeforeInvoke = analysisAccess.getHostVM().dynamicHub(targetMethod.getDeclaringClass()); + } } - return new SubstrateMethodAccessor(member, register(invokeMethod)); + return new SubstrateMethodAccessor(member, receiverType, expandSignature, directTarget, vtableOffset, initializeBeforeInvoke); } else { - ResolvedJavaMethod newInstanceMethod; Class holder = member.getDeclaringClass(); if (Modifier.isAbstract(holder.getModifiers()) || holder.isInterface() || holder.isPrimitive() || holder.isArray()) { /* @@ -134,15 +170,28 @@ private Object createAccessor(Executable member) { * an interface, array, or primitive type, but we are defensive and throw the * exception in that case too. */ - newInstanceMethod = analysisAccess.getMetaAccess().lookupJavaMethod(newInstanceErrorMethod); + expandSignature = register(analysisAccess.getMetaAccess().lookupJavaMethod(newInstanceErrorMethod)); } else { - newInstanceMethod = new ReflectiveInvokeMethod(name, prototype, member); + expandSignature = createExpandSignatureMethod(member); + AnalysisMethod constructor = analysisAccess.getMetaAccess().lookupJavaMethod(member); + AnalysisMethod factoryMethod = (AnalysisMethod) FactoryMethodSupport.singleton().lookup(analysisAccess.getMetaAccess(), constructor, false); + directTarget = register(factoryMethod); + if (!constructor.getDeclaringClass().isInitialized()) { + initializeBeforeInvoke = analysisAccess.getHostVM().dynamicHub(constructor.getDeclaringClass()); + } } - return new SubstrateConstructorAccessor(member, register(newInstanceMethod)); + return new SubstrateConstructorAccessor(member, expandSignature, directTarget, initializeBeforeInvoke); } } - private CFunctionPointer register(ResolvedJavaMethod method) { + private MethodPointer createExpandSignatureMethod(Executable member) { + return expandSignatureMethods.computeIfAbsent(new SignatureKey(member), signatureKey -> { + ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(invokePrototype).getWrapped(); + return register(new ReflectionExpandSignatureMethod(signatureKey.uniqueShortName(), prototype, signatureKey.isStatic, signatureKey.argTypes, signatureKey.returnKind)); + }); + } + + private MethodPointer register(ResolvedJavaMethod method) { AnalysisMethod aMethod = method instanceof AnalysisMethod ? (AnalysisMethod) method : analysisAccess.getUniverse().lookup(method); analysisAccess.registerAsRoot(aMethod, true); return new MethodPointer(aMethod); @@ -208,3 +257,53 @@ public void registerInvocationPlugins(Providers providers, SnippetReflectionProv plugins.getClassInitializationPlugin(), plugins.getInvocationPlugins(), aUniverse, reason); } } + +final class SignatureKey { + final boolean isStatic; + final Class[] argTypes; + final JavaKind returnKind; + + SignatureKey(Executable member) { + isStatic = member instanceof Constructor || Modifier.isStatic(member.getModifiers()); + argTypes = member.getParameterTypes(); + if (member instanceof Method) { + returnKind = JavaKind.fromJavaClass(((Method) member).getReturnType()); + } else { + /* Constructor = factory method that returns the newly allocated object. */ + returnKind = JavaKind.Object; + } + } + + @Override + public boolean equals(Object obj) { + if (obj.getClass() != SignatureKey.class) { + return false; + } + SignatureKey other = (SignatureKey) obj; + return isStatic == other.isStatic && + Arrays.equals(argTypes, other.argTypes) && + Objects.equals(returnKind, other.returnKind); + } + + @Override + public int hashCode() { + return Objects.hash(isStatic, Arrays.hashCode(argTypes), returnKind); + } + + @Override + public String toString() { + StringBuilder fullName = new StringBuilder(); + fullName.append(isStatic); + fullName.append("("); + for (Class c : argTypes) { + fullName.append(c.getName()).append(","); + } + fullName.append(')'); + fullName.append(returnKind); + return fullName.toString(); + } + + String uniqueShortName() { + return SubstrateUtil.digest(toString()); + } +} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionGraphKit.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionGraphKit.java index 004f05fa49d4..2dc7e51ba6dd 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionGraphKit.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionGraphKit.java @@ -25,7 +25,6 @@ package com.oracle.svm.reflect.hosted; import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; @@ -43,6 +42,8 @@ import org.graalvm.compiler.nodes.ConstantNode; import org.graalvm.compiler.nodes.EndNode; import org.graalvm.compiler.nodes.FixedWithNextNode; +import org.graalvm.compiler.nodes.Invoke; +import org.graalvm.compiler.nodes.InvokeWithExceptionNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.MergeNode; import org.graalvm.compiler.nodes.NodeView; @@ -63,7 +64,6 @@ import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; import com.oracle.graal.pointsto.meta.HostedProviders; import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode; -import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.reflect.ReflectionAccessorHolder; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.code.FactoryMethodSupport; @@ -131,22 +131,21 @@ public void branchToInvocationTargetException(ExceptionObjectNode exceptionObjec /** * To reduce machine code size, we want only one call site for each exception class that can be * thrown. We also do not want arguments that require phi functions. So we cannot have an error - * message that, e.g., prints which exact cast failed. But we have the member that the stub is - * for, and the provided argument arrays, which allows a good enough error message. + * message that, e.g., prints which exact cast failed. */ - public void emitIllegalArgumentException(Executable member, ValueNode obj, ValueNode args) { + public void emitIllegalArgumentException(ValueNode obj, ValueNode args) { continueWithMerge(illegalArgumentExceptionPaths); - ValueNode memberNode = createConstant(SubstrateObjectConstant.forObject(member), JavaKind.Object); ResolvedJavaMethod targetMethod; ValueNode[] arguments; if (obj == null) { targetMethod = findMethod(ReflectionAccessorHolder.class, "throwIllegalArgumentExceptionWithoutReceiver", true); - arguments = new ValueNode[]{memberNode, args}; + arguments = new ValueNode[]{args}; } else { targetMethod = findMethod(ReflectionAccessorHolder.class, "throwIllegalArgumentExceptionWithReceiver", true); - arguments = new ValueNode[]{memberNode, obj, args}; + arguments = new ValueNode[]{obj, args}; } - createJavaCallWithExceptionAndUnwind(CallTargetNode.InvokeKind.Static, targetMethod, arguments); + InvokeWithExceptionNode invoke = createJavaCallWithExceptionAndUnwind(CallTargetNode.InvokeKind.Static, targetMethod, arguments); + invoke.setInlineControl(Invoke.InlineControl.Never); append(new LoweredDeadEndNode()); } @@ -157,7 +156,8 @@ public void emitInvocationTargetException() { ValueNode exception = createPhi(invocationTargetExceptionPaths, merge); ResolvedJavaMethod throwInvocationTargetException = FactoryMethodSupport.singleton().lookup((UniverseMetaAccess) getMetaAccess(), getMetaAccess().lookupJavaMethod(invocationTargetExceptionConstructor), true); - createJavaCallWithExceptionAndUnwind(CallTargetNode.InvokeKind.Static, throwInvocationTargetException, exception); + InvokeWithExceptionNode invoke = createJavaCallWithExceptionAndUnwind(CallTargetNode.InvokeKind.Static, throwInvocationTargetException, exception); + invoke.setInlineControl(Invoke.InlineControl.Never); append(new LoweredDeadEndNode()); } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectiveInvokeMethod.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectiveInvokeMethod.java deleted file mode 100644 index e36214c21ede..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectiveInvokeMethod.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.reflect.hosted; - -import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0; -import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0; - -import java.lang.reflect.Executable; -import java.util.ArrayList; -import java.util.List; - -import org.graalvm.compiler.bytecode.Bytecode; -import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.graph.Node; -import org.graalvm.compiler.graph.NodeClass; -import org.graalvm.compiler.nodeinfo.NodeInfo; -import org.graalvm.compiler.nodes.AbstractMergeNode; -import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; -import org.graalvm.compiler.nodes.ConstantNode; -import org.graalvm.compiler.nodes.InvokeWithExceptionNode; -import org.graalvm.compiler.nodes.LogicConstantNode; -import org.graalvm.compiler.nodes.LogicNode; -import org.graalvm.compiler.nodes.NodeView; -import org.graalvm.compiler.nodes.StructuredGraph; -import org.graalvm.compiler.nodes.ValueNode; -import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; -import org.graalvm.compiler.nodes.extended.BranchProbabilityNode; -import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; -import org.graalvm.compiler.nodes.spi.Canonicalizable; -import org.graalvm.compiler.nodes.spi.CanonicalizerTool; -import org.graalvm.compiler.phases.common.inlining.InliningUtil; - -import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess; -import com.oracle.graal.pointsto.meta.HostedProviders; -import com.oracle.graal.pointsto.phases.SubstrateIntrinsicGraphBuilder; -import com.oracle.svm.core.meta.SharedMethod; -import com.oracle.svm.core.reflect.SubstrateMethodAccessor; -import com.oracle.svm.hosted.code.FactoryMethodSupport; -import com.oracle.svm.hosted.code.NonBytecodeStaticMethod; - -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.ResolvedJavaType; - -public class ReflectiveInvokeMethod extends NonBytecodeStaticMethod { - - private final Executable method; - - public ReflectiveInvokeMethod(String name, ResolvedJavaMethod prototype, Executable method) { - super(name, prototype.getDeclaringClass(), prototype.getSignature(), prototype.getConstantPool()); - this.method = method; - } - - /** - * Builds the graph that is invoked via {@link SubstrateMethodAccessor}. To save code size, both - * the {@link SubstrateMethodAccessor#invoke "regular"} and the - * {@link SubstrateMethodAccessor#invokeSpecial} invocations are done via the same generated - * graph. The first parameter decides which invocation is done. - */ - @Override - public StructuredGraph buildGraph(DebugContext ctx, ResolvedJavaMethod m, HostedProviders providers, Purpose purpose) { - ReflectionGraphKit graphKit = new ReflectionGraphKit(ctx, providers, m); - - ValueNode forceSpecialInvoke = graphKit.loadLocal(0, JavaKind.Int); - ValueNode receiver = graphKit.loadLocal(1, JavaKind.Object); - ValueNode argumentArray = graphKit.loadLocal(2, JavaKind.Object); - /* Clear all locals, so that they are not alive and spilled at method calls. */ - graphKit.getFrameState().clearLocals(); - - ResolvedJavaMethod targetMethod = providers.getMetaAccess().lookupJavaMethod(method); - if (targetMethod.isStatic() || targetMethod.isConstructor()) { - graphKit.emitEnsureInitializedCall(targetMethod.getDeclaringClass()); - } - if (targetMethod.isConstructor()) { - /* - * For a constructor, we invoke a synthetic static factory method that combines both the - * allocation and the constructor invocation. - */ - targetMethod = FactoryMethodSupport.singleton().lookup((UniverseMetaAccess) providers.getMetaAccess(), targetMethod, false); - } - - Class[] argTypes = method.getParameterTypes(); - int receiverOffset = targetMethod.isStatic() ? 0 : 1; - ValueNode[] args = new ValueNode[argTypes.length + receiverOffset]; - if (!targetMethod.isStatic()) { - /* - * The specification explicitly demands a NullPointerException and not a - * IllegalArgumentException when the receiver of a non-static method is null - */ - ValueNode receiverNonNull = graphKit.maybeCreateExplicitNullCheck(receiver); - - args[0] = graphKit.startInstanceOf(receiverNonNull, targetMethod.getDeclaringClass(), true, true); - graphKit.elsePart(); - graphKit.branchToIllegalArgumentException(); - graphKit.endIf(); - } - - graphKit.fillArgsArray(argumentArray, receiverOffset, args, argTypes); - - InvokeKind invokeKind; - assert !targetMethod.isConstructor() : "Constructors are already rewritten to static factory methods"; - if (targetMethod.isStatic()) { - invokeKind = InvokeKind.Static; - } else if (targetMethod.isInterface()) { - invokeKind = InvokeKind.Interface; - } else if (targetMethod.canBeStaticallyBound()) { - invokeKind = InvokeKind.Special; - } else { - invokeKind = InvokeKind.Virtual; - } - - List invokes = new ArrayList<>(); - if (invokeKind.isIndirect()) { - /* - * The graphs are generated before the static analysis is finished. At that time, only - * final methods are known to be statically bound. After the static analysis, many more - * methods can be statically bound. To avoid two identical invokeSpecial invokes for - * such cases, we add a CanBeStaticallyBoundNode to the condition. It is replaced with - * "true" when the method can be statically bound after static analysis, effectively - * removing the whole IfNode and the second invoke node. - */ - LogicNode canBeStaticallyBoundCondition = graphKit.getGraph().unique(new CanBeStaticallyBoundNode(targetMethod)); - LogicNode forceSpecialInvokeCondition = graphKit.append(IntegerEqualsNode.create(forceSpecialInvoke, ConstantNode.forBoolean(true, graphKit.getGraph()), NodeView.DEFAULT)); - LogicNode condition = LogicNode.or(canBeStaticallyBoundCondition, forceSpecialInvokeCondition, BranchProbabilityNode.NOT_LIKELY_PROFILE); - graphKit.startIf(condition, BranchProbabilityNode.NOT_LIKELY_PROFILE); - - graphKit.thenPart(); - if (targetMethod.isAbstract()) { - graphKit.branchToIllegalArgumentException(); - } else { - InvokeWithExceptionNode specialInvoke = graphKit.createJavaCallWithException(InvokeKind.Special, targetMethod, args); - invokes.add(specialInvoke); - graphKit.exceptionPart(); - graphKit.branchToInvocationTargetException(graphKit.exceptionObject()); - graphKit.endInvokeWithException(); - } - - graphKit.elsePart(); - } - - InvokeWithExceptionNode regularInvoke = graphKit.createJavaCallWithException(invokeKind, targetMethod, args); - invokes.add(regularInvoke); - graphKit.exceptionPart(); - graphKit.branchToInvocationTargetException(graphKit.exceptionObject()); - graphKit.endInvokeWithException(); - - AbstractMergeNode merge = invokeKind.isIndirect() ? graphKit.endIf() : null; - - JavaKind returnKind = targetMethod.getSignature().getReturnKind(); - ValueNode returnValue; - if (returnKind == JavaKind.Void) { - returnValue = graphKit.createObject(null); - } else { - returnValue = graphKit.createPhi(invokes, merge); - if (returnKind.isPrimitive()) { - ResolvedJavaType boxedRetType = graphKit.getMetaAccess().lookupJavaType(returnKind.toBoxedJavaClass()); - returnValue = graphKit.createBoxing(returnValue, returnKind, boxedRetType); - } - } - graphKit.createReturn(returnValue, JavaKind.Object); - - graphKit.emitIllegalArgumentException(method, invokeKind == InvokeKind.Static ? null : receiver, argumentArray); - graphKit.emitInvocationTargetException(); - - for (InvokeWithExceptionNode invoke : invokes) { - if (invoke.getInvokeKind().isDirect()) { - InvocationPlugin invocationPlugin = providers.getGraphBuilderPlugins().getInvocationPlugins().lookupInvocation(targetMethod, ctx.getOptions()); - if (invocationPlugin != null && !invocationPlugin.inlineOnly()) { - /* - * The BytecodeParser applies invocation plugins directly during bytecode - * parsing. We cannot do that because GraphKit is not a GraphBuilderContext. To - * get as close as possible to the BytecodeParser behavior, we create a new - * graph for the intrinsic and inline it immediately. - */ - Bytecode code = new ResolvedJavaMethodBytecode(targetMethod); - StructuredGraph intrinsicGraph = new SubstrateIntrinsicGraphBuilder(graphKit.getOptions(), graphKit.getDebug(), providers, code).buildGraph(invocationPlugin); - if (intrinsicGraph != null) { - InliningUtil.inline(invoke, intrinsicGraph, false, targetMethod); - } - } - } - } - - return graphKit.finalizeGraph(); - } -} - -@NodeInfo(cycles = CYCLES_0, size = SIZE_0) -final class CanBeStaticallyBoundNode extends LogicNode implements Canonicalizable { - public static final NodeClass TYPE = NodeClass.create(CanBeStaticallyBoundNode.class); - - private final ResolvedJavaMethod method; - - protected CanBeStaticallyBoundNode(ResolvedJavaMethod method) { - super(TYPE); - this.method = method; - } - - @Override - public Node canonical(CanonicalizerTool tool) { - if (method.canBeStaticallyBound()) { - /* Success: the static analysis found the method to be statically bound. */ - return LogicConstantNode.tautology(); - } else if (method instanceof SharedMethod) { - /* - * We are after the static analysis and the method can still not be statically bound. - * Eliminate this node, but canonicalizing to "false" keeps the dynamic check in place - * in the generated Graal graph. - */ - return LogicConstantNode.contradiction(); - } else { - /* Static analysis is still running. */ - return this; - } - } -}