1/*
2 * Copyright (c) 2015, 2016, 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
24package jdk.vm.ci.code.test.sparc;
25
26import jdk.vm.ci.code.CallingConvention;
27import jdk.vm.ci.code.CodeCacheProvider;
28import jdk.vm.ci.code.DebugInfo;
29import jdk.vm.ci.code.Register;
30import jdk.vm.ci.code.Register.RegisterCategory;
31import jdk.vm.ci.code.RegisterValue;
32import jdk.vm.ci.code.StackSlot;
33import jdk.vm.ci.code.site.ConstantReference;
34import jdk.vm.ci.code.site.DataSectionReference;
35import jdk.vm.ci.code.test.TestAssembler;
36import jdk.vm.ci.code.test.TestHotSpotVMConfig;
37import jdk.vm.ci.hotspot.HotSpotCallingConventionType;
38import jdk.vm.ci.hotspot.HotSpotCompiledCode;
39import jdk.vm.ci.hotspot.HotSpotConstant;
40import jdk.vm.ci.hotspot.HotSpotForeignCallTarget;
41import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
42import jdk.vm.ci.meta.AllocatableValue;
43import jdk.vm.ci.meta.JavaKind;
44import jdk.vm.ci.meta.VMConstant;
45import jdk.vm.ci.sparc.SPARC;
46import jdk.vm.ci.sparc.SPARCKind;
47
48public class SPARCTestAssembler extends TestAssembler {
49
50    private static final int MASK13 = (1 << 13) - 1;
51    private static final Register scratchRegister = SPARC.g5;
52    private static final Register floatScratch = SPARC.f30;
53    private static final Register doubleScratch = SPARC.d62;
54
55    public SPARCTestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config) {
56        super(codeCache, config, 0, 16, SPARCKind.WORD, SPARC.l0, SPARC.l1, SPARC.l2, SPARC.l3, SPARC.l4, SPARC.l5, SPARC.l6, SPARC.l7);
57    }
58
59    private void emitOp2(Register rd, int op2, int imm22) {
60        assert isSimm(imm22, 22);
61        code.emitInt((0b00 << 30) | (rd.encoding << 25) | (op2 << 22) | imm22);
62    }
63
64    private void emitOp3(int op, Register rd, int op3, Register rs1, Register rs2) {
65        code.emitInt((op << 30) | (rd.encoding << 25) | (op3 << 19) | (rs1.encoding << 14) | rs2.encoding);
66    }
67
68    private void emitOp3(int op, Register rd, int op3, Register rs1, int simm13) {
69        assert isSimm(simm13, 13);
70        code.emitInt((op << 30) | (rd.encoding << 25) | (op3 << 19) | (rs1.encoding << 14) | (1 << 13) | (simm13 & MASK13));
71    }
72
73    private void emitNop() {
74        code.emitInt(1 << 24);
75    }
76
77    /**
78     * Minimum value for signed immediate ranges.
79     */
80    public static long minSimm(long nbits) {
81        return -(1L << (nbits - 1));
82    }
83
84    /**
85     * Maximum value for signed immediate ranges.
86     */
87    public static long maxSimm(long nbits) {
88        return (1L << (nbits - 1)) - 1;
89    }
90
91    /**
92     * Test if imm is within signed immediate range for nbits.
93     */
94    public static boolean isSimm(long imm, int nbits) {
95        return minSimm(nbits) <= imm && imm <= maxSimm(nbits);
96    }
97
98    @Override
99    public void emitPrologue() {
100        // SAVE sp, -128, sp
101        emitOp3(0b10, SPARC.sp, 0b111100, SPARC.sp, -SPARC.REGISTER_SAFE_AREA_SIZE);
102        setDeoptRescueSlot(newStackSlot(SPARCKind.XWORD));
103    }
104
105    @Override
106    public void emitEpilogue() {
107        recordMark(config.MARKID_DEOPT_HANDLER_ENTRY);
108        recordCall(new HotSpotForeignCallTarget(config.handleDeoptStub), 4, true, null);
109        code.emitInt(1 << 30); // CALL
110    }
111
112    @Override
113    public HotSpotCompiledCode finish(HotSpotResolvedJavaMethod method) {
114        frameSize += SPARC.REGISTER_SAFE_AREA_SIZE;
115        return super.finish(method);
116    }
117
118    @Override
119    public void emitGrowStack(int size) {
120        frameSize += size;
121        if (isSimm(size, 13)) {
122            emitOp3(0b10, SPARC.sp, 0b000100, SPARC.sp, size); // SUB sp, size, sp
123        } else {
124            Register r = emitLoadInt(size);
125            emitOp3(0b10, SPARC.sp, 0b000100, SPARC.sp, r); // SUB sp, size, sp
126        }
127    }
128
129    @Override
130    public Register emitIntArg0() {
131        return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCallee, JavaKind.Int).get(0);
132    }
133
134    @Override
135    public Register emitIntArg1() {
136        return codeCache.getRegisterConfig().getCallingConventionRegisters(HotSpotCallingConventionType.JavaCallee, JavaKind.Int).get(1);
137    }
138
139    @Override
140    public Register emitLoadInt(int c) {
141        Register ret = newRegister();
142        loadIntToRegister(c, ret);
143        return ret;
144    }
145
146    private Register loadIntToRegister(int c, Register ret) {
147        int hi = c >>> 10;
148        int lo = c & ((1 << 10) - 1);
149        if (hi == 0) {
150            emitOp3(0b10, ret, 0b000010, SPARC.g0, lo); // OR g0, lo, ret
151        } else {
152            emitOp2(ret, 0b100, hi);                    // SETHI hi, ret
153            if (lo != 0) {
154                emitOp3(0b10, ret, 0b000010, ret, lo);  // OR ret, lo, ret
155            }
156        }
157        return ret;
158    }
159
160    @Override
161    public Register emitLoadLong(long c) {
162        Register ret = newRegister();
163        emitLoadLongToRegister(c, ret);
164        return ret;
165    }
166
167    private void loadLongToRegister(long c, Register ret) {
168        DataSectionReference ref = new DataSectionReference();
169        data.align(8);
170        ref.setOffset(data.position());
171        data.emitLong(c);
172        emitLoadPointerToRegister(ref, ret);
173    }
174
175    public Register emitLoadLongToRegister(long c, Register r) {
176        if ((c & 0xFFFF_FFFFL) == c) {
177            loadIntToRegister((int) c, r);
178        } else {
179            loadLongToRegister(c, r);
180        }
181        return r;
182    }
183
184    private void emitPatchableSethi(Register ret, boolean wide) {
185        int startPos = code.position();
186        emitOp2(ret, 0b100, 0);              // SETHI 0, ret
187        if (wide) {
188            // pad for later patching
189            while (code.position() < (startPos + 28)) {
190                emitNop();
191            }
192        }
193    }
194
195    @Override
196    public Register emitLoadFloat(float c) {
197        return emitLoadFloat(SPARC.f0, c);
198    }
199
200    public Register emitLoadFloat(Register reg, float c) {
201        return emitLoadFloat(reg, c, newRegister());
202    }
203
204    public Register emitLoadFloat(Register reg, float c, Register scratch) {
205        DataSectionReference ref = new DataSectionReference();
206        data.align(4);
207        ref.setOffset(data.position());
208        data.emitFloat(c);
209
210        recordDataPatchInCode(ref);
211        emitPatchableSethi(scratch, true);
212        emitOp3(0b11, reg, 0b100000, scratch, 0); // LDF [scratch+0], f0
213        return reg;
214    }
215
216    public Register emitLoadDouble(Register reg, double c) {
217        return emitLoadDouble(reg, c, newRegister());
218    }
219
220    public Register emitLoadDouble(Register reg, double c, Register scratch) {
221        DataSectionReference ref = new DataSectionReference();
222        data.align(8);
223        ref.setOffset(data.position());
224        data.emitDouble(c);
225
226        recordDataPatchInCode(ref);
227        emitPatchableSethi(scratch, true);
228        emitOp3(0b11, reg, 0b100011, scratch, 0); // LDDF [ptr+0], f0
229        return reg;
230    }
231
232    @Override
233    public Register emitLoadPointer(HotSpotConstant c) {
234        Register ret = newRegister();
235        recordDataPatchInCode(new ConstantReference((VMConstant) c));
236
237        emitPatchableSethi(ret, !c.isCompressed());
238        emitOp3(0b10, ret, 0b000010, ret, 0); // OR ret, 0, ret
239
240        return ret;
241    }
242
243    @Override
244    public Register emitLoadPointer(DataSectionReference ref) {
245        Register ret = newRegister();
246        emitLoadPointerToRegister(ref, ret);
247        return ret;
248    }
249
250    private void emitLoadPointerToRegister(DataSectionReference ref, Register ret) {
251        recordDataPatchInCode(ref);
252        emitPatchableSethi(ret, true);
253        emitOp3(0b11, ret, 0b001011, ret, 0); // LDX [ret+0], ret
254    }
255
256    @Override
257    public Register emitLoadNarrowPointer(DataSectionReference ref) {
258        Register ret = newRegister();
259        recordDataPatchInCode(ref);
260        emitPatchableSethi(ret, true);
261        emitOp3(0b11, ret, 0b000000, ret, 0); // LDUW [ret+0], ret
262        return ret;
263    }
264
265    @Override
266    public Register emitLoadPointer(Register b, int offset) {
267        Register ret = newRegister();
268        emitOp3(0b11, ret, 0b001011, b, offset); // LDX [b+offset], ret
269        return ret;
270    }
271
272    @Override
273    public StackSlot emitIntToStack(Register a) {
274        StackSlot ret = newStackSlot(SPARCKind.WORD);
275        intToStack(a, ret);
276        return ret;
277    }
278
279    public void intToStack(Register a, StackSlot ret) {
280        // STW a, [(s|f)p+offset]
281        emitStore(0b000100, a, ret);
282    }
283
284    @Override
285    public StackSlot emitLongToStack(Register a) {
286        StackSlot ret = newStackSlot(SPARCKind.XWORD);
287        longToStack(a, ret);
288        return ret;
289    }
290
291    public void longToStack(Register a, StackSlot ret) {
292        // STX a, [(f|s)p+offset]
293        emitStore(0b001110, a, ret);
294    }
295
296    @Override
297    public StackSlot emitFloatToStack(Register a) {
298        StackSlot ret = newStackSlot(SPARCKind.SINGLE);
299        floatToStack(a, ret);
300        return ret;
301    }
302
303    public void floatToStack(Register a, StackSlot ret) {
304        // STF a, [fp+offset]
305        emitStore(0b100100, a, ret);
306    }
307
308    @Override
309    public StackSlot emitDoubleToStack(Register a) {
310        StackSlot ret = newStackSlot(SPARCKind.DOUBLE);
311        return doubleToStack(a, ret);
312    }
313
314    public StackSlot doubleToStack(Register a, StackSlot ret) {
315        // STD a, [(s|f)p+offset]
316        emitStore(0b100111, a, ret);
317        return ret;
318    }
319
320    @Override
321    public StackSlot emitPointerToStack(Register a) {
322        StackSlot ret = newStackSlot(SPARCKind.XWORD);
323        // STX a, [fp+offset]
324        emitStore(0b001110, a, ret);
325        return ret;
326    }
327
328    @Override
329    public StackSlot emitNarrowPointerToStack(Register a) {
330        StackSlot ret = newStackSlot(SPARCKind.WORD);
331        // STW a, [fp+offset]
332        emitStore(0b000100, a, ret);
333        return ret;
334    }
335
336    private void emitStore(int op3, Register a, StackSlot ret) {
337        Register base;
338        if (ret.getRawOffset() < 0) {
339            base = SPARC.fp;
340        } else {
341            base = SPARC.sp;
342        }
343        int offset = ret.getRawOffset() + SPARC.STACK_BIAS;
344        if (isSimm(offset, 13)) {
345            // op3 a, [sp+offset]
346            emitOp3(0b11, a, op3, base, offset);
347        } else {
348            assert a != SPARC.g3;
349            Register r = SPARC.g3;
350            loadLongToRegister(offset, r);
351            // op3 a, [sp+g3]
352            emitOp3(0b11, a, op3, base, r);
353        }
354    }
355
356    @Override
357    public Register emitUncompressPointer(Register compressed, long base, int shift) {
358        Register ret;
359        if (shift > 0) {
360            ret = newRegister();
361            emitOp3(0b10, ret, 0b100101, compressed, shift); // SLL compressed, shift, ret
362        } else {
363            ret = compressed;
364        }
365        if (base == 0) {
366            return ret;
367        } else {
368            Register b = emitLoadLong(base);
369            emitOp3(0b10, b, 0b00000, ret, b); // ADD b, ret, b
370            return b;
371        }
372    }
373
374    @Override
375    public Register emitIntAdd(Register a, Register b) {
376        Register ret = newRegister();
377        emitOp3(0b10, ret, 0b00000, a, b); // ADD a, b, ret
378        return ret;
379    }
380
381    private void emitMove(Register to, Register from) {
382        if (to != from) {
383            emitOp3(0b10, to, 0b000010, from, SPARC.g0); // OR from, g0, to
384        }
385    }
386
387    @Override
388    public void emitIntRet(Register a) {
389        emitPointerRet(a);
390    }
391
392    @Override
393    public void emitFloatRet(Register a) {
394        assert a == SPARC.f0 : "Unimplemented";
395        emitOp3(0b10, SPARC.g0, 0b111000, SPARC.i7, 8);        // JMPL [i7+8], g0
396        emitOp3(0b10, SPARC.g0, 0b111101, SPARC.g0, SPARC.g0); // RESTORE g0, g0, g0
397    }
398
399    @Override
400    public void emitPointerRet(Register a) {
401        emitMove(SPARC.i0, a);
402        emitOp3(0b10, SPARC.g0, 0b111000, SPARC.i7, 8);        // JMPL [i7+8], g0
403        emitOp3(0b10, SPARC.g0, 0b111101, SPARC.g0, SPARC.g0); // RESTORE g0, g0, g0
404    }
405
406    @Override
407    public void emitTrap(DebugInfo info) {
408        recordImplicitException(info);
409        emitOp3(0b11, SPARC.g0, 0b001011, SPARC.g0, 0); // LDX [g0+0], g0
410    }
411
412    @Override
413    public DataSectionReference emitDataItem(HotSpotConstant c) {
414        if (c.isCompressed()) {
415            data.align(4);
416        } else {
417            data.align(8);
418        }
419        return super.emitDataItem(c);
420    }
421
422    @Override
423    public void emitCall(long addr) {
424        Register dst = emitLoadLong(addr);
425        emitOp3(0b10, SPARC.o7, 0b111000, dst, 0);        // JMPL [dst+0], o7
426        emitNop();
427    }
428
429    @Override
430    public void emitLoad(AllocatableValue av, Object prim) {
431        if (av instanceof RegisterValue) {
432            Register reg = ((RegisterValue) av).getRegister();
433            RegisterCategory cat = reg.getRegisterCategory();
434            if (cat.equals(SPARC.FPUs)) {
435                emitLoadFloat(reg, (Float) prim, scratchRegister);
436            } else if (cat.equals(SPARC.FPUd)) {
437                emitLoadDouble(reg, (Double) prim, scratchRegister);
438            } else if (prim instanceof Integer) {
439                loadIntToRegister((Integer) prim, reg);
440            } else if (prim instanceof Long) {
441                loadLongToRegister((Long) prim, reg);
442            }
443        } else if (av instanceof StackSlot) {
444            StackSlot slot = (StackSlot) av;
445            if (prim instanceof Float) {
446                floatToStack(emitLoadFloat(floatScratch, (Float) prim, scratchRegister), slot);
447            } else if (prim instanceof Double) {
448                doubleToStack(emitLoadDouble(doubleScratch, (Double) prim, scratchRegister), slot);
449            } else if (prim instanceof Integer) {
450                intToStack(loadIntToRegister((Integer) prim, scratchRegister), slot);
451            } else if (prim instanceof Long) {
452                longToStack(emitLoadLongToRegister((Long) prim, scratchRegister), slot);
453            }
454        } else {
455            throw new IllegalArgumentException("Unknown value " + av);
456        }
457    }
458
459    @Override
460    public void emitCallEpilogue(CallingConvention cc) {
461        // Nothing to do here.
462    }
463
464    @Override
465    public void emitCallPrologue(CallingConvention cc, Object... prim) {
466        emitGrowStack(cc.getStackSize());
467        frameSize += cc.getStackSize();
468        AllocatableValue[] args = cc.getArguments();
469        for (int i = 0; i < args.length; i++) {
470            emitLoad(args[i], prim[i]);
471        }
472    }
473
474}
475