1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 
  25 package org.graalvm.compiler.core.amd64.test;
  26 
  27 import static org.junit.Assume.assumeTrue;
  28 
  29 import org.graalvm.compiler.core.common.cfg.AbstractBlockBase;
  30 import org.graalvm.compiler.core.test.MatchRuleTest;
  31 import org.graalvm.compiler.lir.LIR;
  32 import org.graalvm.compiler.lir.LIRInstruction;
  33 import org.graalvm.compiler.lir.amd64.AMD64Binary;
  34 import org.graalvm.compiler.lir.amd64.AMD64BinaryConsumer.MemoryConstOp;
  35 import org.graalvm.compiler.lir.amd64.AMD64BinaryConsumer.ConstOp;
  36 import org.graalvm.compiler.lir.amd64.AMD64Unary;
  37 import org.junit.Before;
  38 import org.junit.Test;
  39 
  40 import jdk.vm.ci.amd64.AMD64;
  41 import jdk.vm.ci.amd64.AMD64Kind;
  42 
  43 public class AMD64MatchRuleTest extends MatchRuleTest {
  44     @Before
  45     public void checkAMD64() {
  46         assumeTrue("skipping AMD64 specific test", getTarget().arch instanceof AMD64);
  47     }
  48 
  49     public static int test1Snippet(TestClass o, TestClass b, TestClass c) {
  50         if (o.x == 42) {
  51             return b.z;
  52         } else {
  53             return c.y;
  54         }
  55     }
  56 
  57     /**
  58      * Verifies, if the match rules in AMD64NodeMatchRules do work on the graphs by compiling and
  59      * checking if the expected LIR instruction show up.
  60      */
  61     @Test
  62     public void test1() {
  63         compile(getResolvedJavaMethod("test1Snippet"), null);
  64         LIR lir = getLIR();
  65         boolean found = false;
  66         for (LIRInstruction ins : lir.getLIRforBlock(lir.codeEmittingOrder()[0])) {
  67             if (ins instanceof MemoryConstOp && ((MemoryConstOp) ins).getOpcode().toString().equals("CMP")) {
  68                 assertFalse("MemoryConstOp expected only once in first block", found);
  69                 found = true;
  70             }
  71         }
  72         assertTrue("Memory compare must be in the LIR", found);
  73     }
  74 
  75     public static class TestClass {
  76         public int x;
  77         public int y;
  78         public int z;
  79 
  80         public TestClass(int x) {
  81             super();
  82             this.x = x;
  83         }
  84     }
  85 
  86     static volatile short shortValue;
  87 
  88     public static long testVolatileExtensionSnippet() {
  89         return shortValue;
  90     }
  91 
  92     @Test
  93     public void testVolatileExtension() {
  94         compile(getResolvedJavaMethod("testVolatileExtensionSnippet"), null);
  95         LIR lir = getLIR();
  96         boolean found = false;
  97         for (LIRInstruction ins : lir.getLIRforBlock(lir.codeEmittingOrder()[0])) {
  98             if (ins instanceof AMD64Unary.MemoryOp) {
  99                 ins.visitEachOutput((value, mode, flags) -> assertTrue(value.getPlatformKind().toString(), value.getPlatformKind().equals(AMD64Kind.QWORD)));
 100                 assertFalse("MemoryOp expected only once in first block", found);
 101                 found = true;
 102             }
 103         }
 104         assertTrue("sign extending load must be in the LIR", found);
 105     }
 106 
 107     static int intValue;
 108     static volatile int volatileIntValue;
 109 
 110     /**
 111      * Can't match test and load of input because of volatile store in between.
 112      */
 113     public static short testLoadTestNoMatchSnippet() {
 114         int v = intValue;
 115         volatileIntValue = 42;
 116         if (v == 42) {
 117             return shortValue;
 118         }
 119         return 0;
 120     }
 121 
 122     @Test
 123     public void testLoadTestNoMatch() {
 124         compile(getResolvedJavaMethod("testLoadTestNoMatchSnippet"), null);
 125         LIR lir = getLIR();
 126         boolean found = false;
 127         for (LIRInstruction ins : lir.getLIRforBlock(lir.codeEmittingOrder()[0])) {
 128             if (ins instanceof ConstOp && ((ConstOp) ins).getOpcode().toString().equals("CMP")) {
 129                 assertFalse("CMP expected only once in first block", found);
 130                 found = true;
 131             }
 132         }
 133         assertTrue("CMP must be in the LIR", found);
 134     }
 135 
 136     /**
 137      * Should match as an add with a memory operand.
 138      */
 139     public static int testAddLoadSnippet() {
 140         int v1 = volatileIntValue;
 141         int v2 = intValue;
 142         return v2 + (2 * v1);
 143     }
 144 
 145     @Test
 146     public void testAddLoad() {
 147         compile(getResolvedJavaMethod("testAddLoadSnippet"), null);
 148         LIR lir = getLIR();
 149         boolean found = false;
 150         for (LIRInstruction ins : lir.getLIRforBlock(lir.codeEmittingOrder()[0])) {
 151             if (ins instanceof AMD64Binary.MemoryTwoOp && ((AMD64Binary.MemoryTwoOp) ins).getOpcode().toString().equals("ADD")) {
 152                 assertFalse("MemoryTwoOp expected only once in first block", found);
 153                 found = true;
 154             }
 155         }
 156         assertTrue("ADD with memory argument must be in the LIR", found);
 157     }
 158 
 159     /**
 160      * Can't match as an add with a memory operand because the other add input is too late.
 161      */
 162     public static int testAddLoadNoMatchSnippet() {
 163         int v1 = volatileIntValue;
 164         int v2 = intValue;
 165         return v1 + (2 * v2);
 166     }
 167 
 168     @Test
 169     public void testAddLoadNoMatch() {
 170         compile(getResolvedJavaMethod("testAddLoadNoMatchSnippet"), null);
 171         LIR lir = getLIR();
 172         boolean found = false;
 173         for (LIRInstruction ins : lir.getLIRforBlock(lir.codeEmittingOrder()[0])) {
 174             if (ins instanceof AMD64Binary.CommutativeTwoOp && ((AMD64Binary.CommutativeTwoOp) ins).getOpcode().toString().equals("ADD")) {
 175                 assertFalse("CommutativeTwoOp expected only once in first block", found);
 176                 found = true;
 177             }
 178         }
 179         assertTrue("ADD with memory argument must not be in the LIR", found);
 180     }
 181 
 182     /**
 183      * sign extension and load are in different blocks but can still be matched as a single
 184      * instruction.
 185      */
 186     public static long testVolatileExtensionDifferentBlocksSnippet(boolean flag) {
 187         short v = shortValue;
 188         if (flag) {
 189             return v;
 190         }
 191         return 0;
 192     }
 193 
 194     @Test
 195     public void testVolatileExtensionDifferentBlocks() {
 196         compile(getResolvedJavaMethod("testVolatileExtensionDifferentBlocksSnippet"), null);
 197         LIR lir = getLIR();
 198         boolean found = false;
 199         for (LIRInstruction ins : lir.getLIRforBlock(lir.codeEmittingOrder()[0])) {
 200             if (ins instanceof AMD64Unary.MemoryOp) {
 201                 ins.visitEachOutput((value, mode, flags) -> assertTrue(value.getPlatformKind().toString(), value.getPlatformKind().equals(AMD64Kind.QWORD)));
 202                 assertFalse("MemoryOp expected only once in first block", found);
 203                 found = true;
 204             }
 205         }
 206         assertTrue("sign extending load must be in the LIR", found);
 207     }
 208 
 209     /**
 210      * Add and load are not in the same block and one input is too late: can't match.
 211      */
 212     public static int testAddLoadDifferentBlocksNoMatchSnippet(boolean flag) {
 213         int v1 = volatileIntValue;
 214         if (flag) {
 215             int v2 = intValue;
 216             return v1 + (2 * v2);
 217         }
 218         return 0;
 219     }
 220 
 221     @Test
 222     public void testAddLoadDifferentBlocksNoMatch() {
 223         compile(getResolvedJavaMethod("testAddLoadDifferentBlocksNoMatchSnippet"), null);
 224         LIR lir = getLIR();
 225         boolean found = false;
 226         for (AbstractBlockBase<?> b : lir.codeEmittingOrder()) {
 227             for (LIRInstruction ins : lir.getLIRforBlock(b)) {
 228                 if (ins instanceof AMD64Binary.CommutativeTwoOp && ((AMD64Binary.CommutativeTwoOp) ins).getOpcode().toString().equals("ADD")) {
 229                     assertFalse("CommutativeTwoOp expected only once in first block", found);
 230                     found = true;
 231                 }
 232             }
 233         }
 234         assertTrue("ADD with memory argument must not be in the LIR", found);
 235     }
 236 
 237     /**
 238      * Add and load are in different blocks but can still match.
 239      */
 240     public static int testAddLoadDifferentBlocksSnippet(boolean flag) {
 241         int v2 = intValue;
 242         int v1 = volatileIntValue;
 243         if (flag) {
 244             return v1 + v2;
 245         }
 246         return 0;
 247     }
 248 
 249     @Test
 250     public void testAddLoadDifferentBlocks() {
 251         compile(getResolvedJavaMethod("testAddLoadDifferentBlocksSnippet"), null);
 252         LIR lir = getLIR();
 253         boolean found = false;
 254         for (LIRInstruction ins : lir.getLIRforBlock(lir.codeEmittingOrder()[0])) {
 255             if (ins instanceof AMD64Binary.MemoryTwoOp && ((AMD64Binary.MemoryTwoOp) ins).getOpcode().toString().equals("ADD")) {
 256                 assertFalse("MemoryTwoOp expected only once in first block", found);
 257                 found = true;
 258             }
 259         }
 260         assertTrue("ADD with memory argument must be in the LIR", found);
 261     }
 262 
 263 }