001/*
002 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot.meta;
024
025import static com.oracle.graal.compiler.common.GraalOptions.*;
026import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
027import static com.oracle.graal.hotspot.meta.HotSpotGraalConstantReflectionProvider.ImmutableCodeLazy.*;
028import static com.oracle.graal.hotspot.stubs.SnippetStub.*;
029
030import java.util.*;
031
032import jdk.internal.jvmci.common.*;
033import jdk.internal.jvmci.hotspot.*;
034import jdk.internal.jvmci.meta.*;
035
036import com.oracle.graal.graph.*;
037import com.oracle.graal.replacements.*;
038import com.oracle.graal.replacements.SnippetTemplate.Arguments;
039
040/**
041 * Extends {@link HotSpotConstantReflectionProvider} to override the implementation of
042 * {@link #readConstantFieldValue(JavaField, JavaConstant)} with Graal specific semantics.
043 */
044public class HotSpotGraalConstantReflectionProvider extends HotSpotConstantReflectionProvider {
045
046    public HotSpotGraalConstantReflectionProvider(HotSpotJVMCIRuntimeProvider runtime) {
047        super(runtime);
048    }
049
050    @Override
051    public JavaConstant readConstantFieldValue(JavaField field, JavaConstant receiver) {
052        assert !ImmutableCode.getValue() || isCalledForSnippets() || SnippetGraphUnderConstruction.get() != null || FieldReadEnabledInImmutableCode.get() == Boolean.TRUE : receiver;
053        return super.readConstantFieldValue(field, receiver);
054    }
055
056    /**
057     * In AOT mode, some fields should never be embedded even for snippets/replacements.
058     */
059    @Override
060    protected boolean isStaticFieldConstant(HotSpotResolvedJavaField field) {
061        return super.isStaticFieldConstant(field) && (!ImmutableCode.getValue() || ImmutableCodeLazy.isEmbeddable(field));
062
063    }
064
065    @SuppressWarnings("all")
066    private static boolean assertionsEnabled() {
067        boolean enabled = false;
068        assert enabled = true;
069        return enabled;
070    }
071
072    public static final ThreadLocal<Boolean> FieldReadEnabledInImmutableCode = assertionsEnabled() ? new ThreadLocal<>() : null;
073
074    /**
075     * Compares two {@link StackTraceElement}s for equality, ignoring differences in
076     * {@linkplain StackTraceElement#getLineNumber() line number}.
077     */
078    private static boolean equalsIgnoringLine(StackTraceElement left, StackTraceElement right) {
079        return left.getClassName().equals(right.getClassName()) && left.getMethodName().equals(right.getMethodName()) && left.getFileName().equals(right.getFileName());
080    }
081
082    @Override
083    protected boolean isFinalInstanceFieldValueConstant(JavaConstant value, Class<?> receiverClass) {
084        return super.isFinalInstanceFieldValueConstant(value, receiverClass) || receiverClass == SnippetCounter.class || receiverClass == NodeClass.class;
085    }
086
087    /**
088     * {@inheritDoc}
089     *
090     * {@link HotSpotVMConfig} has a lot of zero-value fields which we know are stable and want to
091     * be considered as constants.
092     */
093    @Override
094    protected boolean isStableInstanceFieldValueConstant(JavaConstant value, Class<? extends Object> receiverClass) {
095        return super.isStableInstanceFieldValueConstant(value, receiverClass) || receiverClass == HotSpotVMConfig.class;
096    }
097
098    /**
099     * Separate out the static initialization of
100     * {@linkplain #isEmbeddable(HotSpotResolvedJavaField) embeddable fields} to eliminate cycles
101     * between clinit and other locks that could lead to deadlock. Static code that doesn't call
102     * back into type or field machinery is probably ok but anything else should be made lazy.
103     */
104    static class ImmutableCodeLazy {
105
106        /**
107         * If the compiler is configured for AOT mode,
108         * {@link #readConstantFieldValue(JavaField, JavaConstant)} should be only called for
109         * snippets or replacements.
110         */
111        static boolean isCalledForSnippets() {
112            assert ImmutableCode.getValue();
113            MetaAccessProvider metaAccess = runtime().getHostProviders().getMetaAccess();
114            ResolvedJavaMethod makeGraphMethod = null;
115            ResolvedJavaMethod initMethod = null;
116            try {
117                Class<?> rjm = ResolvedJavaMethod.class;
118                makeGraphMethod = metaAccess.lookupJavaMethod(ReplacementsImpl.class.getDeclaredMethod("makeGraph", rjm, Object[].class, rjm));
119                initMethod = metaAccess.lookupJavaMethod(SnippetTemplate.AbstractTemplates.class.getDeclaredMethod("template", Arguments.class));
120            } catch (NoSuchMethodException | SecurityException e) {
121                throw new JVMCIError(e);
122            }
123            StackTraceElement makeGraphSTE = makeGraphMethod.asStackTraceElement(0);
124            StackTraceElement initSTE = initMethod.asStackTraceElement(0);
125
126            StackTraceElement[] stackTrace = new Exception().getStackTrace();
127            for (StackTraceElement element : stackTrace) {
128                // Ignoring line numbers should not weaken this check too much while at
129                // the same time making it more robust against source code changes
130                if (equalsIgnoringLine(makeGraphSTE, element) || equalsIgnoringLine(initSTE, element)) {
131                    return true;
132                }
133            }
134            return false;
135        }
136
137        /**
138         * Determine if it's ok to embed the value of {@code field}.
139         */
140        static boolean isEmbeddable(HotSpotResolvedJavaField field) {
141            assert ImmutableCode.getValue();
142            return !embeddableFields.contains(field);
143        }
144
145        private static final List<ResolvedJavaField> embeddableFields = new ArrayList<>();
146        static {
147            try {
148                MetaAccessProvider metaAccess = runtime().getHostProviders().getMetaAccess();
149                embeddableFields.add(metaAccess.lookupJavaField(Boolean.class.getDeclaredField("TRUE")));
150                embeddableFields.add(metaAccess.lookupJavaField(Boolean.class.getDeclaredField("FALSE")));
151
152                Class<?> characterCacheClass = Character.class.getDeclaredClasses()[0];
153                assert "java.lang.Character$CharacterCache".equals(characterCacheClass.getName());
154                embeddableFields.add(metaAccess.lookupJavaField(characterCacheClass.getDeclaredField("cache")));
155
156                Class<?> byteCacheClass = Byte.class.getDeclaredClasses()[0];
157                assert "java.lang.Byte$ByteCache".equals(byteCacheClass.getName());
158                embeddableFields.add(metaAccess.lookupJavaField(byteCacheClass.getDeclaredField("cache")));
159
160                Class<?> shortCacheClass = Short.class.getDeclaredClasses()[0];
161                assert "java.lang.Short$ShortCache".equals(shortCacheClass.getName());
162                embeddableFields.add(metaAccess.lookupJavaField(shortCacheClass.getDeclaredField("cache")));
163
164                Class<?> integerCacheClass = Integer.class.getDeclaredClasses()[0];
165                assert "java.lang.Integer$IntegerCache".equals(integerCacheClass.getName());
166                embeddableFields.add(metaAccess.lookupJavaField(integerCacheClass.getDeclaredField("cache")));
167
168                Class<?> longCacheClass = Long.class.getDeclaredClasses()[0];
169                assert "java.lang.Long$LongCache".equals(longCacheClass.getName());
170                embeddableFields.add(metaAccess.lookupJavaField(longCacheClass.getDeclaredField("cache")));
171
172                embeddableFields.add(metaAccess.lookupJavaField(Throwable.class.getDeclaredField("UNASSIGNED_STACK")));
173                embeddableFields.add(metaAccess.lookupJavaField(Throwable.class.getDeclaredField("SUPPRESSED_SENTINEL")));
174            } catch (SecurityException | NoSuchFieldException e) {
175                throw new JVMCIError(e);
176            }
177        }
178    }
179
180}