1 /*
   2  * Copyright (c) 2013, 2015, 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 
  26 package org.graalvm.compiler.asm.aarch64.test;
  27 
  28 import static org.junit.Assert.assertArrayEquals;
  29 
  30 import java.util.EnumSet;
  31 
  32 import org.junit.Assert;
  33 import org.junit.Before;
  34 import org.junit.Test;
  35 
  36 import org.graalvm.compiler.core.common.NumUtil;
  37 import org.graalvm.compiler.asm.aarch64.AArch64Address;
  38 import org.graalvm.compiler.asm.aarch64.AArch64Assembler;
  39 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler;
  40 import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler.AddressGenerationPlan;
  41 import org.graalvm.compiler.test.GraalTest;
  42 
  43 import jdk.vm.ci.aarch64.AArch64;
  44 import jdk.vm.ci.aarch64.AArch64.CPUFeature;
  45 import jdk.vm.ci.code.Architecture;
  46 import jdk.vm.ci.code.Register;
  47 import jdk.vm.ci.code.TargetDescription;
  48 
  49 public class AArch64MacroAssemblerTest extends GraalTest {
  50 
  51     private AArch64MacroAssembler masm;
  52     private TestProtectedAssembler asm;
  53     private Register base;
  54     private Register index;
  55     private Register scratch;
  56 
  57     private static EnumSet<AArch64.CPUFeature> computeFeatures() {
  58         EnumSet<AArch64.CPUFeature> features = EnumSet.noneOf(AArch64.CPUFeature.class);
  59         features.add(CPUFeature.FP);
  60         return features;
  61     }
  62 
  63     private static EnumSet<AArch64.Flag> computeFlags() {
  64         EnumSet<AArch64.Flag> flags = EnumSet.noneOf(AArch64.Flag.class);
  65         return flags;
  66     }
  67 
  68     private static TargetDescription createTarget() {
  69         final int stackFrameAlignment = 16;
  70         final int implicitNullCheckLimit = 4096;
  71         final boolean inlineObjects = true;
  72         Architecture arch = new AArch64(computeFeatures(), computeFlags());
  73         return new TargetDescription(arch, true, stackFrameAlignment, implicitNullCheckLimit, inlineObjects);
  74     }
  75 
  76     @Before
  77     public void setupEnvironment() {
  78         TargetDescription target = createTarget();
  79         masm = new AArch64MacroAssembler(target);
  80         asm = new TestProtectedAssembler(target);
  81         base = AArch64.r10;
  82         index = AArch64.r13;
  83         scratch = AArch64.r15;
  84     }
  85 
  86     @Test
  87     public void testGenerateAddressPlan() {
  88         AddressGenerationPlan plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(8), false, 0);
  89         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.NO_WORK && !plan.needsScratch &&
  90                         (plan.addressingMode == AArch64Address.AddressingMode.IMMEDIATE_SCALED || plan.addressingMode == AArch64Address.AddressingMode.IMMEDIATE_UNSCALED));
  91 
  92         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(8), false, 1);
  93         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.NO_WORK && !plan.needsScratch &&
  94                         (plan.addressingMode == AArch64Address.AddressingMode.IMMEDIATE_SCALED || plan.addressingMode == AArch64Address.AddressingMode.IMMEDIATE_UNSCALED));
  95 
  96         plan = AArch64MacroAssembler.generateAddressPlan(-NumUtil.getNbitNumberInt(8) - 1, false, 0);
  97         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.NO_WORK && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.IMMEDIATE_UNSCALED);
  98 
  99         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(12), false, 1);
 100         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.NO_WORK && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.IMMEDIATE_SCALED);
 101 
 102         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(12) << 2, false, 4);
 103         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.NO_WORK && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.IMMEDIATE_SCALED);
 104 
 105         plan = AArch64MacroAssembler.generateAddressPlan(0, false, 8);
 106         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.NO_WORK && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 107 
 108         plan = AArch64MacroAssembler.generateAddressPlan(0, false, 0);
 109         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.NO_WORK && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 110 
 111         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(9), false, 0);
 112         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_BASE && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 113 
 114         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(12), false, 8);
 115         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_BASE && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 116 
 117         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(13), false, 8);
 118         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_BASE && plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 119 
 120         plan = AArch64MacroAssembler.generateAddressPlan(-NumUtil.getNbitNumberInt(12), false, 8);
 121         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_BASE && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 122 
 123         plan = AArch64MacroAssembler.generateAddressPlan(-(NumUtil.getNbitNumberInt(12) << 12), false, 8);
 124         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_BASE && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 125 
 126         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(12), true, 8);
 127         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_BASE && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 128 
 129         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(12) << 3, true, 8);
 130         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_INDEX && !plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 131 
 132         plan = AArch64MacroAssembler.generateAddressPlan(NumUtil.getNbitNumberInt(13) << 3, true, 8);
 133         Assert.assertTrue(plan.workPlan == AddressGenerationPlan.WorkPlan.ADD_TO_INDEX && plan.needsScratch && plan.addressingMode == AArch64Address.AddressingMode.REGISTER_OFFSET);
 134     }
 135 
 136     @Test
 137     public void testMakeAddressNoAction() {
 138         AArch64Address address = masm.makeAddress(base, NumUtil.getNbitNumberInt(12) << 3, AArch64.zr, false, 8, null, false);
 139         Assert.assertTrue(address.isScaled() && address.getAddressingMode() == AArch64Address.AddressingMode.IMMEDIATE_SCALED && address.getBase().equals(base) &&
 140                         address.getOffset().equals(AArch64.zr) && address.getImmediateRaw() == NumUtil.getNbitNumberInt(12));
 141         // No code generated.
 142         compareAssembly();
 143     }
 144 
 145     @Test
 146     public void testMakeAddressAddIndex() {
 147         AArch64Address address = masm.makeAddress(base, NumUtil.getNbitNumberInt(8) << 5, index, false, 8, null, true);
 148         Assert.assertTrue(address.isScaled() && address.getAddressingMode() == AArch64Address.AddressingMode.REGISTER_OFFSET && address.getBase().equals(base) && address.getOffset().equals(index));
 149         asm.add(64, index, index, NumUtil.getNbitNumberInt(8) << 2);
 150         compareAssembly();
 151     }
 152 
 153     @Test
 154     public void testMakeAddressAddIndexNoOverwrite() {
 155         AArch64Address address = masm.makeAddress(base, NumUtil.getNbitNumberInt(8) << 5, index, false, 8, scratch, false);
 156         Assert.assertTrue(address.isScaled() && address.getAddressingMode() == AArch64Address.AddressingMode.REGISTER_OFFSET && address.getBase().equals(base) && address.getOffset().equals(scratch));
 157         asm.add(64, scratch, index, NumUtil.getNbitNumberInt(8) << 2);
 158         compareAssembly();
 159     }
 160 
 161     @Test
 162     public void testMakeAddressAddBaseNoOverwrite() {
 163         AArch64Address address = masm.makeAddress(base, NumUtil.getNbitNumberInt(12), index, false, 8, scratch, false);
 164         Assert.assertTrue(address.isScaled() && address.getAddressingMode() == AArch64Address.AddressingMode.REGISTER_OFFSET && address.getBase().equals(scratch) && address.getOffset().equals(index));
 165         asm.add(64, scratch, base, NumUtil.getNbitNumberInt(12));
 166         compareAssembly();
 167     }
 168 
 169     @Test
 170     public void testMakeAddressAddBase() {
 171         AArch64Address address = masm.makeAddress(base, NumUtil.getNbitNumberInt(12), index, false, 8, null, true);
 172         Assert.assertTrue(address.isScaled() && address.getAddressingMode() == AArch64Address.AddressingMode.REGISTER_OFFSET && address.getBase().equals(base) && address.getOffset().equals(index));
 173         asm.add(64, base, base, NumUtil.getNbitNumberInt(12));
 174         compareAssembly();
 175     }
 176 
 177     @Test
 178     public void testMakeAddressAddIndexNoOverwriteExtend() {
 179         AArch64Address address = masm.makeAddress(base, NumUtil.getNbitNumberInt(8) << 5, index, true, 8, scratch, false);
 180         Assert.assertTrue(address.isScaled() && address.getAddressingMode() == AArch64Address.AddressingMode.EXTENDED_REGISTER_OFFSET && address.getBase().equals(base) &&
 181                         address.getOffset().equals(scratch) && address.getExtendType() == AArch64Assembler.ExtendType.SXTW);
 182         asm.add(32, scratch, index, NumUtil.getNbitNumberInt(8) << 2);
 183         compareAssembly();
 184     }
 185 
 186     @Test
 187     public void testMakeAddressAddIndexExtend() {
 188         AArch64Address address = masm.makeAddress(base, NumUtil.getNbitNumberInt(8) << 5, index, true, 8, scratch, true);
 189         Assert.assertTrue(address.isScaled() && address.getAddressingMode() == AArch64Address.AddressingMode.EXTENDED_REGISTER_OFFSET && address.getBase().equals(base) &&
 190                         address.getOffset().equals(index) && address.getExtendType() == AArch64Assembler.ExtendType.SXTW);
 191         asm.add(32, index, index, NumUtil.getNbitNumberInt(8) << 2);
 192         compareAssembly();
 193     }
 194 
 195     @Test
 196     public void testLoadAddressUnscaled() {
 197         Register dst = AArch64.r26;
 198         AArch64Address address = AArch64Address.createUnscaledImmediateAddress(base, NumUtil.getNbitNumberInt(8));
 199         masm.loadAddress(dst, address, 8);
 200         asm.add(64, dst, base, NumUtil.getNbitNumberInt(8));
 201         compareAssembly();
 202     }
 203 
 204     @Test
 205     public void testLoadAddressUnscaled2() {
 206         Register dst = AArch64.r26;
 207         AArch64Address address = AArch64Address.createUnscaledImmediateAddress(base, -NumUtil.getNbitNumberInt(8));
 208         masm.loadAddress(dst, address, 8);
 209         asm.sub(64, dst, base, NumUtil.getNbitNumberInt(8));
 210         compareAssembly();
 211     }
 212 
 213     @Test
 214     public void testLoadAddressScaled() {
 215         Register dst = AArch64.r26;
 216         AArch64Address address = AArch64Address.createScaledImmediateAddress(base, NumUtil.getNbitNumberInt(12));
 217         masm.loadAddress(dst, address, 8);
 218         asm.add(64, dst, base, NumUtil.getNbitNumberInt(9) << 3);
 219         asm.add(64, dst, dst, NumUtil.getNbitNumberInt(3) << 12);
 220         compareAssembly();
 221     }
 222 
 223     @Test
 224     public void testLoadAddressScaledLowerOnly() {
 225         Register dst = AArch64.r26;
 226         AArch64Address address = AArch64Address.createScaledImmediateAddress(base, NumUtil.getNbitNumberInt(5));
 227         masm.loadAddress(dst, address, 8);
 228         asm.add(64, dst, base, NumUtil.getNbitNumberInt(5) << 3);
 229         compareAssembly();
 230     }
 231 
 232     @Test
 233     public void testLoadAddressScaledHigherOnly() {
 234         Register dst = AArch64.r26;
 235         AArch64Address address = AArch64Address.createScaledImmediateAddress(base, 1 << 11);
 236         masm.loadAddress(dst, address, 8);
 237         asm.add(64, dst, base, 1 << 11 << 3);
 238         compareAssembly();
 239     }
 240 
 241     @Test
 242     public void testLoadAddressRegisterOffsetUnscaled() {
 243         Register dst = AArch64.r26;
 244         AArch64Address address = AArch64Address.createRegisterOffsetAddress(base, index, false);
 245         masm.loadAddress(dst, address, 4);
 246         asm.add(64, dst, base, index, AArch64Assembler.ShiftType.LSL, 0);
 247         compareAssembly();
 248     }
 249 
 250     @Test
 251     public void testLoadAddressRegisterOffsetScaled() {
 252         Register dst = AArch64.r26;
 253         AArch64Address address = AArch64Address.createRegisterOffsetAddress(base, index, true);
 254         masm.loadAddress(dst, address, 4);
 255         asm.add(64, dst, base, index, AArch64Assembler.ShiftType.LSL, 2);
 256         compareAssembly();
 257     }
 258 
 259     @Test
 260     public void testLoadAddressExtendedRegisterOffsetUnscaled() {
 261         Register dst = AArch64.r26;
 262         AArch64Address address = AArch64Address.createExtendedRegisterOffsetAddress(base, index, false, AArch64Assembler.ExtendType.SXTW);
 263         masm.loadAddress(dst, address, 4);
 264         asm.add(64, dst, base, index, AArch64Assembler.ExtendType.SXTW, 0);
 265         compareAssembly();
 266     }
 267 
 268     @Test
 269     public void testLoadAddressExtendedRegisterOffsetScaled() {
 270         Register dst = AArch64.r26;
 271         AArch64Address address = AArch64Address.createExtendedRegisterOffsetAddress(base, index, true, AArch64Assembler.ExtendType.SXTW);
 272         masm.loadAddress(dst, address, 4);
 273         asm.add(64, dst, base, index, AArch64Assembler.ExtendType.SXTW, 2);
 274         compareAssembly();
 275     }
 276 
 277     /**
 278      * Compares assembly generated by the macro assembler to the hand-generated assembly.
 279      */
 280     private void compareAssembly() {
 281         byte[] expected = asm.close(true);
 282         byte[] actual = masm.close(true);
 283         assertArrayEquals(expected, actual);
 284     }
 285 
 286 }