001/*
002 * Copyright (c) 2012, 2013, 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.compiler.test.inlining;
024
025import jdk.internal.jvmci.code.*;
026import com.oracle.graal.debug.*;
027import com.oracle.graal.debug.Debug.*;
028import jdk.internal.jvmci.meta.*;
029
030import org.junit.*;
031
032import com.oracle.graal.compiler.test.*;
033import com.oracle.graal.graph.*;
034import com.oracle.graal.graphbuilderconf.*;
035import com.oracle.graal.nodes.*;
036import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
037import com.oracle.graal.phases.*;
038import com.oracle.graal.phases.common.*;
039import com.oracle.graal.phases.common.inlining.*;
040import com.oracle.graal.phases.tiers.*;
041
042public class InliningTest extends GraalCompilerTest {
043
044    @Test
045    public void testInvokeStaticInlining() {
046        assertInlined(getGraph("invokeStaticSnippet", false));
047        assertInlined(getGraph("invokeStaticOnInstanceSnippet", false));
048    }
049
050    @SuppressWarnings("all")
051    public static Boolean invokeStaticSnippet(boolean value) {
052        return Boolean.valueOf(value);
053    }
054
055    @SuppressWarnings("all")
056    public static Boolean invokeStaticOnInstanceSnippet(Boolean obj, boolean value) {
057        return obj.valueOf(value);
058    }
059
060    @Test
061    public void testStaticBindableInlining() {
062        assertInlined(getGraph("invokeConstructorSnippet", false));
063        assertInlined(getGraph("invokeFinalMethodSnippet", false));
064        assertInlined(getGraph("invokeMethodOnFinalClassSnippet", false));
065        assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", false));
066    }
067
068    @Ignore("would need read elimination/EA before inlining")
069    @Test
070    public void testDependentStaticBindableInlining() {
071        assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", false));
072        assertInlined(getGraph("invokeMethodOnFieldSnippet", false));
073    }
074
075    @Test
076    public void testStaticBindableInliningIP() {
077        assertManyMethodInfopoints(assertInlined(getGraph("invokeConstructorSnippet", true)));
078        assertManyMethodInfopoints(assertInlined(getGraph("invokeFinalMethodSnippet", true)));
079        assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalClassSnippet", true)));
080        assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnStaticFinalFieldSnippet", true)));
081    }
082
083    @Ignore("would need read elimination/EA before inlining")
084    @Test
085    public void testDependentStaticBindableInliningIP() {
086        assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFinalFieldSnippet", true)));
087        assertManyMethodInfopoints(assertInlined(getGraph("invokeMethodOnFieldSnippet", true)));
088    }
089
090    @SuppressWarnings("all")
091    public static Object invokeConstructorSnippet(int value) {
092        return new SuperClass(value);
093    }
094
095    @SuppressWarnings("all")
096    public static int invokeFinalMethodSnippet(SuperClass superClass, SubClassA subClassA, FinalSubClass finalSubClass) {
097        return superClass.publicFinalMethod() + subClassA.publicFinalMethod() + finalSubClass.publicFinalMethod() + superClass.protectedFinalMethod() + subClassA.protectedFinalMethod() +
098                        finalSubClass.protectedFinalMethod();
099    }
100
101    @SuppressWarnings("all")
102    public static int invokeMethodOnFinalClassSnippet(FinalSubClass finalSubClass) {
103        return finalSubClass.publicFinalMethod() + finalSubClass.publicNotOverriddenMethod() + finalSubClass.publicOverriddenMethod() + finalSubClass.protectedFinalMethod() +
104                        finalSubClass.protectedNotOverriddenMethod() + finalSubClass.protectedOverriddenMethod();
105    }
106
107    @SuppressWarnings("all")
108    public static int invokeMethodOnStaticFinalFieldSnippet() {
109        return StaticFinalFields.NumberStaticFinalField.intValue() + StaticFinalFields.SuperClassStaticFinalField.publicOverriddenMethod() +
110                        StaticFinalFields.FinalSubClassStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SingleImplementorStaticFinalField.publicOverriddenMethod() +
111                        StaticFinalFields.MultipleImplementorsStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassAStaticFinalField.publicOverriddenMethod() +
112                        StaticFinalFields.SubClassBStaticFinalField.publicOverriddenMethod() + StaticFinalFields.SubClassCStaticFinalField.publicOverriddenMethod();
113    }
114
115    @SuppressWarnings("all")
116    public static int invokeMethodOnFinalFieldSnippet() {
117        FinalFields fields = new FinalFields();
118        return fields.numberFinalField.intValue() + fields.superClassFinalField.publicOverriddenMethod() + fields.finalSubClassFinalField.publicOverriddenMethod() +
119                        fields.singleImplementorFinalField.publicOverriddenMethod() + fields.multipleImplementorsFinalField.publicOverriddenMethod() +
120                        fields.subClassAFinalField.publicOverriddenMethod() + fields.subClassBFinalField.publicOverriddenMethod() + fields.subClassCFinalField.publicOverriddenMethod();
121    }
122
123    @SuppressWarnings("all")
124    public static int invokeMethodOnFieldSnippet() {
125        Fields fields = new Fields();
126        return fields.numberField.intValue() + fields.superClassField.publicOverriddenMethod() + fields.finalSubClassField.publicOverriddenMethod() +
127                        fields.singleImplementorField.publicOverriddenMethod() + fields.multipleImplementorsField.publicOverriddenMethod() + fields.subClassAField.publicOverriddenMethod() +
128                        fields.subClassBField.publicOverriddenMethod() + fields.subClassCField.publicOverriddenMethod();
129    }
130
131    public interface Attributes {
132
133        int getLength();
134    }
135
136    public class NullAttributes implements Attributes {
137
138        @Override
139        public int getLength() {
140            return 0;
141        }
142
143    }
144
145    public class TenAttributes implements Attributes {
146
147        @Override
148        public int getLength() {
149            return 10;
150        }
151
152    }
153
154    public int getAttributesLength(Attributes a) {
155        return a.getLength();
156    }
157
158    @Test
159    public void testGuardedInline() {
160        NullAttributes nullAttributes = new NullAttributes();
161        for (int i = 0; i < 10000; i++) {
162            getAttributesLength(nullAttributes);
163        }
164        getAttributesLength(new TenAttributes());
165
166        test("getAttributesLength", nullAttributes);
167        test("getAttributesLength", (Object) null);
168    }
169
170    @Test
171    public void testClassHierarchyAnalysis() {
172        assertInlined(getGraph("invokeLeafClassMethodSnippet", false));
173        assertInlined(getGraph("invokeConcreteMethodSnippet", false));
174        assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", false));
175        // assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", false));
176
177        assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", false));
178        assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", false));
179        assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", false));
180    }
181
182    @Test
183    public void testClassHierarchyAnalysisIP() {
184        assertManyMethodInfopoints(assertInlined(getGraph("invokeLeafClassMethodSnippet", true)));
185        assertManyMethodInfopoints(assertInlined(getGraph("invokeConcreteMethodSnippet", true)));
186        assertManyMethodInfopoints(assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", true)));
187        //@formatter:off
188        // assertInlineInfopoints(assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", true)));
189        //@formatter:on
190
191        assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", true)));
192        assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenProtectedMethodSnippet", true)));
193        assertFewMethodInfopoints(assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet", true)));
194    }
195
196    @SuppressWarnings("all")
197    public static int invokeLeafClassMethodSnippet(SubClassA subClassA) {
198        return subClassA.publicFinalMethod() + subClassA.publicNotOverriddenMethod() + subClassA.publicOverriddenMethod();
199    }
200
201    @SuppressWarnings("all")
202    public static int invokeConcreteMethodSnippet(SuperClass superClass) {
203        return superClass.publicNotOverriddenMethod() + superClass.protectedNotOverriddenMethod();
204    }
205
206    @SuppressWarnings("all")
207    public static int invokeSingleImplementorInterfaceSnippet(SingleImplementorInterface testInterface) {
208        return testInterface.publicNotOverriddenMethod() + testInterface.publicOverriddenMethod();
209    }
210
211    @SuppressWarnings("all")
212    public static int invokeConcreteInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
213        return testInterface.publicNotOverriddenMethod();
214    }
215
216    @SuppressWarnings("all")
217    public static int invokeOverriddenInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
218        return testInterface.publicOverriddenMethod();
219    }
220
221    @SuppressWarnings("all")
222    public static int invokeOverriddenPublicMethodSnippet(SuperClass superClass) {
223        return superClass.publicOverriddenMethod();
224    }
225
226    @SuppressWarnings("all")
227    public static int invokeOverriddenProtectedMethodSnippet(SuperClass superClass) {
228        return superClass.protectedOverriddenMethod();
229    }
230
231    private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) {
232        try (Scope s = Debug.scope("InliningTest", new DebugDumpScope(snippet))) {
233            ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
234            StructuredGraph graph = eagerInfopointMode ? parseDebug(method, AllowAssumptions.YES) : parseEager(method, AllowAssumptions.YES);
235            PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getFullDebugDefault(getDefaultGraphBuilderPlugins()))
236                            : getDefaultGraphBuilderSuite();
237            HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
238            Debug.dump(graph, "Graph");
239            new CanonicalizerPhase().apply(graph, context);
240            new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
241            Debug.dump(graph, "Graph");
242            new CanonicalizerPhase().apply(graph, context);
243            new DeadCodeEliminationPhase().apply(graph);
244            return graph;
245        } catch (Throwable e) {
246            throw Debug.handle(e);
247        }
248    }
249
250    private static StructuredGraph assertInlined(StructuredGraph graph) {
251        return assertNotInGraph(graph, Invoke.class);
252    }
253
254    private static StructuredGraph assertNotInlined(StructuredGraph graph) {
255        return assertInGraph(graph, Invoke.class);
256    }
257
258    private static StructuredGraph assertNotInGraph(StructuredGraph graph, Class<?> clazz) {
259        for (Node node : graph.getNodes()) {
260            if (clazz.isInstance(node)) {
261                fail(node.toString());
262            }
263        }
264        return graph;
265    }
266
267    private static StructuredGraph assertInGraph(StructuredGraph graph, Class<?> clazz) {
268        for (Node node : graph.getNodes()) {
269            if (clazz.isInstance(node)) {
270                return graph;
271            }
272        }
273        fail("Graph does not contain a node of class " + clazz.getName());
274        return graph;
275    }
276
277    private static int[] countMethodInfopoints(StructuredGraph graph) {
278        int start = 0;
279        int end = 0;
280        for (FullInfopointNode ipn : graph.getNodes().filter(FullInfopointNode.class)) {
281            if (ipn.getReason() == InfopointReason.METHOD_START) {
282                ++start;
283            } else if (ipn.getReason() == InfopointReason.METHOD_END) {
284                ++end;
285            }
286        }
287        return new int[]{start, end};
288    }
289
290    private static StructuredGraph assertManyMethodInfopoints(StructuredGraph graph) {
291        int[] counts = countMethodInfopoints(graph);
292        if (counts[0] <= 1 || counts[1] <= 1) {
293            fail(String.format("Graph contains too few required method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
294        }
295        return graph;
296    }
297
298    private static StructuredGraph assertFewMethodInfopoints(StructuredGraph graph) {
299        int[] counts = countMethodInfopoints(graph);
300        if (counts[0] > 1 || counts[1] > 1) {
301            fail(String.format("Graph contains too many method boundary infopoints: %d starts, %d ends.", counts[0], counts[1]));
302        }
303        return graph;
304    }
305
306    // some interfaces and classes for testing
307    private interface MultipleImplementorsInterface {
308
309        int publicNotOverriddenMethod();
310
311        int publicOverriddenMethod();
312    }
313
314    private interface SingleImplementorInterface {
315
316        int publicNotOverriddenMethod();
317
318        int publicOverriddenMethod();
319    }
320
321    private static class SuperClass implements MultipleImplementorsInterface {
322
323        protected int value;
324
325        public SuperClass(int value) {
326            this.value = value;
327        }
328
329        public int publicNotOverriddenMethod() {
330            return value;
331        }
332
333        public int publicOverriddenMethod() {
334            return value;
335        }
336
337        protected int protectedNotOverriddenMethod() {
338            return value;
339        }
340
341        protected int protectedOverriddenMethod() {
342            return value;
343        }
344
345        public final int publicFinalMethod() {
346            return value + 255;
347        }
348
349        protected final int protectedFinalMethod() {
350            return value + 255;
351        }
352    }
353
354    private static class SubClassA extends SuperClass implements SingleImplementorInterface {
355
356        public SubClassA(int value) {
357            super(value);
358        }
359
360        @Override
361        public int publicOverriddenMethod() {
362            return value + 2;
363        }
364
365        @Override
366        protected int protectedOverriddenMethod() {
367            return value * 2;
368        }
369    }
370
371    private static class SubClassB extends SuperClass {
372
373        public SubClassB(int value) {
374            super(value);
375        }
376
377        @Override
378        public int publicOverriddenMethod() {
379            return value + 3;
380        }
381
382        @Override
383        protected int protectedOverriddenMethod() {
384            return value * 3;
385        }
386    }
387
388    private static class SubClassC extends SuperClass {
389
390        public SubClassC(int value) {
391            super(value);
392        }
393
394        @Override
395        public int publicOverriddenMethod() {
396            return value + 4;
397        }
398
399        @Override
400        protected int protectedOverriddenMethod() {
401            return value * 4;
402        }
403    }
404
405    private static final class FinalSubClass extends SuperClass {
406
407        public FinalSubClass(int value) {
408            super(value);
409        }
410
411        @Override
412        public int publicOverriddenMethod() {
413            return value + 5;
414        }
415
416        @Override
417        protected int protectedOverriddenMethod() {
418            return value * 5;
419        }
420    }
421
422    private static final class StaticFinalFields {
423
424        private static final Number NumberStaticFinalField = new Integer(1);
425        private static final SuperClass SuperClassStaticFinalField = new SubClassA(2);
426        private static final FinalSubClass FinalSubClassStaticFinalField = new FinalSubClass(3);
427        private static final SingleImplementorInterface SingleImplementorStaticFinalField = new SubClassA(4);
428        private static final MultipleImplementorsInterface MultipleImplementorsStaticFinalField = new SubClassC(5);
429        private static final SubClassA SubClassAStaticFinalField = new SubClassA(6);
430        private static final SubClassB SubClassBStaticFinalField = new SubClassB(7);
431        private static final SubClassC SubClassCStaticFinalField = new SubClassC(8);
432    }
433
434    private static final class FinalFields {
435
436        private final Number numberFinalField = new Integer(1);
437        private final SuperClass superClassFinalField = new SubClassA(2);
438        private final FinalSubClass finalSubClassFinalField = new FinalSubClass(3);
439        private final SingleImplementorInterface singleImplementorFinalField = new SubClassA(4);
440        private final MultipleImplementorsInterface multipleImplementorsFinalField = new SubClassC(5);
441        private final SubClassA subClassAFinalField = new SubClassA(6);
442        private final SubClassB subClassBFinalField = new SubClassB(7);
443        private final SubClassC subClassCFinalField = new SubClassC(8);
444    }
445
446    private static final class Fields {
447
448        private Number numberField = new Integer(1);
449        private SuperClass superClassField = new SubClassA(2);
450        private FinalSubClass finalSubClassField = new FinalSubClass(3);
451        private SingleImplementorInterface singleImplementorField = new SubClassA(4);
452        private MultipleImplementorsInterface multipleImplementorsField = new SubClassC(5);
453        private SubClassA subClassAField = new SubClassA(6);
454        private SubClassB subClassBField = new SubClassB(7);
455        private SubClassC subClassCField = new SubClassC(8);
456    }
457}