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