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