Skip to content

Commit 890a261

Browse files
author
Christian Wimmer
committed
Workaround for final fields that are written outside of constructor
1 parent 7341914 commit 890a261

File tree

6 files changed

+63
-12
lines changed

6 files changed

+63
-12
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@
4646
import java.util.concurrent.CopyOnWriteArrayList;
4747
import java.util.function.BiPredicate;
4848

49-
import com.oracle.svm.core.jdk.InternalVMMethod;
50-
import com.oracle.svm.core.jdk.LambdaFormHiddenMethod;
5149
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
5250
import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor;
5351
import org.graalvm.compiler.core.common.spi.ForeignCallsProvider;
@@ -105,6 +103,8 @@
105103
import com.oracle.svm.core.hub.HubType;
106104
import com.oracle.svm.core.hub.ReferenceType;
107105
import com.oracle.svm.core.jdk.ClassLoaderSupport;
106+
import com.oracle.svm.core.jdk.InternalVMMethod;
107+
import com.oracle.svm.core.jdk.LambdaFormHiddenMethod;
108108
import com.oracle.svm.core.jdk.RecordSupport;
109109
import com.oracle.svm.core.jdk.SealedClassSupport;
110110
import com.oracle.svm.core.option.HostedOptionKey;
@@ -116,6 +116,7 @@
116116
import com.oracle.svm.hosted.code.InliningUtilities;
117117
import com.oracle.svm.hosted.code.UninterruptibleAnnotationChecker;
118118
import com.oracle.svm.hosted.heap.PodSupport;
119+
import com.oracle.svm.hosted.meta.HostedField;
119120
import com.oracle.svm.hosted.meta.HostedType;
120121
import com.oracle.svm.hosted.meta.HostedUniverse;
121122
import com.oracle.svm.hosted.phases.AnalysisGraphBuilderPhase;
@@ -157,6 +158,8 @@ public class SVMHost extends HostVM {
157158
private final ConcurrentMap<AnalysisMethod, Set<AnalysisType>> initializedClasses = new ConcurrentHashMap<>();
158159
private final ConcurrentMap<AnalysisMethod, Boolean> analysisTrivialMethods = new ConcurrentHashMap<>();
159160

161+
private final Set<AnalysisField> finalFieldsStoredOutsideOfConstructor = ConcurrentHashMap.newKeySet();
162+
160163
public SVMHost(OptionValues options, ClassLoader classLoader, ClassInitializationSupport classInitializationSupport,
161164
UnsafeAutomaticSubstitutionProcessor automaticSubstitutions, Platform platform, SnippetReflectionProvider originalSnippetReflection) {
162165
super(options, classLoader);
@@ -219,7 +222,7 @@ public void checkForbidden(AnalysisType type, AnalysisType.UsageKind kind) {
219222

220223
@Override
221224
public Instance createGraphBuilderPhase(HostedProviders providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
222-
return new AnalysisGraphBuilderPhase(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, providers.getWordTypes());
225+
return new AnalysisGraphBuilderPhase(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, providers.getWordTypes(), this);
223226
}
224227

225228
@Override
@@ -774,4 +777,29 @@ public Comparator<? super ResolvedJavaType> getTypeComparator() {
774777
return HostedUniverse.TYPE_COMPARATOR.compare((HostedType) o1, (HostedType) o2);
775778
};
776779
}
780+
781+
/**
782+
* According to the Java VM specification, final instance fields are only allowed to be written
783+
* in a constructor. But some compilers violate the spec, notably the Scala compiler. This means
784+
* final fields of image heap objects can be modified at image run time, and constant folding
785+
* such fields at image build time would fold in the wrong value. As a workaround, we record
786+
* which final instance fields are written outside of a constructor, and disable constant
787+
* folding for those fields.
788+
*
789+
* Note that there can be races: A constant folding can happen during bytecode parsing before
790+
* the store was recorded. Currently, we do not encounter that problem because constant folding
791+
* only happens after static analysis. But there would not be a good solution other than
792+
* aborting the image build, because a constant folding cannot be reversed.
793+
*/
794+
public void recordFieldStore(ResolvedJavaField field, ResolvedJavaMethod method) {
795+
if (!field.isStatic() && field.isFinal() && (!method.isConstructor() || !field.getDeclaringClass().equals(method.getDeclaringClass()))) {
796+
AnalysisField aField = field instanceof HostedField ? ((HostedField) field).getWrapped() : (AnalysisField) field;
797+
finalFieldsStoredOutsideOfConstructor.add(aField);
798+
}
799+
}
800+
801+
public boolean preventConstantFolding(ResolvedJavaField field) {
802+
AnalysisField aField = field instanceof HostedField ? ((HostedField) field).getWrapped() : (AnalysisField) field;
803+
return finalFieldsStoredOutsideOfConstructor.contains(aField);
804+
}
777805
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class AnalysisConstantFieldProvider extends SharedConstantFieldProvider {
4646

4747
public AnalysisConstantFieldProvider(AnalysisUniverse universe, AnalysisMetaAccess metaAccess, AnalysisConstantReflectionProvider constantReflection,
4848
ClassInitializationSupport classInitializationSupport) {
49-
super(metaAccess, classInitializationSupport);
49+
super(metaAccess, classInitializationSupport, (SVMHost) universe.hostVM());
5050
this.universe = universe;
5151
this.constantReflection = constantReflection;
5252
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/HostedRuntimeConfigurationBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ protected CodeCacheProvider createCodeCacheProvider(RegisterConfig registerConfi
106106

107107
@Override
108108
protected ConstantFieldProvider createConstantFieldProvider(Providers p) {
109-
return new HostedConstantFieldProvider(p.getMetaAccess(), classInitializationSupport);
109+
return new HostedConstantFieldProvider(p.getMetaAccess(), classInitializationSupport, universe.hostVM());
110110
}
111111

112112
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedConstantFieldProvider.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import com.oracle.graal.pointsto.meta.AnalysisField;
3131
import com.oracle.svm.core.meta.ReadableJavaField;
32+
import com.oracle.svm.hosted.SVMHost;
3233
import com.oracle.svm.hosted.ameta.AnalysisConstantFieldProvider;
3334
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
3435

@@ -38,8 +39,8 @@
3839
@Platforms(Platform.HOSTED_ONLY.class)
3940
public class HostedConstantFieldProvider extends SharedConstantFieldProvider {
4041

41-
public HostedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport) {
42-
super(metaAccess, classInitializationSupport);
42+
public HostedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport, SVMHost hostVM) {
43+
super(metaAccess, classInitializationSupport, hostVM);
4344
}
4445

4546
/**

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess;
3232
import com.oracle.svm.core.meta.MethodPointer;
33+
import com.oracle.svm.hosted.SVMHost;
3334
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
3435

3536
import jdk.vm.ci.meta.JavaConstant;
@@ -42,18 +43,24 @@ public abstract class SharedConstantFieldProvider extends JavaConstantFieldProvi
4243

4344
protected final ClassInitializationSupport classInitializationSupport;
4445
protected final UniverseMetaAccess metaAccess;
46+
protected final SVMHost hostVM;
4547

46-
public SharedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport) {
48+
public SharedConstantFieldProvider(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport, SVMHost hostVM) {
4749
super(metaAccess);
4850
this.classInitializationSupport = classInitializationSupport;
4951
this.metaAccess = (UniverseMetaAccess) metaAccess;
52+
this.hostVM = hostVM;
5053
}
5154

5255
@Override
5356
public boolean isFinalField(ResolvedJavaField field, ConstantFieldTool<?> tool) {
5457
if (classInitializationSupport.shouldInitializeAtRuntime(field.getDeclaringClass())) {
5558
return false;
5659
}
60+
if (hostVM.preventConstantFolding(field)) {
61+
return false;
62+
}
63+
5764
return super.isFinalField(field, tool);
5865
}
5966

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/AnalysisGraphBuilderPhase.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
*/
2525
package com.oracle.svm.hosted.phases;
2626

27-
import com.oracle.graal.pointsto.infrastructure.AnalysisConstantPool;
2827
import org.graalvm.compiler.core.common.BootstrapMethodIntrospection;
2928
import org.graalvm.compiler.java.BytecodeParser;
3029
import org.graalvm.compiler.java.GraphBuilderPhase;
@@ -40,29 +39,39 @@
4039
import org.graalvm.compiler.phases.OptimisticOptimizations;
4140
import org.graalvm.compiler.word.WordTypes;
4241

42+
import com.oracle.graal.pointsto.infrastructure.AnalysisConstantPool;
4343
import com.oracle.graal.pointsto.meta.AnalysisMethod;
4444
import com.oracle.svm.core.SubstrateOptions;
45+
import com.oracle.svm.hosted.SVMHost;
4546
import com.oracle.svm.util.ModuleSupport;
4647

4748
import jdk.vm.ci.meta.JavaKind;
49+
import jdk.vm.ci.meta.ResolvedJavaField;
4850
import jdk.vm.ci.meta.ResolvedJavaMethod;
4951

5052
public class AnalysisGraphBuilderPhase extends SharedGraphBuilderPhase {
5153

54+
private final SVMHost hostVM;
55+
5256
public AnalysisGraphBuilderPhase(CoreProviders providers,
53-
GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes) {
57+
GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext, WordTypes wordTypes, SVMHost hostVM) {
5458
super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes);
59+
this.hostVM = hostVM;
5560
}
5661

5762
@Override
5863
protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
59-
return new AnalysisBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext);
64+
return new AnalysisBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext, hostVM);
6065
}
6166

6267
public static class AnalysisBytecodeParser extends SharedBytecodeParser {
68+
69+
private final SVMHost hostVM;
70+
6371
protected AnalysisBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI,
64-
IntrinsicContext intrinsicContext) {
72+
IntrinsicContext intrinsicContext, SVMHost hostVM) {
6573
super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, true);
74+
this.hostVM = hostVM;
6675
}
6776

6877
@Override
@@ -125,5 +134,11 @@ protected void genInvokeDynamic(int cpi, int opcode) {
125134
}
126135
super.genInvokeDynamic(cpi, opcode);
127136
}
137+
138+
@Override
139+
protected void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value) {
140+
hostVM.recordFieldStore(field, method);
141+
super.genStoreField(receiver, field, value);
142+
}
128143
}
129144
}

0 commit comments

Comments
 (0)