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