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), 5, true, null);
267         code.emitInt(0x94000000);  // bl <imm26>
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         emitLoadLong(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 
323     @Override
emitLoadPointer(HotSpotConstant c)324     public Register emitLoadPointer(HotSpotConstant c) {
325         recordDataPatchInCode(new ConstantReference((VMConstant) c));
326 
327         Register ret = newRegister();
328         if (c.isCompressed()) {
329             // Set upper 16 bits first. See MacroAssembler::patch_oop().
330             emitMovz(ret, 0xdead, 16);
331             emitMovk(ret, 0xdead, 0);
332         } else {
333             // 48-bit VA
334             emitMovz(ret, 0xdead, 0);
335             emitMovk(ret, 0xdead, 16);
336             emitMovk(ret, 0xdead, 32);
337         }
338         return ret;
339     }
340 
341     @Override
emitLoadPointer(Register b, int offset)342     public Register emitLoadPointer(Register b, int offset) {
343         Register ret = newRegister();
344         emitLoadRegister(ret, AArch64Kind.QWORD, b, offset);
345         return ret;
346     }
347 
348     @Override
emitLoadNarrowPointer(DataSectionReference ref)349     public Register emitLoadNarrowPointer(DataSectionReference ref) {
350         recordDataPatchInCode(ref);
351 
352         Register ret = newRegister();
353         emitLoadRegister(ret, AArch64Kind.DWORD, 0xdead);
354         return ret;
355     }
356 
357     @Override
emitLoadPointer(DataSectionReference ref)358     public Register emitLoadPointer(DataSectionReference ref) {
359         recordDataPatchInCode(ref);
360 
361         Register ret = newRegister();
362         emitLoadRegister(ret, AArch64Kind.QWORD, 0xdead);
363         return ret;
364     }
365 
emitLoadDouble(Register reg, double c)366     private Register emitLoadDouble(Register reg, double c) {
367         DataSectionReference ref = new DataSectionReference();
368         ref.setOffset(data.position());
369         data.emitDouble(c);
370 
371         recordDataPatchInCode(ref);
372         emitLoadRegister(scratchRegister, AArch64Kind.QWORD, 0xdead);
373         emitFmov(reg, AArch64Kind.DOUBLE, scratchRegister);
374         return reg;
375     }
376 
emitLoadFloat(Register reg, float c)377     private Register emitLoadFloat(Register reg, float c) {
378         DataSectionReference ref = new DataSectionReference();
379         ref.setOffset(data.position());
380         data.emitFloat(c);
381 
382         recordDataPatchInCode(ref);
383         emitLoadRegister(scratchRegister, AArch64Kind.DWORD, 0xdead);
384         emitFmov(reg, AArch64Kind.SINGLE, scratchRegister);
385         return reg;
386     }
387 
388     @Override
emitLoadFloat(float c)389     public Register emitLoadFloat(float c) {
390         Register ret = AArch64.v0;
391         return emitLoadFloat(ret, c);
392     }
393 
emitLoadLong(Register reg, long c)394     private Register emitLoadLong(Register reg, long c) {
395         emitMovz(reg, (int)(c & 0xffff), 0);
396         emitMovk(reg, (int)((c >> 16) & 0xffff), 16);
397         emitMovk(reg, (int)((c >> 32) & 0xffff), 32);
398         emitMovk(reg, (int)((c >> 48) & 0xffff), 48);
399         return reg;
400     }
401 
402     @Override
emitLoadLong(long c)403     public Register emitLoadLong(long c) {
404         Register ret = newRegister();
405         return emitLoadLong(ret, c);
406     }
407 
emitLoadInt(Register reg, int c)408     private Register emitLoadInt(Register reg, int c) {
409         emitMovz(reg, (int)(c & 0xffff), 0);
410         emitMovk(reg, (int)((c >> 16) & 0xffff), 16);
411         return reg;
412     }
413 
414     @Override
emitLoadInt(int c)415     public Register emitLoadInt(int c) {
416         Register ret = newRegister();
417         return emitLoadInt(ret, c);
418     }
419 
420     @Override
emitIntArg0()421     public Register emitIntArg0() {
422         return codeCache.getRegisterConfig()
423             .getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int)
424             .get(0);
425     }
426 
427     @Override
emitIntArg1()428     public Register emitIntArg1() {
429         return codeCache.getRegisterConfig()
430             .getCallingConventionRegisters(HotSpotCallingConventionType.JavaCall, JavaKind.Int)
431             .get(1);
432     }
433 
434     @Override
emitIntAdd(Register a, Register b)435     public Register emitIntAdd(Register a, Register b) {
436         emitAdd(a, a, b);
437         return a;
438     }
439 
440     @Override
emitTrap(DebugInfo info)441     public void emitTrap(DebugInfo info) {
442         // Dereference null pointer
443         emitMovz(scratchRegister, 0, 0);
444         recordImplicitException(info);
445         emitLoadRegister(AArch64.zr, AArch64Kind.QWORD, scratchRegister, 0);
446     }
447 
448     @Override
emitIntRet(Register a)449     public void emitIntRet(Register a) {
450         emitMov(AArch64.r0, a);
451         code.emitInt(0x910003bf);  // mov sp, x29
452         code.emitInt(0xa8c27bfd);  // ldp x29, x30, [sp], #32
453         code.emitInt(0xd65f03c0);  // ret
454     }
455 
456     @Override
emitFloatRet(Register a)457     public void emitFloatRet(Register a) {
458         assert a == AArch64.v0 : "Unimplemented move " + a;
459         code.emitInt(0x910003bf);  // mov sp, x29
460         code.emitInt(0xa8c27bfd);  // ldp x29, x30, [sp], #32
461         code.emitInt(0xd65f03c0);  // ret
462     }
463 
464     @Override
emitPointerRet(Register a)465     public void emitPointerRet(Register a) {
466         emitIntRet(a);
467     }
468 
469     @Override
emitPointerToStack(Register a)470     public StackSlot emitPointerToStack(Register a) {
471         return emitLongToStack(a);
472     }
473 
474     @Override
emitNarrowPointerToStack(Register a)475     public StackSlot emitNarrowPointerToStack(Register a) {
476         return emitIntToStack(a);
477     }
478 
479     @Override
emitUncompressPointer(Register compressed, long base, int shift)480     public Register emitUncompressPointer(Register compressed, long base, int shift) {
481         if (shift > 0) {
482             emitShiftLeft(compressed, compressed, shift);
483         }
484 
485         if (base != 0) {
486             emitLoadLong(scratchRegister, base);
487             emitAdd(compressed, compressed, scratchRegister);
488         }
489 
490         return compressed;
491     }
492 
emitDoubleToStack(StackSlot slot, Register a)493     private StackSlot emitDoubleToStack(StackSlot slot, Register a) {
494         emitStoreRegister(a, AArch64Kind.DOUBLE, AArch64.sp, slot.getOffset(frameSize));
495         return slot;
496     }
497 
498     @Override
emitDoubleToStack(Register a)499     public StackSlot emitDoubleToStack(Register a) {
500         StackSlot ret = newStackSlot(AArch64Kind.DOUBLE);
501         return emitDoubleToStack(ret, a);
502     }
503 
emitFloatToStack(StackSlot slot, Register a)504     private StackSlot emitFloatToStack(StackSlot slot, Register a) {
505         emitStoreRegister(a, AArch64Kind.SINGLE, AArch64.sp, slot.getOffset(frameSize));
506         return slot;
507     }
508 
509     @Override
emitFloatToStack(Register a)510     public StackSlot emitFloatToStack(Register a) {
511         StackSlot ret = newStackSlot(AArch64Kind.SINGLE);
512         return emitFloatToStack(ret, a);
513     }
514 
emitIntToStack(StackSlot slot, Register a)515     private StackSlot emitIntToStack(StackSlot slot, Register a) {
516         emitStoreRegister(a, AArch64Kind.DWORD, AArch64.sp, slot.getOffset(frameSize));
517         return slot;
518     }
519 
520     @Override
emitIntToStack(Register a)521     public StackSlot emitIntToStack(Register a) {
522         StackSlot ret = newStackSlot(AArch64Kind.DWORD);
523         return emitIntToStack(ret, a);
524     }
525 
emitLongToStack(StackSlot slot, Register a)526     private StackSlot emitLongToStack(StackSlot slot, Register a) {
527         emitStoreRegister(a, AArch64Kind.QWORD, AArch64.sp, slot.getOffset(frameSize));
528         return slot;
529     }
530 
531     @Override
emitLongToStack(Register a)532     public StackSlot emitLongToStack(Register a) {
533         StackSlot ret = newStackSlot(AArch64Kind.QWORD);
534         return emitLongToStack(ret, a);
535     }
536 
537 }
538