1 /*
   2  * Copyright (c) 2013, 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.asm.aarch64;
  26 
  27 import static jdk.vm.ci.aarch64.AArch64.zr;
  28 
  29 import org.graalvm.compiler.asm.AbstractAddress;
  30 import org.graalvm.compiler.core.common.NumUtil;
  31 import org.graalvm.compiler.debug.GraalError;
  32 
  33 import jdk.vm.ci.aarch64.AArch64;
  34 import jdk.vm.ci.code.Register;
  35 
  36 /**
  37  * Represents an address in target machine memory, specified using one of the different addressing
  38  * modes of the AArch64 ISA. - Base register only - Base register + immediate or register with
  39  * shifted offset - Pre-indexed: base + immediate offset are written back to base register, value
  40  * used in instruction is base + offset - Post-indexed: base + offset (immediate or register) are
  41  * written back to base register, value used in instruction is base only - Literal: PC + 19-bit
  42  * signed word aligned offset
  43  * <p>
  44  * Not all addressing modes are supported for all instructions.
  45  */
  46 public final class AArch64Address extends AbstractAddress {
  47     // Placeholder for addresses that get patched later.
  48     public static final AArch64Address PLACEHOLDER = createPcLiteralAddress(0);
  49 
  50     public enum AddressingMode {
  51         /**
  52          * base + uimm12 << log2(memory_transfer_size).
  53          */
  54         IMMEDIATE_SCALED,
  55         /**
  56          * base + imm9.
  57          */
  58         IMMEDIATE_UNSCALED,
  59         /**
  60          * base.
  61          */
  62         BASE_REGISTER_ONLY,
  63         /**
  64          * base + offset [<< log2(memory_transfer_size)].
  65          */
  66         REGISTER_OFFSET,
  67         /**
  68          * base + extend(offset) [<< log2(memory_transfer_size)].
  69          */
  70         EXTENDED_REGISTER_OFFSET,
  71         /**
  72          * PC + imm21 (word aligned).
  73          */
  74         PC_LITERAL,
  75         /**
  76          * address = base. base is updated to base + imm9
  77          */
  78         IMMEDIATE_POST_INDEXED,
  79         /**
  80          * address = base + imm9. base is updated to base + imm9
  81          */
  82         IMMEDIATE_PRE_INDEXED,
  83     }
  84 
  85     private final Register base;
  86     private final Register offset;
  87     private final int immediate;
  88     /**
  89      * Should register offset be scaled or not.
  90      */
  91     private final boolean scaled;
  92     private final AArch64Assembler.ExtendType extendType;
  93     private final AddressingMode addressingMode;
  94 
  95     /**
  96      * General address generation mechanism. Accepted values for all parameters depend on the
  97      * addressingMode. Null is never accepted for a register, if an addressMode doesn't use a
  98      * register the register has to be the zero-register. extendType has to be null for every
  99      * addressingMode except EXTENDED_REGISTER_OFFSET.
 100      */
 101     public static AArch64Address createAddress(AddressingMode addressingMode, Register base, Register offset, int immediate, boolean isScaled, AArch64Assembler.ExtendType extendType) {
 102         return new AArch64Address(base, offset, immediate, isScaled, extendType, addressingMode);
 103     }
 104 
 105     /**
 106      * @param base may not be null or the zero-register.
 107      * @param imm9 Signed 9-bit immediate value.
 108      * @return an address specifying a post-indexed immediate address pointing to base. After
 109      *         ldr/str instruction, base is updated to point to base + imm9
 110      */
 111     public static AArch64Address createPostIndexedImmediateAddress(Register base, int imm9) {
 112         return new AArch64Address(base, zr, imm9, false, null, AddressingMode.IMMEDIATE_POST_INDEXED);
 113     }
 114 
 115     /**
 116      * @param base may not be null or the zero-register.
 117      * @param imm9 Signed 9-bit immediate value.
 118      * @return an address specifying a pre-indexed immediate address pointing to base + imm9. After
 119      *         ldr/str instruction, base is updated to point to base + imm9
 120      */
 121     public static AArch64Address createPreIndexedImmediateAddress(Register base, int imm9) {
 122         return new AArch64Address(base, zr, imm9, false, null, AddressingMode.IMMEDIATE_PRE_INDEXED);
 123     }
 124 
 125     /**
 126      * @param base may not be null or the zero-register.
 127      * @param imm12 Unsigned 12-bit immediate value. This is scaled by the word access size. This
 128      *            means if this address is used to load/store a word, the immediate is shifted by 2
 129      *            (log2Ceil(4)).
 130      * @return an address specifying a signed address of the form base + imm12 <<
 131      *         log2(memory_transfer_size).
 132      */
 133     public static AArch64Address createScaledImmediateAddress(Register base, int imm12) {
 134         return new AArch64Address(base, zr, imm12, true, null, AddressingMode.IMMEDIATE_SCALED);
 135     }
 136 
 137     /**
 138      * @param base may not be null or the zero-register.
 139      * @param imm9 Signed 9-bit immediate value.
 140      * @return an address specifying an unscaled immediate address of the form base + imm9
 141      */
 142     public static AArch64Address createUnscaledImmediateAddress(Register base, int imm9) {
 143         return new AArch64Address(base, zr, imm9, false, null, AddressingMode.IMMEDIATE_UNSCALED);
 144     }
 145 
 146     /**
 147      * @param base May not be null or the zero register.
 148      * @return an address specifying the address pointed to by base.
 149      */
 150     public static AArch64Address createBaseRegisterOnlyAddress(Register base) {
 151         return createRegisterOffsetAddress(base, zr, false);
 152     }
 153 
 154     /**
 155      * @param base may not be null or the zero-register.
 156      * @param offset Register specifying some offset, optionally scaled by the memory_transfer_size.
 157      *            May not be null or the stackpointer.
 158      * @param scaled Specifies whether offset should be scaled by memory_transfer_size or not.
 159      * @return an address specifying a register offset address of the form base + offset [<< log2
 160      *         (memory_transfer_size)]
 161      */
 162     public static AArch64Address createRegisterOffsetAddress(Register base, Register offset, boolean scaled) {
 163         return new AArch64Address(base, offset, 0, scaled, null, AddressingMode.REGISTER_OFFSET);
 164     }
 165 
 166     /**
 167      * @param base may not be null or the zero-register.
 168      * @param imm7 Signed 7-bit immediate value.
 169      * @return an address specifying an unscaled immediate address of the form base + imm7
 170      */
 171     public static AArch64Address createPairUnscaledImmediateAddress(Register base, int imm7) {
 172         return new AArch64Address(base, zr, imm7, false, null, AddressingMode.IMMEDIATE_UNSCALED);
 173     }
 174 
 175     /**
 176      * @param base may not be null or the zero-register.
 177      * @param offset Word register specifying some offset, optionally scaled by the
 178      *            memory_transfer_size. May not be null or the stackpointer.
 179      * @param scaled Specifies whether offset should be scaled by memory_transfer_size or not.
 180      * @param extendType Describes whether register is zero- or sign-extended. May not be null.
 181      * @return an address specifying an extended register offset of the form base +
 182      *         extendType(offset) [<< log2(memory_transfer_size)]
 183      */
 184     public static AArch64Address createExtendedRegisterOffsetAddress(Register base, Register offset, boolean scaled, AArch64Assembler.ExtendType extendType) {
 185         return new AArch64Address(base, offset, 0, scaled, extendType, AddressingMode.EXTENDED_REGISTER_OFFSET);
 186     }
 187 
 188     /**
 189      * @param imm21 Signed 21-bit offset, word aligned.
 190      * @return AArch64Address specifying a PC-literal address of the form PC + offset
 191      */
 192     public static AArch64Address createPcLiteralAddress(int imm21) {
 193         return new AArch64Address(zr, zr, imm21, false, null, AddressingMode.PC_LITERAL);
 194     }
 195 
 196     private AArch64Address(Register base, Register offset, int immediate, boolean scaled, AArch64Assembler.ExtendType extendType, AddressingMode addressingMode) {
 197         this.base = base;
 198         this.offset = offset;
 199         if ((addressingMode == AddressingMode.REGISTER_OFFSET || addressingMode == AddressingMode.EXTENDED_REGISTER_OFFSET) && offset.equals(zr)) {
 200             this.addressingMode = AddressingMode.BASE_REGISTER_ONLY;
 201         } else {
 202             this.addressingMode = addressingMode;
 203         }
 204         this.immediate = immediate;
 205         this.scaled = scaled;
 206         this.extendType = extendType;
 207         assert verify();
 208     }
 209 
 210     private boolean verify() {
 211         assert addressingMode != null;
 212         assert base.getRegisterCategory().equals(AArch64.CPU);
 213         assert offset.getRegisterCategory().equals(AArch64.CPU);
 214 
 215         switch (addressingMode) {
 216             case IMMEDIATE_SCALED:
 217                 assert !base.equals(zr);
 218                 assert offset.equals(zr);
 219                 assert extendType == null;
 220                 assert NumUtil.isUnsignedNbit(12, immediate);
 221                 break;
 222             case IMMEDIATE_UNSCALED:
 223                 assert !base.equals(zr);
 224                 assert offset.equals(zr);
 225                 assert extendType == null;
 226                 assert NumUtil.isSignedNbit(9, immediate);
 227                 break;
 228             case BASE_REGISTER_ONLY:
 229                 assert !base.equals(zr);
 230                 assert offset.equals(zr);
 231                 assert extendType == null;
 232                 assert immediate == 0;
 233                 break;
 234             case REGISTER_OFFSET:
 235                 assert !base.equals(zr);
 236                 assert offset.getRegisterCategory().equals(AArch64.CPU);
 237                 assert extendType == null;
 238                 assert immediate == 0;
 239                 break;
 240             case EXTENDED_REGISTER_OFFSET:
 241                 assert !base.equals(zr);
 242                 assert offset.getRegisterCategory().equals(AArch64.CPU);
 243                 assert (extendType == AArch64Assembler.ExtendType.SXTW || extendType == AArch64Assembler.ExtendType.UXTW);
 244                 assert immediate == 0;
 245                 break;
 246             case PC_LITERAL:
 247                 assert base.equals(zr);
 248                 assert offset.equals(zr);
 249                 assert extendType == null;
 250                 assert NumUtil.isSignedNbit(21, immediate);
 251                 assert ((immediate & 0x3) == 0);
 252                 break;
 253             case IMMEDIATE_POST_INDEXED:
 254             case IMMEDIATE_PRE_INDEXED:
 255                 assert !base.equals(zr);
 256                 assert offset.equals(zr);
 257                 assert extendType == null;
 258                 assert NumUtil.isSignedNbit(9, immediate);
 259                 break;
 260             default:
 261                 throw GraalError.shouldNotReachHere();
 262         }
 263 
 264         return true;
 265     }
 266 
 267     public Register getBase() {
 268         return base;
 269     }
 270 
 271     public Register getOffset() {
 272         return offset;
 273     }
 274 
 275     /**
 276      * @return immediate in correct representation for the given addressing mode. For example in
 277      *         case of <code>addressingMode ==IMMEDIATE_UNSCALED </code> the value will be returned
 278      *         as the 9-bit signed representation.
 279      */
 280     public int getImmediate() {
 281         switch (addressingMode) {
 282             case IMMEDIATE_UNSCALED:
 283             case IMMEDIATE_POST_INDEXED:
 284             case IMMEDIATE_PRE_INDEXED:
 285                 // 9-bit signed value
 286                 assert NumUtil.isSignedNbit(9, immediate);
 287                 return immediate & NumUtil.getNbitNumberInt(9);
 288             case IMMEDIATE_SCALED:
 289                 // Unsigned value can be returned as-is.
 290                 assert NumUtil.isUnsignedNbit(12, immediate);
 291                 return immediate;
 292             case PC_LITERAL:
 293                 // 21-bit signed value, but lower 2 bits are always 0 and are shifted out.
 294                 assert NumUtil.isSignedNbit(19, immediate >> 2);
 295                 return (immediate >> 2) & NumUtil.getNbitNumberInt(19);
 296             default:
 297                 throw GraalError.shouldNotReachHere("Should only be called for addressing modes that use immediate values.");
 298         }
 299     }
 300 
 301     /**
 302      * @return Raw immediate as a 32-bit signed value.
 303      */
 304     public int getImmediateRaw() {
 305         switch (addressingMode) {
 306             case IMMEDIATE_UNSCALED:
 307             case IMMEDIATE_SCALED:
 308             case IMMEDIATE_POST_INDEXED:
 309             case IMMEDIATE_PRE_INDEXED:
 310             case PC_LITERAL:
 311                 return immediate;
 312             default:
 313                 throw GraalError.shouldNotReachHere("Should only be called for addressing modes that use immediate values.");
 314         }
 315     }
 316 
 317     public boolean isScaled() {
 318         return scaled;
 319     }
 320 
 321     public AArch64Assembler.ExtendType getExtendType() {
 322         return extendType;
 323     }
 324 
 325     public AddressingMode getAddressingMode() {
 326         return addressingMode;
 327     }
 328 
 329     public String toString(int log2TransferSize) {
 330         int shiftVal = scaled ? log2TransferSize : 0;
 331         switch (addressingMode) {
 332             case IMMEDIATE_SCALED:
 333                 return String.format("[X%d, %d]", base.encoding, immediate << log2TransferSize);
 334             case IMMEDIATE_UNSCALED:
 335                 return String.format("[X%d, %d]", base.encoding, immediate);
 336             case BASE_REGISTER_ONLY:
 337                 return String.format("[X%d]", base.encoding);
 338             case EXTENDED_REGISTER_OFFSET:
 339                 if (shiftVal != 0) {
 340                     return String.format("[X%d, W%d, %s %d]", base.encoding, offset.encoding, extendType.name(), shiftVal);
 341                 } else {
 342                     return String.format("[X%d, W%d, %s]", base.encoding, offset.encoding, extendType.name());
 343                 }
 344             case REGISTER_OFFSET:
 345                 if (shiftVal != 0) {
 346                     return String.format("[X%d, X%d, LSL %d]", base.encoding, offset.encoding, shiftVal);
 347                 } else {
 348                     // LSL 0 may be optional, but still encoded differently so we always leave it
 349                     // off
 350                     return String.format("[X%d, X%d]", base.encoding, offset.encoding);
 351                 }
 352             case PC_LITERAL:
 353                 return String.format(".%s%d", immediate >= 0 ? "+" : "", immediate);
 354             case IMMEDIATE_POST_INDEXED:
 355                 return String.format("[X%d],%d", base.encoding, immediate);
 356             case IMMEDIATE_PRE_INDEXED:
 357                 return String.format("[X%d,%d]!", base.encoding, immediate);
 358             default:
 359                 throw GraalError.shouldNotReachHere();
 360         }
 361     }
 362 
 363     /**
 364      * Loads an address into Register r.
 365      *
 366      * @param masm the macro assembler.
 367      * @param r general purpose register. May not be null.
 368      */
 369     public void lea(AArch64MacroAssembler masm, Register r) {
 370         switch (addressingMode) {
 371             case IMMEDIATE_UNSCALED:
 372                 if (immediate == 0 && base.equals(r)) { // it's a nop
 373                     break;
 374                 }
 375                 masm.add(64, r, base, immediate);
 376                 break;
 377             case REGISTER_OFFSET:
 378                 masm.add(64, r, base, offset);
 379                 break;
 380             case PC_LITERAL: {
 381                 masm.mov(r, getImmediate());
 382                 break;
 383             }
 384             default:
 385                 throw GraalError.shouldNotReachHere();
 386         }
 387     }
 388 }