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      */
createAddress(AddressingMode addressingMode, Register base, Register offset, int immediate, boolean isScaled, AArch64Assembler.ExtendType extendType)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      */
createPostIndexedImmediateAddress(Register base, int imm9)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      */
createPreIndexedImmediateAddress(Register base, int imm9)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      */
createScaledImmediateAddress(Register base, int imm12)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      */
createUnscaledImmediateAddress(Register base, int imm9)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      */
createBaseRegisterOnlyAddress(Register base)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      */
createRegisterOffsetAddress(Register base, Register offset, boolean scaled)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      */
createPairUnscaledImmediateAddress(Register base, int imm7)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      */
createExtendedRegisterOffsetAddress(Register base, Register offset, boolean scaled, AArch64Assembler.ExtendType extendType)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      */
createPcLiteralAddress(int imm21)192     public static AArch64Address createPcLiteralAddress(int imm21) {
193         return new AArch64Address(zr, zr, imm21, false, null, AddressingMode.PC_LITERAL);
194     }
195 
AArch64Address(Register base, Register offset, int immediate, boolean scaled, AArch64Assembler.ExtendType extendType, AddressingMode addressingMode)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 
verify()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 
getBase()267     public Register getBase() {
268         return base;
269     }
270 
getOffset()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      */
getImmediate()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      */
getImmediateRaw()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 
isScaled()317     public boolean isScaled() {
318         return scaled;
319     }
320 
getExtendType()321     public AArch64Assembler.ExtendType getExtendType() {
322         return extendType;
323     }
324 
getAddressingMode()325     public AddressingMode getAddressingMode() {
326         return addressingMode;
327     }
328 
toString(int log2TransferSize)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      */
lea(AArch64MacroAssembler masm, Register r)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 }
389