001/*
002 * Copyright (c) 2012, 2014, 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.stubs;
024
025import static com.oracle.graal.compiler.GraalCompiler.*;
026import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
027
028import java.util.*;
029
030import jdk.internal.jvmci.code.*;
031import jdk.internal.jvmci.common.*;
032import com.oracle.graal.debug.*;
033import com.oracle.graal.debug.Debug.*;
034import com.oracle.graal.debug.internal.*;
035import jdk.internal.jvmci.hotspot.*;
036import jdk.internal.jvmci.meta.*;
037
038import com.oracle.graal.compiler.target.*;
039import com.oracle.graal.hotspot.*;
040import com.oracle.graal.hotspot.meta.*;
041import com.oracle.graal.hotspot.nodes.*;
042import com.oracle.graal.lir.asm.*;
043import com.oracle.graal.lir.phases.*;
044import com.oracle.graal.lir.phases.PostAllocationOptimizationPhase.PostAllocationOptimizationContext;
045import com.oracle.graal.lir.profiling.*;
046import com.oracle.graal.nodes.*;
047import com.oracle.graal.phases.*;
048import com.oracle.graal.phases.schedule.*;
049import com.oracle.graal.phases.tiers.*;
050
051//JaCoCo Exclude
052
053/**
054 * Base class for implementing some low level code providing the out-of-line slow path for a snippet
055 * and/or a callee saved call to a HotSpot C/C++ runtime function or even a another compiled Java
056 * method.
057 */
058public abstract class Stub {
059
060    private static final List<Stub> stubs = new ArrayList<>();
061
062    /**
063     * The linkage information for a call to this stub from compiled code.
064     */
065    protected final HotSpotForeignCallLinkage linkage;
066
067    /**
068     * The code installed for the stub.
069     */
070    protected InstalledCode code;
071
072    /**
073     * Compilation result from which {@link #code} was created.
074     */
075    protected CompilationResult compResult;
076
077    /**
078     * The registers destroyed by this stub.
079     */
080    private Set<Register> destroyedRegisters;
081
082    public void initDestroyedRegisters(Set<Register> registers) {
083        assert registers != null;
084        assert destroyedRegisters == null || registers.equals(destroyedRegisters) : "cannot redefine";
085        destroyedRegisters = registers;
086    }
087
088    /**
089     * Gets the registers defined by this stub. These are the temporaries of this stub and must thus
090     * be caller saved by a callers of this stub.
091     */
092    public Set<Register> getDestroyedRegisters() {
093        assert destroyedRegisters != null : "not yet initialized";
094        return destroyedRegisters;
095    }
096
097    /**
098     * Determines if this stub preserves all registers apart from those it
099     * {@linkplain #getDestroyedRegisters() destroys}.
100     */
101    public boolean preservesRegisters() {
102        return true;
103    }
104
105    protected final HotSpotProviders providers;
106
107    /**
108     * Creates a new stub.
109     *
110     * @param linkage linkage details for a call to the stub
111     */
112    public Stub(HotSpotProviders providers, HotSpotForeignCallLinkage linkage) {
113        this.linkage = linkage;
114        this.providers = providers;
115        stubs.add(this);
116    }
117
118    /**
119     * Gets an immutable view of all stubs that have been created.
120     */
121    public static Collection<Stub> getStubs() {
122        return Collections.unmodifiableList(stubs);
123    }
124
125    /**
126     * Gets the linkage for a call to this stub from compiled code.
127     */
128    public HotSpotForeignCallLinkage getLinkage() {
129        return linkage;
130    }
131
132    public RegisterConfig getRegisterConfig() {
133        return null;
134    }
135
136    /**
137     * Gets the graph that from which the code for this stub will be compiled.
138     */
139    protected abstract StructuredGraph getGraph();
140
141    @Override
142    public String toString() {
143        return "Stub<" + linkage.getDescriptor() + ">";
144    }
145
146    /**
147     * Gets the method the stub's code will be associated with once installed. This may be null.
148     */
149    protected abstract ResolvedJavaMethod getInstalledCodeOwner();
150
151    /**
152     * Gets a context object for the debug scope created when producing the code for this stub.
153     */
154    protected abstract Object debugScopeContext();
155
156    /**
157     * Gets the code for this stub, compiling it first if necessary.
158     */
159    public synchronized InstalledCode getCode(final Backend backend) {
160        if (code == null) {
161            try (Scope d = Debug.sandbox("CompilingStub", DebugScope.getConfig(), providers.getCodeCache(), debugScopeContext())) {
162                final StructuredGraph graph = getGraph();
163
164                // Stubs cannot be recompiled so they cannot be compiled with
165                // assumptions and there is no point in recording evol_method dependencies
166                assert graph.getAssumptions() == null;
167                assert !graph.isInlinedMethodRecordingEnabled() : graph;
168
169                if (!(graph.start() instanceof StubStartNode)) {
170                    StubStartNode newStart = graph.add(new StubStartNode(Stub.this));
171                    newStart.setStateAfter(graph.start().stateAfter());
172                    graph.replaceFixed(graph.start(), newStart);
173                }
174
175                CodeCacheProvider codeCache = providers.getCodeCache();
176                // The stub itself needs the incoming calling convention.
177                CallingConvention incomingCc = linkage.getIncomingCallingConvention();
178                TargetDescription target = codeCache.getTarget();
179
180                compResult = new CompilationResult(toString());
181                try (Scope s0 = Debug.scope("StubCompilation", graph, providers.getCodeCache())) {
182                    Suites defaultSuites = providers.getSuites().getDefaultSuites();
183                    Suites suites = new Suites(new PhaseSuite<>(), defaultSuites.getMidTier(), defaultSuites.getLowTier());
184                    SchedulePhase schedule = emitFrontEnd(providers, target, graph, providers.getSuites().getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL, getProfilingInfo(graph), suites);
185                    LIRSuites lirSuites = createLIRSuites();
186                    emitBackEnd(graph, Stub.this, incomingCc, getInstalledCodeOwner(), backend, target, compResult, CompilationResultBuilderFactory.Default, schedule, getRegisterConfig(), lirSuites);
187                } catch (Throwable e) {
188                    throw Debug.handle(e);
189                }
190
191                assert destroyedRegisters != null;
192                try (Scope s = Debug.scope("CodeInstall")) {
193                    Stub stub = Stub.this;
194                    HotSpotRuntimeStub installedCode = new HotSpotRuntimeStub(stub);
195                    HotSpotCompiledCode hsCompResult = new HotSpotCompiledRuntimeStub(compResult);
196
197                    HotSpotGraalRuntime runtime = runtime();
198                    int result = runtime.getCompilerToVM().installCode(hsCompResult, installedCode, null);
199                    HotSpotVMConfig config = runtime.getConfig();
200                    if (result != config.codeInstallResultOk) {
201                        throw new JVMCIError("Error installing stub %s: %s", Stub.this, config.getCodeInstallResultDescription(result));
202                    }
203                    ((HotSpotCodeCacheProvider) codeCache).logOrDump(installedCode, compResult);
204                    code = installedCode;
205                } catch (Throwable e) {
206                    throw Debug.handle(e);
207                }
208            } catch (Throwable e) {
209                throw Debug.handle(e);
210            }
211            assert code != null : "error installing stub " + this;
212        }
213
214        return code;
215    }
216
217    private LIRSuites createLIRSuites() {
218        LIRSuites lirSuites = new LIRSuites(providers.getSuites().getDefaultLIRSuites());
219        ListIterator<LIRPhase<PostAllocationOptimizationContext>> moveProfiling = lirSuites.getPostAllocationOptimizationStage().findPhase(MoveProfiling.class);
220        if (moveProfiling != null) {
221            moveProfiling.remove();
222        }
223        return lirSuites;
224    }
225
226    /**
227     * Gets the compilation result for this stub, compiling it first if necessary, and installing it
228     * in code.
229     */
230    public synchronized CompilationResult getCompilationResult(final Backend backend) {
231        if (code == null) {
232            getCode(backend);
233        }
234        return compResult;
235    }
236}