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 */
23package org.graalvm.compiler.asm.aarch64;
24
25import static jdk.vm.ci.aarch64.AArch64.zr;
26
27import org.graalvm.compiler.asm.AbstractAddress;
28import org.graalvm.compiler.core.common.NumUtil;
29import org.graalvm.compiler.debug.GraalError;
30
31import jdk.vm.ci.aarch64.AArch64;
32import 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 */
44public 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 9-bit 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                assert NumUtil.isSignedNbit(9, immediate);
286                return immediate & NumUtil.getNbitNumberInt(9);
287            case IMMEDIATE_SCALED:
288                // Unsigned value can be returned as-is.
289                assert NumUtil.isUnsignedNbit(12, immediate);
290                return immediate;
291            case PC_LITERAL:
292                // 21-bit signed value, but lower 2 bits are always 0 and are shifted out.
293                assert NumUtil.isSignedNbit(19, immediate >> 2);
294                return (immediate >> 2) & NumUtil.getNbitNumberInt(19);
295            default:
296                throw GraalError.shouldNotReachHere("Should only be called for addressing modes that use immediate values.");
297        }
298    }
299
300    /**
301     * @return Raw immediate as a 32-bit signed value.
302     */
303    public int getImmediateRaw() {
304        switch (addressingMode) {
305            case IMMEDIATE_UNSCALED:
306            case IMMEDIATE_SCALED:
307            case IMMEDIATE_POST_INDEXED:
308            case IMMEDIATE_PRE_INDEXED:
309            case PC_LITERAL:
310                return immediate;
311            default:
312                throw GraalError.shouldNotReachHere("Should only be called for addressing modes that use immediate values.");
313        }
314    }
315
316    public boolean isScaled() {
317        return scaled;
318    }
319
320    public AArch64Assembler.ExtendType getExtendType() {
321        return extendType;
322    }
323
324    public AddressingMode getAddressingMode() {
325        return addressingMode;
326    }
327
328    public String toString(int log2TransferSize) {
329        int shiftVal = scaled ? log2TransferSize : 0;
330        switch (addressingMode) {
331            case IMMEDIATE_SCALED:
332                return String.format("[X%d, %d]", base.encoding, immediate << log2TransferSize);
333            case IMMEDIATE_UNSCALED:
334                return String.format("[X%d, %d]", base.encoding, immediate);
335            case BASE_REGISTER_ONLY:
336                return String.format("[X%d]", base.encoding);
337            case EXTENDED_REGISTER_OFFSET:
338                if (shiftVal != 0) {
339                    return String.format("[X%d, W%d, %s %d]", base.encoding, offset.encoding, extendType.name(), shiftVal);
340                } else {
341                    return String.format("[X%d, W%d, %s]", base.encoding, offset.encoding, extendType.name());
342                }
343            case REGISTER_OFFSET:
344                if (shiftVal != 0) {
345                    return String.format("[X%d, X%d, LSL %d]", base.encoding, offset.encoding, shiftVal);
346                } else {
347                    // LSL 0 may be optional, but still encoded differently so we always leave it
348                    // off
349                    return String.format("[X%d, X%d]", base.encoding, offset.encoding);
350                }
351            case PC_LITERAL:
352                return String.format(".%s%d", immediate >= 0 ? "+" : "", immediate);
353            case IMMEDIATE_POST_INDEXED:
354                return String.format("[X%d],%d", base.encoding, immediate);
355            case IMMEDIATE_PRE_INDEXED:
356                return String.format("[X%d,%d]!", base.encoding, immediate);
357            default:
358                throw GraalError.shouldNotReachHere();
359        }
360    }
361
362    /**
363     * Loads an address into Register r.
364     *
365     * @param masm the macro assembler.
366     * @param r general purpose register. May not be null.
367     */
368    public void lea(AArch64MacroAssembler masm, Register r) {
369        switch (addressingMode) {
370            case IMMEDIATE_UNSCALED:
371                if (immediate == 0 && base.equals(r)) { // it's a nop
372                    break;
373                }
374                masm.add(64, r, base, immediate);
375                break;
376            case REGISTER_OFFSET:
377                masm.add(64, r, base, offset);
378                break;
379            case PC_LITERAL: {
380                masm.mov(r, getImmediate());
381                break;
382            }
383            default:
384                throw GraalError.shouldNotReachHere();
385        }
386    }
387}
388