1 /*
2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3  * Copyright (c) 2020, Arm Limited. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  */
24 
25 package jdk.vm.ci.code.test.aarch64;
26 
27 import jdk.vm.ci.aarch64.AArch64;
28 import jdk.vm.ci.aarch64.AArch64Kind;
29 import jdk.vm.ci.code.CallingConvention;
30 import jdk.vm.ci.code.CodeCacheProvider;
31 import jdk.vm.ci.code.DebugInfo;
32 import jdk.vm.ci.code.Register;
33 import jdk.vm.ci.code.RegisterValue;
34 import jdk.vm.ci.code.StackSlot;
35 import jdk.vm.ci.code.site.ConstantReference;
36 import jdk.vm.ci.code.site.DataSectionReference;
37 import jdk.vm.ci.code.test.TestAssembler;
38 import jdk.vm.ci.code.test.TestHotSpotVMConfig;
39 import jdk.vm.ci.hotspot.HotSpotCallingConventionType;
40 import jdk.vm.ci.hotspot.HotSpotConstant;
41 import jdk.vm.ci.hotspot.HotSpotForeignCallTarget;
42 import jdk.vm.ci.meta.AllocatableValue;
43 import jdk.vm.ci.meta.JavaKind;
44 import jdk.vm.ci.meta.VMConstant;
45 
46 public class AArch64TestAssembler extends TestAssembler {
47 
48     private static final Register scratchRegister = AArch64.rscratch1;
49     private static final Register doubleScratch = AArch64.v9;
50 
AArch64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config)51     public AArch64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config) {
52         super(codeCache, config,
53               16 /* initialFrameSize */, 16 /* stackAlignment */,
54               AArch64Kind.DWORD /* narrowOopKind */,
55               /* registers */
56               AArch64.r0, AArch64.r1, AArch64.r2, AArch64.r3,
57               AArch64.r4, AArch64.r5, AArch64.r6, AArch64.r7);
58     }
59 
f(int val, int msb, int lsb)60     private static int f(int val, int msb, int lsb) {
61         int nbits = msb - lsb + 1;
62         assert val >= 0;
63         assert val < (1 << nbits);
64         assert msb >= lsb;
65         return val << lsb;
66     }
67 
68     private static int f(Register r, int msb, int lsb) {
69         assert msb - lsb == 4;
70         return f(r.encoding, msb, lsb);
71     }
72 
73     private void emitNop() {
74         code.emitInt(0xd503201f);
75     }
76 
77     private void emitAdd(Register Rd, Register Rn, Register Rm) {
78         // ADD (shifted register)
79         code.emitInt(f(0b10001011000, 31, 21)
80                      | f(Rm, 20, 16)
81                      | f(0, 15, 10)
82                      | f(Rn, 9, 5)
83                      | f(Rd, 4, 0));
84     }
85 
86     private void emitAdd(Register Rd, Register Rn, int imm12) {
87         // ADD (immediate)
88         code.emitInt(f(0b1001000100, 31, 22)
89                      | f(imm12, 21, 10)
90                      | f(Rn, 9, 5)
91                      | f(Rd, 4, 0));
92     }
93 
94     private void emitSub(Register Rd, Register Rn, int imm12) {
95         // SUB (immediate)
96         code.emitInt(f(0b1101000100, 31, 22)
97                      | f(imm12, 21, 10)
98                      | f(Rn, 9, 5)
99                      | f(Rd, 4, 0));
100     }
101 
102     private void emitSub(Register Rd, Register Rn, Register Rm) {
103         // SUB (extended register)
104         code.emitInt(f(0b11001011001, 31, 21)
105                      | f(Rm, 20, 16)
106                      | f(0b011000, 15, 10)
107                      | f(Rn, 9, 5)
108                      | f(Rd, 4, 0));
109     }
110 
111     private void emitMov(Register Rd, Register Rm) {
112         // MOV (register)
113         code.emitInt(f(0b10101010000, 31, 21)
114                      | f(Rm, 20, 16)
115                      | f(0, 15, 10)
116                      | f(AArch64.zr, 9, 5)
117                      | f(Rd, 4, 0));
118     }
119 
120     private void emitMovz(Register Rd, int imm16, int shift) {
121         // MOVZ
122         int hw = 0;
123         switch (shift) {
124             case 0:  hw = 0; break;
125             case 16: hw = 1; break;
126             case 32: hw = 2; break;
127             case 48: hw = 3; break;
128             default: throw new IllegalArgumentException();
129         }
130         code.emitInt(f(0b110100101, 31, 23)
131                      | f(hw, 22, 21)
132                      | f(imm16, 20, 5)
133                      | f(Rd, 4, 0));
134     }
135 
136     private void emitMovk(Register Rd, int imm16, int shift) {
137         // MOVK
138         int hw = 0;
139         switch (shift) {
140             case 0:  hw = 0; break;
141             case 16: hw = 1; break;
142             case 32: hw = 2; break;
143             case 48: hw = 3; break;
144             default: throw new IllegalArgumentException();
145         }
146         code.emitInt(f(0b111100101, 31, 23)
147                      | f(hw, 22, 21)
148                      | f(imm16, 20, 5)
149                      | f(Rd, 4, 0));
150     }
151 
152     private void emitShiftLeft(Register Rd, Register Rn, int shift) {
153         // LSL (immediate)
154         code.emitInt(f(0b1101001101, 31, 22)
155                      | f(-shift & 0b111111, 21, 16)
156                      | f(63 - shift, 15, 10)
157                      | f(Rn, 9, 5)
158                      | f(Rd, 4, 0));
159     }
160 
161     private void emitLoadRegister(Register Rt, AArch64Kind kind, int offset) {
162         // LDR (literal)
163         int opc = 0;
164         switch (kind) {
165             case DWORD: opc = 0; break;
166             case QWORD: opc = 1; break;
167             default: throw new IllegalArgumentException();
168         }
169         code.emitInt(f(opc, 31, 30)
170                      | f(0b011000, 29, 24)
171                      | f(offset, 23, 5)
172                      | f(Rt, 4, 0));
173     }
174 
175     private void emitLoadRegister(Register Rt, AArch64Kind kind, Register Rn, int offset) {
176         // LDR (immediate)
177         assert offset >= 0;
178         int size = 0;
179         switch (kind) {
180             case DWORD: size = 0b10; break;
181             case QWORD: size = 0b11; break;
182             default: throw new IllegalArgumentException();
183         }
184         code.emitInt(f(size, 31, 30)
185                      | f(0b11100101, 29, 22)
186                      | f(offset >> size, 21, 10)
187                      | f(Rn, 9, 5)
188                      | f(Rt, 4, 0));
189     }
190 
191     private void emitStoreRegister(Register Rt, AArch64Kind kind, Register Rn, int offset) {
192         // STR (immediate)
193         assert offset >= 0;
194         int size = 0, fp = 0;
195         switch (kind) {
196             case DWORD: size = 0b10; fp = 0; break;
197             case QWORD: size = 0b11; fp = 0; break;
198             case SINGLE: size = 0b10; fp = 1; break;
199             case DOUBLE: size = 0b11; fp = 1; break;
200             default: throw new IllegalArgumentException();
201         }
202         code.emitInt(f(size, 31, 30)
203                      | f(0b111, 29, 27)
204                      | f(fp, 26, 26)
205                      | f(0b0100, 25, 22)
206                      | f(offset >> size, 21, 10)
207                      | f(Rn, 9, 5)
208                      | f(Rt, 4, 0));
209     }
210 
emitBlr(Register Rn)211     private void emitBlr(Register Rn) {
212         // BLR
213         code.emitInt(f(0b1101011000111111000000, 31, 10)
214                      | f(Rn, 9, 5)
215                      | f(0, 4, 0));
216     }
217 
emitFmov(Register Rd, AArch64Kind kind, Register Rn)218     private void emitFmov(Register Rd, AArch64Kind kind, Register Rn) {
219         // FMOV (general)
220         int ftype = 0, sf = 0;
221         switch (kind) {
222             case SINGLE: sf = 0; ftype = 0b00; break;
223             case DOUBLE: sf = 1; ftype = 0b01; break;
224             default: throw new IllegalArgumentException();
225         }
226         code.emitInt(f(sf, 31, 31)
227                      | f(0b0011110, 30, 24)
228                      | f(ftype, 23, 22)
229                      | f(0b100111, 21, 16)
230                      | f(0, 15, 10)
231                      | f(Rn, 9, 5)
232                      | f(Rd, 4, 0));
233     }
234 
235     @Override
emitGrowStack(int size)236     public void emitGrowStack(int size) {
237         assert size % 16 == 0;
238         if (size > -4096 && size < 0) {
239             emitAdd(AArch64.sp, AArch64.sp, -size);
240         } else if (size == 0) {
241             // No-op
242         } else if (size < 4096) {
243             emitSub(AArch64.sp, AArch64.sp, size);
244         } else if (size < 65535) {
245             emitMovz(scratchRegister, size & 0xffff, 0);
246             emitMovk(scratchRegister, (size >> 16) & 0xffff, 16);
247             emitSub(AArch64.sp, AArch64.sp, scratchRegister);
248         } else {
249             throw new IllegalArgumentException();
250         }
251     }
252 
253     @Override
emitPrologue()254     public void emitPrologue() {
255         // Must be patchable by NativeJump::patch_verified_entry
256         emitNop();
257         code.emitInt(0xa9be7bfd);  // stp x29, x30, [sp, #-32]!
258         code.emitInt(0x910003fd);  // mov x29, sp
259 
260         setDeoptRescueSlot(newStackSlot(AArch64Kind.QWORD));
261     }
262 
263     @Override
emitEpilogue()264     public void emitEpilogue() {
265         recordMark(config.MARKID_DEOPT_HANDLER_ENTRY);
266         recordCall(new HotSpotForeignCallTarget(config.handleDeoptStub), 4*4, true, null);
267         emitCall(0xdeaddeaddeadL);
268     }
269 
270     @Override
emitCallPrologue(CallingConvention cc, Object... prim)271     public void emitCallPrologue(CallingConvention cc, Object... prim) {
272         emitGrowStack(cc.getStackSize());
273         frameSize += cc.getStackSize();
274         AllocatableValue[] args = cc.getArguments();
275         for (int i = 0; i < args.length; i++) {
276             emitLoad(args[i], prim[i]);
277         }
278     }
279 
280     @Override
emitCallEpilogue(CallingConvention cc)281     public void emitCallEpilogue(CallingConvention cc) {
282         emitGrowStack(-cc.getStackSize());
283         frameSize -= cc.getStackSize();
284     }
285 
286     @Override
emitCall(long addr)287     public void emitCall(long addr) {
288         emitLoadPointer48(scratchRegister, addr);
289         emitBlr(scratchRegister);
290     }
291 
292     @Override
emitLoad(AllocatableValue av, Object prim)293     public void emitLoad(AllocatableValue av, Object prim) {
294         if (av instanceof RegisterValue) {
295             Register reg = ((RegisterValue) av).getRegister();
296             if (prim instanceof Float) {
297                 emitLoadFloat(reg, (Float) prim);
298             } else if (prim instanceof Double) {
299                 emitLoadDouble(reg, (Double) prim);
300             } else if (prim instanceof Integer) {
301                 emitLoadInt(reg, (Integer) prim);
302             } else if (prim instanceof Long) {
303                 emitLoadLong(reg, (Long) prim);
304             }
305         } else if (av instanceof StackSlot) {
306             StackSlot slot = (StackSlot) av;
307             if (prim instanceof Float) {
308                 emitFloatToStack(slot, emitLoadFloat(doubleScratch, (Float) prim));
309             } else if (prim instanceof Double) {
310                 emitDoubleToStack(slot, emitLoadDouble(doubleScratch, (Double) prim));
311             } else if (prim instanceof Integer) {
312                 emitIntToStack(slot, emitLoadInt(scratchRegister, (Integer) prim));
313             } else if (prim instanceof Long) {
314                 emitLongToStack(slot, emitLoadLong(scratchRegister, (Long) prim));
315             } else {
316                 assert false : "Unimplemented";
317             }
318         } else {
319             throw new IllegalArgumentException("Unknown value " + av);
320         }
321     }
322 
emitLoadPointer32(Register ret, long addr)323     private void emitLoadPointer32(Register ret, long addr) {
324         long a = addr;
325         long al = a;
326         a >>= 16;
327         long ah = a;
328         a >>= 16;
329         assert a == 0 : "invalid pointer" + Long.toHexString(addr);
330         // Set upper 16 bits first. See MacroAssembler::patch_oop().
331         emitMovz(ret, ((int)ah & 0xffff), 16);
332         emitMovk(ret, ((int)al & 0xffff), 0);
333     }
334 
emitLoadPointer48(Register ret, long addr)335     private void emitLoadPointer48(Register ret, long addr) {
336         // 48-bit VA
337         long a = addr;
338         emitMovz(ret, ((int)a & 0xffff), 0);
339         a >>= 16;
340         emitMovk(ret, ((int)a & 0xffff), 16);
341         a >>= 16;
342         emitMovk(ret, ((int)a & 0xffff), 32);
343         a >>= 16;
344         assert a == 0 : "invalid pointer" + Long.toHexString(addr);
345     }
346 
347     @Override
emitLoadPointer(HotSpotConstant c)348     public Register emitLoadPointer(HotSpotConstant c) {
349         recordDataPatchInCode(new ConstantReference((VMConstant) c));
350 
351         Register ret = newRegister();
352         if (c.isCompressed()) {
353             emitLoadPointer32(ret, 0xdeaddeadL);
354         } else {
355             emitLoadPointer48(ret, 0xdeaddeaddeadL);
356         }
357         return ret;
358     }
359 
360     @Override
emitLoadPointer(Register b, int offset)361     public Register emitLoadPointer(Register b, int offset) {
362         Register ret = newRegister();
363         emitLoadRegister(ret, AArch64Kind.QWORD, b, offset);
364         return ret;
365     }
366 
367     @Override
emitLoadNarrowPointer(DataSectionReference ref)368     public Register emitLoadNarrowPointer(DataSectionReference ref) {
369         recordDataPatchInCode(ref);
370 
371         Register ret = newRegister();
372         emitLoadRegister(ret, AArch64Kind.DWORD, 0xdead);
373         return ret;
374     }
375 
376     @Override
emitLoadPointer(DataSectionReference ref)377     public Register emitLoadPointer(DataSectionReference ref) {
378         recordDataPatchInCode(ref);
379 
380         Register ret = newRegister();
381         emitLoadRegister(ret, AArch64Kind.QWORD, 0xdead);
382         return ret;
383     }
384 
emitLoadDouble(Register reg, double c)385     private Register emitLoadDouble(Register reg, double c) {
386         DataSectionReference ref = new DataSectionReference();
387         ref.setOffset(data.position());
388         data.emitDouble(c);
389 
390         recordDataPatchInCode(ref);
391         emitLoadRegister(scratchRegister, AArch64Kind.QWORD, 0xdead);
392         emitFmov(reg, AArch64Kind.DOUBLE, scratchRegister);
393         return reg;
394     }
395 
emitLoadFloat(Register reg, float c)396     private Register emitLoadFloat(Register reg, float c) {
397         DataSectionReference ref = new DataSectionReference();
398         ref.setOffset(data.position());
399         data.emitFloat(c);
400 
401         recordDataPatchInCode(ref);
402         emitLoadRegister(scratchRegister, AArch64Kind.DWORD, 0xdead);
403         emitFmov(reg, AArch64Kind.SINGLE, scratchRegister);
404         return reg;
405     }
406 
407     @Override
emitLoadFloat(float c)408     public Register emitLoadFloat(float c) {
409         Register ret = AArch64.v0;
410         return emitLoadFloat(ret, c);
411     }
412 
emitLoadLong(Register reg, long c)413     private Register emitLoadLong(Register reg, long c) {
414         emitMovz(reg, (int)(c & 0xffff), 0);
415         emitMovk(reg, (int)((c >> 16) & 0xffff), 16);
416         emitMovk(reg, (int)((c >> 32) & 0xffff), 32);
417         emitMovk(reg, (int)((c >> 48) & 0xffff), 48);
418         return reg;
419     }
420 
421     @Override
emitLoadLong(long c)422     public Register emitLoadLong(long c) {
423         Register ret = newRegister();
424         return emitLoadLong(ret, c);
425     }
426 
emitLoadInt(Register reg, int c)427     private Register emitLoadInt(Register reg, int c) {
428         emitMovz(reg, (int)(c & 0xffff), 0);
429         emitMovk(reg, (int)((c >> 16) & 0xffff), 16);
430         return reg;
431     }
432 
433     @Override
emitLoadInt(int c)434     public Register emitLoadInt(int c) {
435         Register ret = newRegister();
436         return emitLoadInt(ret, c);
437     }
438 
439     @Override
emitIntArg0()440     public Register emitIntArg0() {
441         return codeCache.getRegisterConfig()
442             .getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int)
443             .get(0);
444     }
445 
446     @Override
emitIntArg1()447     public Register emitIntArg1() {
448         return codeCache.getRegisterConfig()
449             .getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int)
450             .get(1);
451     }
452 
453     @Override
emitIntAdd(Register a, Register b)454     public Register emitIntAdd(Register a, Register b) {
455         emitAdd(a, a, b);
456         return a;
457     }
458 
459     @Override
emitTrap(DebugInfo info)460     public void emitTrap(DebugInfo info) {
461         // Dereference null pointer
462         emitMovz(scratchRegister, 0, 0);
463         recordImplicitException(info);
464         emitLoadRegister(AArch64.zr, AArch64Kind.QWORD, scratchRegister, 0);
465     }
466 
467     @Override
emitIntRet(Register a)468     public void emitIntRet(Register a) {
469         emitMov(AArch64.r0, a);
470         code.emitInt(0x910003bf);  // mov sp, x29
471         code.emitInt(0xa8c27bfd);  // ldp x29, x30, [sp], #32
472         code.emitInt(0xd65f03c0);  // ret
473     }
474 
475     @Override
emitFloatRet(Register a)476     public void emitFloatRet(Register a) {
477         assert a == AArch64.v0 : "Unimplemented move " + a;
478         code.emitInt(0x910003bf);  // mov sp, x29
479         code.emitInt(0xa8c27bfd);  // ldp x29, x30, [sp], #32
480         code.emitInt(0xd65f03c0);  // ret
481     }
482 
483     @Override
emitPointerRet(Register a)484     public void emitPointerRet(Register a) {
485         emitIntRet(a);
486     }
487 
488     @Override
emitPointerToStack(Register a)489     public StackSlot emitPointerToStack(Register a) {
490         return emitLongToStack(a);
491     }
492 
493     @Override
emitNarrowPointerToStack(Register a)494     public StackSlot emitNarrowPointerToStack(Register a) {
495         return emitIntToStack(a);
496     }
497 
498     @Override
emitUncompressPointer(Register compressed, long base, int shift)499     public Register emitUncompressPointer(Register compressed, long base, int shift) {
500         if (shift > 0) {
501             emitShiftLeft(compressed, compressed, shift);
502         }
503 
504         if (base != 0) {
505             emitLoadLong(scratchRegister, base);
506             emitAdd(compressed, compressed, scratchRegister);
507         }
508 
509         return compressed;
510     }
511 
emitDoubleToStack(StackSlot slot, Register a)512     private StackSlot emitDoubleToStack(StackSlot slot, Register a) {
513         emitStoreRegister(a, AArch64Kind.DOUBLE, AArch64.sp, slot.getOffset(frameSize));
514         return slot;
515     }
516 
517     @Override
emitDoubleToStack(Register a)518     public StackSlot emitDoubleToStack(Register a) {
519         StackSlot ret = newStackSlot(AArch64Kind.DOUBLE);
520         return emitDoubleToStack(ret, a);
521     }
522 
emitFloatToStack(StackSlot slot, Register a)523     private StackSlot emitFloatToStack(StackSlot slot, Register a) {
524         emitStoreRegister(a, AArch64Kind.SINGLE, AArch64.sp, slot.getOffset(frameSize));
525         return slot;
526     }
527 
528     @Override
emitFloatToStack(Register a)529     public StackSlot emitFloatToStack(Register a) {
530         StackSlot ret = newStackSlot(AArch64Kind.SINGLE);
531         return emitFloatToStack(ret, a);
532     }
533 
emitIntToStack(StackSlot slot, Register a)534     private StackSlot emitIntToStack(StackSlot slot, Register a) {
535         emitStoreRegister(a, AArch64Kind.DWORD, AArch64.sp, slot.getOffset(frameSize));
536         return slot;
537     }
538 
539     @Override
emitIntToStack(Register a)540     public StackSlot emitIntToStack(Register a) {
541         StackSlot ret = newStackSlot(AArch64Kind.DWORD);
542         return emitIntToStack(ret, a);
543     }
544 
emitLongToStack(StackSlot slot, Register a)545     private StackSlot emitLongToStack(StackSlot slot, Register a) {
546         emitStoreRegister(a, AArch64Kind.QWORD, AArch64.sp, slot.getOffset(frameSize));
547         return slot;
548     }
549 
550     @Override
emitLongToStack(Register a)551     public StackSlot emitLongToStack(Register a) {
552         StackSlot ret = newStackSlot(AArch64Kind.QWORD);
553         return emitLongToStack(ret, a);
554     }
555 
556 }
557