1/*
2 * Copyright (c) 2015, 2015, 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 org.graalvm.compiler.core.aarch64;
25
26import static jdk.vm.ci.aarch64.AArch64.sp;
27import static jdk.vm.ci.aarch64.AArch64Kind.DWORD;
28import static jdk.vm.ci.aarch64.AArch64Kind.QWORD;
29import static org.graalvm.compiler.lir.LIRValueUtil.asJavaConstant;
30import static org.graalvm.compiler.lir.LIRValueUtil.isJavaConstant;
31import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.BSR;
32import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.CLZ;
33import static org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp.BitManipulationOpCode.CTZ;
34
35import org.graalvm.compiler.core.common.NumUtil;
36import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler;
37import org.graalvm.compiler.core.common.LIRKind;
38import org.graalvm.compiler.core.common.calc.FloatConvert;
39import org.graalvm.compiler.debug.GraalError;
40import org.graalvm.compiler.lir.ConstantValue;
41import org.graalvm.compiler.lir.LIRFrameState;
42import org.graalvm.compiler.lir.Variable;
43import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
44import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool;
45import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticOp;
46import org.graalvm.compiler.lir.aarch64.AArch64BitManipulationOp;
47import org.graalvm.compiler.lir.aarch64.AArch64Move.LoadOp;
48import org.graalvm.compiler.lir.aarch64.AArch64Move.StoreConstantOp;
49import org.graalvm.compiler.lir.aarch64.AArch64Move.StoreOp;
50import org.graalvm.compiler.lir.aarch64.AArch64ReinterpretOp;
51import org.graalvm.compiler.lir.aarch64.AArch64SignExtendOp;
52import org.graalvm.compiler.lir.aarch64.AArch64Unary;
53import org.graalvm.compiler.lir.gen.ArithmeticLIRGenerator;
54
55import jdk.vm.ci.aarch64.AArch64Kind;
56import jdk.vm.ci.code.RegisterValue;
57import jdk.vm.ci.meta.AllocatableValue;
58import jdk.vm.ci.meta.JavaConstant;
59import jdk.vm.ci.meta.PlatformKind;
60import jdk.vm.ci.meta.Value;
61import jdk.vm.ci.meta.ValueKind;
62
63public class AArch64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implements AArch64ArithmeticLIRGeneratorTool {
64
65    @Override
66    public AArch64LIRGenerator getLIRGen() {
67        return (AArch64LIRGenerator) super.getLIRGen();
68    }
69
70    @Override
71    protected boolean isNumericInteger(PlatformKind kind) {
72        return ((AArch64Kind) kind).isInteger();
73    }
74
75    @Override
76    protected Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags) {
77        if (isNumericInteger(a.getPlatformKind())) {
78            AArch64ArithmeticOp op = setFlags ? AArch64ArithmeticOp.ADDS : AArch64ArithmeticOp.ADD;
79            return emitBinary(resultKind, op, true, a, b);
80        } else {
81            assert !setFlags : "Cannot set flags on floating point arithmetic";
82            return emitBinary(resultKind, AArch64ArithmeticOp.FADD, true, a, b);
83        }
84    }
85
86    @Override
87    protected Variable emitSub(LIRKind resultKind, Value a, Value b, boolean setFlags) {
88        if (isNumericInteger(a.getPlatformKind())) {
89            AArch64ArithmeticOp op = setFlags ? AArch64ArithmeticOp.SUBS : AArch64ArithmeticOp.SUB;
90            return emitBinary(resultKind, op, false, a, b);
91        } else {
92            assert !setFlags : "Cannot set flags on floating point arithmetic";
93            return emitBinary(resultKind, AArch64ArithmeticOp.FSUB, false, a, b);
94        }
95    }
96
97    public Value emitExtendMemory(boolean isSigned, AArch64Kind memoryKind, int resultBits, AArch64AddressValue address, LIRFrameState state) {
98        // Issue a zero extending load of the proper bit size and set the result to
99        // the proper kind.
100        Variable result = getLIRGen().newVariable(LIRKind.value(resultBits == 32 ? AArch64Kind.DWORD : AArch64Kind.QWORD));
101
102        int targetSize = resultBits <= 32 ? 32 : 64;
103        switch (memoryKind) {
104            case BYTE:
105            case WORD:
106            case DWORD:
107            case QWORD:
108                getLIRGen().append(new AArch64Unary.MemoryOp(isSigned, targetSize,
109                                memoryKind.getSizeInBytes() * 8, result, address, state));
110                break;
111            default:
112                throw GraalError.shouldNotReachHere();
113        }
114        return result;
115    }
116
117    @Override
118    public Value emitMul(Value a, Value b, boolean setFlags) {
119        AArch64ArithmeticOp intOp = setFlags ? AArch64ArithmeticOp.MULVS : AArch64ArithmeticOp.MUL;
120        return emitBinary(LIRKind.combine(a, b), getOpCode(a, intOp, AArch64ArithmeticOp.FMUL), true, a, b);
121    }
122
123    @Override
124    public Value emitMulHigh(Value a, Value b) {
125        assert isNumericInteger(a.getPlatformKind());
126        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.SMULH, true, a, b);
127    }
128
129    @Override
130    public Value emitUMulHigh(Value a, Value b) {
131        assert isNumericInteger(a.getPlatformKind());
132        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UMULH, true, a, b);
133    }
134
135    @Override
136    public Value emitDiv(Value a, Value b, LIRFrameState state) {
137        return emitBinary(LIRKind.combine(a, b), getOpCode(a, AArch64ArithmeticOp.DIV, AArch64ArithmeticOp.FDIV), false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
138    }
139
140    @Override
141    public Value emitRem(Value a, Value b, LIRFrameState state) {
142        return emitBinary(LIRKind.combine(a, b), getOpCode(a, AArch64ArithmeticOp.REM, AArch64ArithmeticOp.FREM), false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
143    }
144
145    @Override
146    public Value emitUDiv(Value a, Value b, LIRFrameState state) {
147        assert isNumericInteger(a.getPlatformKind());
148        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UDIV, false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
149    }
150
151    @Override
152    public Value emitURem(Value a, Value b, LIRFrameState state) {
153        assert isNumericInteger(a.getPlatformKind());
154        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.UREM, false, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
155    }
156
157    @Override
158    public Value emitAnd(Value a, Value b) {
159        assert isNumericInteger(a.getPlatformKind());
160        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.AND, true, a, b);
161    }
162
163    @Override
164    public Value emitOr(Value a, Value b) {
165        assert isNumericInteger(a.getPlatformKind());
166        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.OR, true, a, b);
167    }
168
169    @Override
170    public Value emitXor(Value a, Value b) {
171        assert isNumericInteger(a.getPlatformKind());
172        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.XOR, true, a, b);
173    }
174
175    @Override
176    public Value emitShl(Value a, Value b) {
177        assert isNumericInteger(a.getPlatformKind());
178        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.SHL, false, a, b);
179    }
180
181    @Override
182    public Value emitShr(Value a, Value b) {
183        assert isNumericInteger(a.getPlatformKind());
184        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.ASHR, false, a, b);
185    }
186
187    @Override
188    public Value emitUShr(Value a, Value b) {
189        assert isNumericInteger(a.getPlatformKind());
190        return emitBinary(LIRKind.combine(a, b), AArch64ArithmeticOp.LSHR, false, a, b);
191    }
192
193    @Override
194    public Value emitFloatConvert(FloatConvert op, Value inputVal) {
195        PlatformKind resultPlatformKind = getFloatConvertResultKind(op);
196        LIRKind resultLirKind = LIRKind.combine(inputVal).changeType(resultPlatformKind);
197        Variable result = getLIRGen().newVariable(resultLirKind);
198        getLIRGen().append(new AArch64FloatConvertOp(op, result, getLIRGen().asAllocatable(inputVal)));
199        return result;
200    }
201
202    private static PlatformKind getFloatConvertResultKind(FloatConvert op) {
203        switch (op) {
204            case F2I:
205            case D2I:
206                return AArch64Kind.DWORD;
207            case F2L:
208            case D2L:
209                return AArch64Kind.QWORD;
210            case I2F:
211            case L2F:
212            case D2F:
213                return AArch64Kind.SINGLE;
214            case I2D:
215            case L2D:
216            case F2D:
217                return AArch64Kind.DOUBLE;
218            default:
219                throw GraalError.shouldNotReachHere();
220        }
221    }
222
223    @Override
224    public Value emitReinterpret(LIRKind to, Value inputVal) {
225        ValueKind<?> from = inputVal.getValueKind();
226        if (to.equals(from)) {
227            return inputVal;
228        }
229        Variable result = getLIRGen().newVariable(to);
230        getLIRGen().append(new AArch64ReinterpretOp(result, getLIRGen().asAllocatable(inputVal)));
231        return result;
232    }
233
234    @Override
235    public Value emitNarrow(Value inputVal, int bits) {
236        if (inputVal.getPlatformKind() == AArch64Kind.QWORD && bits <= 32) {
237            LIRKind resultKind = getResultLirKind(bits, inputVal);
238            long mask = NumUtil.getNbitNumberLong(bits);
239            Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
240            return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
241        } else {
242            return inputVal;
243        }
244    }
245
246    @Override
247    public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
248        assert fromBits <= toBits && toBits <= 64;
249        if (fromBits == toBits) {
250            return inputVal;
251        }
252        LIRKind resultKind = getResultLirKind(toBits, inputVal);
253        long mask = NumUtil.getNbitNumberLong(fromBits);
254        Value maskValue = new ConstantValue(resultKind, JavaConstant.forLong(mask));
255        return emitBinary(resultKind, AArch64ArithmeticOp.AND, true, inputVal, maskValue);
256    }
257
258    @Override
259    public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
260        LIRKind resultKind = getResultLirKind(toBits, inputVal);
261        assert fromBits <= toBits && toBits <= 64;
262        if (fromBits == toBits) {
263            return inputVal;
264        } else if (isJavaConstant(inputVal)) {
265            JavaConstant javaConstant = asJavaConstant(inputVal);
266            long constant;
267            if (javaConstant.isNull()) {
268                constant = 0;
269            } else {
270                constant = javaConstant.asLong();
271            }
272            int shiftCount = QWORD.getSizeInBytes() * 8 - fromBits;
273            return new ConstantValue(resultKind, JavaConstant.forLong((constant << shiftCount) >> shiftCount));
274        }
275        Variable result = getLIRGen().newVariable(resultKind);
276        getLIRGen().append(new AArch64SignExtendOp(result, getLIRGen().asAllocatable(inputVal), fromBits, toBits));
277        return result;
278    }
279
280    private static LIRKind getResultLirKind(int resultBitSize, Value... inputValues) {
281        if (resultBitSize == 64) {
282            return LIRKind.combine(inputValues).changeType(QWORD);
283        } else {
284            // FIXME: I have no idea what this assert was ever for
285            // assert resultBitSize == 32;
286            return LIRKind.combine(inputValues).changeType(DWORD);
287        }
288    }
289
290    protected Variable emitBinary(ValueKind<?> resultKind, AArch64ArithmeticOp op, boolean commutative, Value a, Value b) {
291        Variable result = getLIRGen().newVariable(resultKind);
292        if (isValidBinaryConstant(op, a, b)) {
293            emitBinaryConst(result, op, getLIRGen().asAllocatable(a), asJavaConstant(b));
294        } else if (commutative && isValidBinaryConstant(op, b, a)) {
295            emitBinaryConst(result, op, getLIRGen().asAllocatable(b), asJavaConstant(a));
296        } else {
297            emitBinaryVar(result, op, getLIRGen().asAllocatable(a), getLIRGen().asAllocatable(b));
298        }
299        return result;
300    }
301
302    private void emitBinaryVar(Variable result, AArch64ArithmeticOp op, AllocatableValue a, AllocatableValue b) {
303        AllocatableValue x = moveSp(a);
304        AllocatableValue y = moveSp(b);
305        switch (op) {
306            case FREM:
307            case REM:
308            case UREM:
309                getLIRGen().append(new AArch64ArithmeticOp.BinaryCompositeOp(op, result, x, y));
310                break;
311            default:
312                getLIRGen().append(new AArch64ArithmeticOp.BinaryOp(op, result, x, y));
313                break;
314        }
315    }
316
317    private void emitBinaryConst(Variable result, AArch64ArithmeticOp op, AllocatableValue a, JavaConstant b) {
318        AllocatableValue x = moveSp(a);
319        getLIRGen().append(new AArch64ArithmeticOp.BinaryConstOp(op, result, x, b));
320    }
321
322    private static boolean isValidBinaryConstant(AArch64ArithmeticOp op, Value a, Value b) {
323        if (!isJavaConstant(b)) {
324            return false;
325        }
326        JavaConstant constValue = asJavaConstant(b);
327        switch (op.category) {
328            case LOGICAL:
329                return isLogicalConstant(constValue);
330            case ARITHMETIC:
331                return isArithmeticConstant(constValue);
332            case SHIFT:
333                assert constValue.asLong() >= 0 && constValue.asLong() < a.getPlatformKind().getSizeInBytes() * Byte.SIZE;
334                return true;
335            case NONE:
336                return false;
337            default:
338                throw GraalError.shouldNotReachHere();
339        }
340    }
341
342    private static boolean isLogicalConstant(JavaConstant constValue) {
343        switch (constValue.getJavaKind()) {
344            case Int:
345                return AArch64MacroAssembler.isLogicalImmediate(constValue.asInt());
346            case Long:
347                return AArch64MacroAssembler.isLogicalImmediate(constValue.asLong());
348            default:
349                return false;
350        }
351    }
352
353    protected static boolean isArithmeticConstant(JavaConstant constValue) {
354        switch (constValue.getJavaKind()) {
355            case Int:
356            case Long:
357                return AArch64MacroAssembler.isArithmeticImmediate(constValue.asLong());
358            case Object:
359                return constValue.isNull();
360            default:
361                return false;
362        }
363    }
364
365    @Override
366    public Value emitNegate(Value inputVal) {
367        return emitUnary(getOpCode(inputVal, AArch64ArithmeticOp.NEG, AArch64ArithmeticOp.FNEG), inputVal);
368    }
369
370    @Override
371    public Value emitNot(Value input) {
372        assert isNumericInteger(input.getPlatformKind());
373        return emitUnary(AArch64ArithmeticOp.NOT, input);
374    }
375
376    @Override
377    public Value emitMathAbs(Value input) {
378        return emitUnary(getOpCode(input, AArch64ArithmeticOp.ABS, AArch64ArithmeticOp.FABS), input);
379    }
380
381    @Override
382    public Value emitMathSqrt(Value input) {
383        assert input.getPlatformKind() == AArch64Kind.DOUBLE;
384        return emitUnary(AArch64ArithmeticOp.SQRT, input);
385    }
386
387    @Override
388    public Variable emitBitScanForward(Value value) {
389        throw GraalError.unimplemented();
390    }
391
392    @Override
393    public Value emitBitCount(Value operand) {
394        throw GraalError.unimplemented("AArch64 ISA does not offer way to implement this more efficiently than a simple Java algorithm.");
395    }
396
397    @Override
398    public Value emitBitScanReverse(Value value) {
399        Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
400        getLIRGen().append(new AArch64BitManipulationOp(BSR, result, getLIRGen().asAllocatable(value)));
401        return result;
402    }
403
404    @Override
405    public Value emitCountLeadingZeros(Value value) {
406        Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
407        getLIRGen().append(new AArch64BitManipulationOp(CLZ, result, getLIRGen().asAllocatable(value)));
408        return result;
409    }
410
411    @Override
412    public Value emitCountTrailingZeros(Value value) {
413        Variable result = getLIRGen().newVariable(LIRKind.combine(value).changeType(AArch64Kind.DWORD));
414        getLIRGen().append(new AArch64BitManipulationOp(CTZ, result, getLIRGen().asAllocatable(value)));
415        return result;
416    }
417
418    private Variable emitUnary(AArch64ArithmeticOp op, Value inputVal) {
419        AllocatableValue input = getLIRGen().asAllocatable(inputVal);
420        Variable result = getLIRGen().newVariable(LIRKind.combine(input));
421        getLIRGen().append(new AArch64ArithmeticOp.UnaryOp(op, result, input));
422        return result;
423    }
424
425    /**
426     * If val denotes the stackpointer, move it to another location. This is necessary since most
427     * ops cannot handle the stackpointer as input or output.
428     */
429    private AllocatableValue moveSp(AllocatableValue val) {
430        if (val instanceof RegisterValue && ((RegisterValue) val).getRegister().equals(sp)) {
431            assert val.getPlatformKind() == AArch64Kind.QWORD : "Stackpointer must be long";
432            return getLIRGen().emitMove(val);
433        }
434        return val;
435    }
436
437    /**
438     * Returns the opcode depending on the platform kind of val.
439     */
440    private AArch64ArithmeticOp getOpCode(Value val, AArch64ArithmeticOp intOp, AArch64ArithmeticOp floatOp) {
441        return isNumericInteger(val.getPlatformKind()) ? intOp : floatOp;
442    }
443
444    @Override
445    public Variable emitLoad(LIRKind kind, Value address, LIRFrameState state) {
446        AArch64AddressValue loadAddress = getLIRGen().asAddressValue(address);
447        Variable result = getLIRGen().newVariable(getLIRGen().toRegisterKind(kind));
448        getLIRGen().append(new LoadOp((AArch64Kind) kind.getPlatformKind(), result, loadAddress, state));
449        return result;
450    }
451
452    @Override
453    public void emitStore(ValueKind<?> lirKind, Value address, Value inputVal, LIRFrameState state) {
454        AArch64AddressValue storeAddress = getLIRGen().asAddressValue(address);
455        AArch64Kind kind = (AArch64Kind) lirKind.getPlatformKind();
456
457        if (isJavaConstant(inputVal) && kind.isInteger()) {
458            JavaConstant c = asJavaConstant(inputVal);
459            if (c.isDefaultForKind()) {
460                // We can load 0 directly into integer registers
461                getLIRGen().append(new StoreConstantOp(kind, storeAddress, c, state));
462                return;
463            }
464        }
465        AllocatableValue input = getLIRGen().asAllocatable(inputVal);
466        getLIRGen().append(new StoreOp(kind, storeAddress, input, state));
467    }
468
469    @Override
470    public Value emitMathLog(Value input, boolean base10) {
471        throw GraalError.unimplemented();
472    }
473
474    @Override
475    public Value emitMathCos(Value input) {
476        throw GraalError.unimplemented();
477    }
478
479    @Override
480    public Value emitMathSin(Value input) {
481        throw GraalError.unimplemented();
482    }
483
484    @Override
485    public Value emitMathTan(Value input) {
486        throw GraalError.unimplemented();
487    }
488
489    @Override
490    public void emitCompareOp(AArch64Kind cmpKind, Variable left, Value right) {
491        throw GraalError.unimplemented();
492    }
493
494}
495