1 /*
2  * Copyright (c) 2013, 2017, 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.lir.sparc;
26 
27 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BPCC;
28 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CCR_V_SHIFT;
29 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CCR_XCC_SHIFT;
30 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.FBPCC;
31 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.isSimm13;
32 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.ANNUL;
33 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Annul.NOT_ANNUL;
34 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.BranchPredict.PREDICT_TAKEN;
35 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Fcc0;
36 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.CC.Xcc;
37 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.Equal;
38 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.ConditionFlag.F_Ordered;
39 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fcmpd;
40 import static org.graalvm.compiler.asm.sparc.SPARCAssembler.Opfs.Fcmps;
41 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST;
42 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.HINT;
43 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
44 import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
45 import static jdk.vm.ci.code.ValueUtil.asRegister;
46 import static jdk.vm.ci.code.ValueUtil.isRegister;
47 import static jdk.vm.ci.sparc.SPARC.g0;
48 import static jdk.vm.ci.sparc.SPARCKind.DOUBLE;
49 import static jdk.vm.ci.sparc.SPARCKind.SINGLE;
50 import static jdk.vm.ci.sparc.SPARCKind.WORD;
51 import static jdk.vm.ci.sparc.SPARCKind.XWORD;
52 
53 import org.graalvm.compiler.asm.Label;
54 import org.graalvm.compiler.asm.sparc.SPARCAssembler;
55 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler;
56 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister;
57 import org.graalvm.compiler.core.common.LIRKind;
58 import org.graalvm.compiler.debug.GraalError;
59 import org.graalvm.compiler.lir.LIRFrameState;
60 import org.graalvm.compiler.lir.LIRInstructionClass;
61 import org.graalvm.compiler.lir.Opcode;
62 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
63 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
64 
65 import jdk.vm.ci.code.Register;
66 import jdk.vm.ci.meta.AllocatableValue;
67 import jdk.vm.ci.meta.Value;
68 import jdk.vm.ci.sparc.SPARC;
69 
70 public class SPARCArithmetic {
71     public static final class FloatConvertOp extends SPARCLIRInstruction {
72         public static final LIRInstructionClass<FloatConvertOp> TYPE = LIRInstructionClass.create(FloatConvertOp.class);
73         public static final SizeEstimate SIZE = SizeEstimate.create(5);
74 
75         @Opcode private final FloatConvert opcode;
76         @Def({REG, HINT}) protected AllocatableValue result;
77         @Use({REG}) protected Value x;
78 
79         public enum FloatConvert {
80             F2I,
81             D2I,
82             F2L,
83             D2L
84         }
85 
FloatConvertOp(FloatConvert opcode, Value x, AllocatableValue result)86         public FloatConvertOp(FloatConvert opcode, Value x, AllocatableValue result) {
87             super(TYPE, SIZE);
88             this.opcode = opcode;
89             this.x = x;
90             this.result = result;
91         }
92 
93         @Override
emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)94         protected void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
95             Label notOrdered = new Label();
96             switch (opcode) {
97                 case F2L:
98                     masm.fcmp(Fcc0, Fcmps, asRegister(x, SINGLE), asRegister(x, SINGLE));
99                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
100                     masm.fstox(asRegister(x, SINGLE), asRegister(result, DOUBLE));
101                     masm.fxtod(asRegister(result), asRegister(result));
102                     masm.fsubd(asRegister(result, DOUBLE), asRegister(result, DOUBLE), asRegister(result, DOUBLE));
103                     masm.bind(notOrdered);
104                     break;
105                 case F2I:
106                     masm.fcmp(Fcc0, Fcmps, asRegister(x, SINGLE), asRegister(x, SINGLE));
107                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
108                     masm.fstoi(asRegister(x, SINGLE), asRegister(result, SINGLE));
109                     masm.fitos(asRegister(result, SINGLE), asRegister(result, SINGLE));
110                     masm.fsubs(asRegister(result, SINGLE), asRegister(result, SINGLE), asRegister(result, SINGLE));
111                     masm.bind(notOrdered);
112                     break;
113                 case D2L:
114                     masm.fcmp(Fcc0, Fcmpd, asRegister(x, DOUBLE), asRegister(x, DOUBLE));
115                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
116                     masm.fdtox(asRegister(x, DOUBLE), asRegister(result, DOUBLE));
117                     masm.fxtod(asRegister(result, DOUBLE), asRegister(result, DOUBLE));
118                     masm.fsubd(asRegister(result, DOUBLE), asRegister(result, DOUBLE), asRegister(result, DOUBLE));
119                     masm.bind(notOrdered);
120                     break;
121                 case D2I:
122                     masm.fcmp(Fcc0, Fcmpd, asRegister(x, DOUBLE), asRegister(x, DOUBLE));
123                     FBPCC.emit(masm, Fcc0, F_Ordered, ANNUL, PREDICT_TAKEN, notOrdered);
124                     masm.fdtoi(asRegister(x, DOUBLE), asRegister(result, SINGLE));
125                     masm.fitos(asRegister(result, SINGLE), asRegister(result, SINGLE));
126                     masm.fsubs(asRegister(result, SINGLE), asRegister(result, SINGLE), asRegister(result, SINGLE));
127                     masm.bind(notOrdered);
128                     break;
129                 default:
130                     throw GraalError.shouldNotReachHere("missing: " + opcode);
131             }
132         }
133     }
134 
135     /**
136      * Special LIR instruction as it requires a bunch of scratch registers.
137      */
138     public static final class RemOp extends SPARCLIRInstruction implements SPARCTailDelayedLIRInstruction {
139         public static final LIRInstructionClass<RemOp> TYPE = LIRInstructionClass.create(RemOp.class);
140         public static final SizeEstimate SIZE = SizeEstimate.create(4);
141 
142         @Opcode private final Rem opcode;
143         @Def({REG}) protected AllocatableValue result;
144         @Alive({REG, CONST}) protected Value x;
145         @Alive({REG, CONST}) protected Value y;
146         @Temp({REG}) protected AllocatableValue scratch1;
147         @Temp({REG}) protected AllocatableValue scratch2;
148         @State protected LIRFrameState state;
149 
150         public enum Rem {
151             IUREM,
152             LUREM
153         }
154 
RemOp(Rem opcode, AllocatableValue result, Value x, Value y, AllocatableValue scratch1, AllocatableValue scratch2, LIRFrameState state)155         public RemOp(Rem opcode, AllocatableValue result, Value x, Value y, AllocatableValue scratch1, AllocatableValue scratch2, LIRFrameState state) {
156             super(TYPE, SIZE);
157             this.opcode = opcode;
158             this.result = result;
159             this.x = x;
160             this.y = y;
161             this.scratch1 = scratch1;
162             this.scratch2 = scratch2;
163             this.state = state;
164         }
165 
166         @Override
emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)167         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
168             if (!isJavaConstant(x) && isJavaConstant(y)) {
169                 assert isSimm13(crb.asIntConst(y));
170                 assert !x.equals(scratch1);
171                 assert !x.equals(scratch2);
172                 assert !y.equals(scratch1);
173                 switch (opcode) {
174                     case LUREM:
175                         if (state != null) {
176                             crb.recordImplicitException(masm.position(), state);
177                         }
178                         masm.udivx(asRegister(x, XWORD), crb.asIntConst(y), asRegister(scratch1, XWORD));
179                         masm.mulx(asRegister(scratch1, XWORD), crb.asIntConst(y), asRegister(scratch2, XWORD));
180                         getDelayedControlTransfer().emitControlTransfer(crb, masm);
181                         masm.sub(asRegister(x, XWORD), asRegister(scratch2, XWORD), asRegister(result, XWORD));
182                         break;
183                     case IUREM:
184                         GraalError.unimplemented();
185                         break;
186                     default:
187                         throw GraalError.shouldNotReachHere();
188                 }
189             } else if (isRegister(x) && isRegister(y)) {
190                 Value xLeft = x;
191                 switch (opcode) {
192                     case LUREM:
193                         if (isJavaConstant(x)) {
194                             masm.setx(crb.asLongConst(x), asRegister(scratch2, XWORD), false);
195                             xLeft = scratch2;
196                         }
197                         assert !asRegister(xLeft, XWORD).equals(asRegister(scratch1, XWORD));
198                         assert !asRegister(y, XWORD).equals(asRegister(scratch1, XWORD));
199                         if (state != null) {
200                             crb.recordImplicitException(masm.position(), state);
201                         }
202                         masm.udivx(asRegister(xLeft, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD));
203                         masm.mulx(asRegister(scratch1, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD));
204                         getDelayedControlTransfer().emitControlTransfer(crb, masm);
205                         masm.sub(asRegister(xLeft, XWORD), asRegister(scratch1, XWORD), asRegister(result, XWORD));
206                         break;
207                     case IUREM:
208                         assert !asRegister(result, WORD).equals(asRegister(scratch1, WORD));
209                         assert !asRegister(result, WORD).equals(asRegister(scratch2, WORD));
210                         masm.srl(asRegister(x, WORD), 0, asRegister(scratch1, WORD));
211                         masm.srl(asRegister(y, WORD), 0, asRegister(result, WORD));
212                         if (state != null) {
213                             crb.recordImplicitException(masm.position(), state);
214                         }
215                         masm.udivx(asRegister(scratch1, WORD), asRegister(result, WORD), asRegister(scratch2, WORD));
216                         masm.mulx(asRegister(scratch2, WORD), asRegister(result, WORD), asRegister(result, WORD));
217                         getDelayedControlTransfer().emitControlTransfer(crb, masm);
218                         masm.sub(asRegister(scratch1, WORD), asRegister(result, WORD), asRegister(result, WORD));
219                         break;
220                     default:
221                         throw GraalError.shouldNotReachHere();
222                 }
223             } else {
224                 throw GraalError.shouldNotReachHere();
225             }
226         }
227     }
228 
229     public static final class SPARCIMulccOp extends SPARCLIRInstruction {
230         public static final LIRInstructionClass<SPARCIMulccOp> TYPE = LIRInstructionClass.create(SPARCIMulccOp.class);
231         public static final SizeEstimate SIZE = SizeEstimate.create(10);
232         @Def({REG}) protected AllocatableValue result;
233         @Alive({REG}) protected AllocatableValue x;
234         @Alive({REG}) protected AllocatableValue y;
235 
SPARCIMulccOp(AllocatableValue result, AllocatableValue x, AllocatableValue y)236         public SPARCIMulccOp(AllocatableValue result, AllocatableValue x, AllocatableValue y) {
237             super(TYPE, SIZE);
238             this.result = result;
239             this.x = x;
240             this.y = y;
241         }
242 
243         @Override
emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)244         protected void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
245             try (ScratchRegister tmpScratch = masm.getScratchRegister()) {
246                 Register tmp = tmpScratch.getRegister();
247                 Register resultRegister = asRegister(result, WORD);
248                 Register xRegister = asRegister(x, WORD);
249                 Register yRegister = asRegister(y, WORD);
250                 masm.sra(xRegister, 0, xRegister);
251                 masm.sra(yRegister, 0, yRegister);
252                 masm.mulx(xRegister, yRegister, resultRegister);
253                 Label noOverflow = new Label();
254                 masm.sra(resultRegister, 0, tmp);
255                 masm.compareBranch(tmp, resultRegister, Equal, Xcc, noOverflow, PREDICT_TAKEN, null);
256                 masm.wrccr(SPARC.g0, 1 << (SPARCAssembler.CCR_ICC_SHIFT + SPARCAssembler.CCR_V_SHIFT));
257                 masm.bind(noOverflow);
258             }
259         }
260     }
261 
262     /**
263      * Calculates the product and condition code for long multiplication of long values.
264      */
265     public static final class SPARCLMulccOp extends SPARCLIRInstruction {
266         public static final LIRInstructionClass<SPARCLMulccOp> TYPE = LIRInstructionClass.create(SPARCLMulccOp.class);
267         public static final SizeEstimate SIZE = SizeEstimate.create(13);
268 
269         @Def({REG}) protected AllocatableValue result;
270         @Alive({REG}) protected AllocatableValue x;
271         @Alive({REG}) protected AllocatableValue y;
272         @Temp({REG}) protected AllocatableValue scratch1;
273         @Temp({REG}) protected AllocatableValue scratch2;
274 
SPARCLMulccOp(AllocatableValue result, AllocatableValue x, AllocatableValue y, LIRGeneratorTool gen)275         public SPARCLMulccOp(AllocatableValue result, AllocatableValue x, AllocatableValue y, LIRGeneratorTool gen) {
276             super(TYPE, SIZE);
277             this.result = result;
278             this.x = x;
279             this.y = y;
280             this.scratch1 = gen.newVariable(LIRKind.combine(x, y));
281             this.scratch2 = gen.newVariable(LIRKind.combine(x, y));
282         }
283 
284         @Override
emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)285         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
286             Label noOverflow = new Label();
287             masm.mulx(asRegister(x, XWORD), asRegister(y, XWORD), asRegister(result, XWORD));
288 
289             // Calculate the upper 64 bit signed := (umulxhi product - (x{63}&y + y{63}&x))
290             masm.umulxhi(asRegister(x, XWORD), asRegister(y, XWORD), asRegister(scratch1, XWORD));
291             masm.srax(asRegister(x, XWORD), 63, asRegister(scratch2, XWORD));
292             masm.and(asRegister(scratch2, XWORD), asRegister(y, XWORD), asRegister(scratch2, XWORD));
293             masm.sub(asRegister(scratch1, XWORD), asRegister(scratch2, XWORD), asRegister(scratch1, XWORD));
294 
295             masm.srax(asRegister(y, XWORD), 63, asRegister(scratch2, XWORD));
296             masm.and(asRegister(scratch2, XWORD), asRegister(x, XWORD), asRegister(scratch2, XWORD));
297             masm.sub(asRegister(scratch1, XWORD), asRegister(scratch2, XWORD), asRegister(scratch1, XWORD));
298 
299             // Now construct the lower half and compare
300             masm.srax(asRegister(result, XWORD), 63, asRegister(scratch2, XWORD));
301             masm.cmp(asRegister(scratch1, XWORD), asRegister(scratch2, XWORD));
302             BPCC.emit(masm, Xcc, Equal, NOT_ANNUL, PREDICT_TAKEN, noOverflow);
303             masm.nop();
304             masm.wrccr(g0, 1 << (CCR_XCC_SHIFT + CCR_V_SHIFT));
305             masm.bind(noOverflow);
306         }
307     }
308 
309     public static final class MulHighOp extends SPARCLIRInstruction {
310         public static final LIRInstructionClass<MulHighOp> TYPE = LIRInstructionClass.create(MulHighOp.class);
311         public static final SizeEstimate SIZE = SizeEstimate.create(4);
312 
313         @Opcode private final MulHigh opcode;
314         @Def({REG}) public AllocatableValue result;
315         @Alive({REG}) public AllocatableValue x;
316         @Alive({REG}) public AllocatableValue y;
317         @Temp({REG}) public AllocatableValue scratch;
318 
319         public enum MulHigh {
320             IMUL,
321             LMUL
322         }
323 
MulHighOp(MulHigh opcode, AllocatableValue x, AllocatableValue y, AllocatableValue result, AllocatableValue scratch)324         public MulHighOp(MulHigh opcode, AllocatableValue x, AllocatableValue y, AllocatableValue result, AllocatableValue scratch) {
325             super(TYPE, SIZE);
326             this.opcode = opcode;
327             this.x = x;
328             this.y = y;
329             this.scratch = scratch;
330             this.result = result;
331         }
332 
333         @Override
emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm)334         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
335             assert isRegister(x) && isRegister(y) && isRegister(result) && isRegister(scratch);
336             switch (opcode) {
337                 case IMUL:
338                     masm.sra(asRegister(x), 0, asRegister(x));
339                     masm.sra(asRegister(y), 0, asRegister(y));
340                     masm.mulx(asRegister(x, WORD), asRegister(y, WORD), asRegister(result, WORD));
341                     masm.srax(asRegister(result, WORD), 32, asRegister(result, WORD));
342                     break;
343                 case LMUL:
344                     assert !asRegister(scratch, XWORD).equals(asRegister(result, XWORD));
345                     masm.umulxhi(asRegister(x, XWORD), asRegister(y, XWORD), asRegister(result, XWORD));
346 
347                     masm.srlx(asRegister(x, XWORD), 63, asRegister(scratch, XWORD));
348                     masm.mulx(asRegister(scratch, XWORD), asRegister(y, XWORD), asRegister(scratch, XWORD));
349                     masm.sub(asRegister(result, XWORD), asRegister(scratch, XWORD), asRegister(result, XWORD));
350 
351                     masm.srlx(asRegister(y, XWORD), 63, asRegister(scratch, XWORD));
352                     masm.mulx(asRegister(scratch, XWORD), asRegister(x, XWORD), asRegister(scratch, XWORD));
353                     masm.sub(asRegister(result, XWORD), asRegister(scratch, XWORD), asRegister(result, XWORD));
354                     break;
355                 default:
356                     throw GraalError.shouldNotReachHere();
357             }
358         }
359     }
360 }
361