diff --git a/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64MacroAssemblerTest.java b/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64AddressingModeTest.java similarity index 99% rename from compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64MacroAssemblerTest.java rename to compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64AddressingModeTest.java index dde1fe6f9382..e3bbd96a1e69 100644 --- a/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64MacroAssemblerTest.java +++ b/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64AddressingModeTest.java @@ -46,7 +46,7 @@ import jdk.vm.ci.code.Register; import jdk.vm.ci.code.TargetDescription; -public class AArch64MacroAssemblerTest extends GraalTest { +public class AArch64AddressingModeTest extends GraalTest { private AArch64MacroAssembler masm; private TestProtectedAssembler asm; diff --git a/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64MoveConstantTest.java b/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64MoveConstantTest.java new file mode 100644 index 000000000000..d75545801802 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.asm.aarch64.test/src/org/graalvm/compiler/asm/aarch64/test/AArch64MoveConstantTest.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Arm Limited and affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.graalvm.compiler.asm.aarch64.test; + +import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.code.TargetDescription; +import jdk.vm.ci.runtime.JVMCI; +import org.graalvm.compiler.asm.aarch64.AArch64Assembler; +import org.graalvm.compiler.asm.aarch64.AArch64MacroAssembler; +import org.graalvm.compiler.test.GraalTest; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; + +public class AArch64MoveConstantTest extends GraalTest { + + private AArch64MacroAssembler masm; + private TestProtectedAssembler asm; + private Register dst; + private Register zr; + + @Before + public void setupEnvironment() { + // Setup AArch64 MacroAssembler and Assembler. + TargetDescription target = JVMCI.getRuntime().getHostJVMCIBackend().getTarget(); + masm = new AArch64MacroAssembler(target); + asm = new TestProtectedAssembler(target); + dst = AArch64.r10; + zr = AArch64.zr; + } + + /** + * MacroAssembler behavior test for 32-bit constant move. + */ + @Test + public void testMoveIntZero() { + masm.mov(dst, 0); // zero is specially handled by OR(dst, zr, zr). + asm.orr(32, dst, zr, zr, AArch64Assembler.ShiftType.LSL, 0); + compareAssembly(); + } + + @Test + public void testMoveIntLogicalImm() { + masm.mov(dst, 0x5555_5555); // 0b01010101...0101 is a 32-bit logical immediate. + asm.orr(32, dst, zr, 0x5555_5555); + compareAssembly(); + } + + @Test + public void testMoveIntMinusOne() { + masm.mov(dst, -1); + asm.movn(32, dst, 0, 0); + compareAssembly(); + } + + @Test + public void testMoveIntHighZero() { + masm.mov(dst, 0x0000_1234); + asm.movz(32, dst, 0x1234, 0); + compareAssembly(); + } + + @Test + public void testMoveIntLowZero() { + masm.mov(dst, 0x5678_0000); + asm.movz(32, dst, 0x5678, 16); + compareAssembly(); + } + + @Test + public void testMoveIntHighNeg() { + masm.mov(dst, 0xFFFF_CAFE); + asm.movn(32, dst, 0xCAFE ^ 0xFFFF, 0); + compareAssembly(); + } + + @Test + public void testMoveIntLowNeg() { + masm.mov(dst, 0xBABE_FFFF); + asm.movn(32, dst, 0xBABE ^ 0xFFFF, 16); + compareAssembly(); + } + + @Test + public void testMoveIntCommon() { + masm.mov(dst, 0x1357_BEEF); + asm.movz(32, dst, 0xBEEF, 0); + asm.movk(32, dst, 0x1357, 16); + compareAssembly(); + } + + /** + * MacroAssembler behavior test for 64-bit constant move. + */ + @Test + public void testMoveLongZero() { + masm.mov(dst, 0L); // zero is specially handled by OR(dst, zr, zr). + asm.orr(64, dst, zr, zr, AArch64Assembler.ShiftType.LSL, 0); + compareAssembly(); + } + + @Test + public void testMoveLongLogicalImm() { + masm.mov(dst, 0x3333_3333_3333_3333L); // 0b00110011...0011 is a 64-bit logical immediate. + asm.orr(64, dst, zr, 0x3333_3333_3333_3333L); + compareAssembly(); + } + + @Test + public void testMoveLongSignExtendedLogicalImm() { + masm.mov(dst, 0xFFFF_FFFF_8888_8888L); // 0x88888888 is a 32-bit logical immediate. + asm.orr(32, dst, zr, 0x8888_8888); + asm.sbfm(64, dst, dst, 0, 31); + compareAssembly(); + } + + @Test + public void testMoveLongWithTwoZeros() { + masm.mov(dst, 0x1357_0000_ABCD_0000L); + asm.movz(64, dst, 0xABCD, 16); + asm.movk(64, dst, 0x1357, 48); + compareAssembly(); + } + + @Test + public void testMoveLongWithTwoNegs() { + masm.mov(dst, 0x2222_FFFF_FFFF_7777L); + asm.movn(64, dst, 0x7777 ^ 0xFFFF, 0); + asm.movk(64, dst, 0x2222, 48); + compareAssembly(); + } + + @Test + public void testMoveLongWithOneZero() { + masm.mov(dst, 0x0000_6666_5555_4444L); + asm.movz(64, dst, 0x4444, 0); + asm.movk(64, dst, 0x5555, 16); + asm.movk(64, dst, 0x6666, 32); + compareAssembly(); + } + + @Test + public void testMoveLongWithOneNeg() { + masm.mov(dst, 0xDDDD_CCCC_BBBB_FFFFL); + asm.movn(64, dst, 0xBBBB ^ 0xFFFF, 16); + asm.movk(64, dst, 0xCCCC, 32); + asm.movk(64, dst, 0xDDDD, 48); + compareAssembly(); + } + + @Test + public void testMoveLongCommon() { + masm.mov(dst, 0x3D38_2A05_B001_1942L); + asm.movz(64, dst, 0x1942, 0); + asm.movk(64, dst, 0xB001, 16); + asm.movk(64, dst, 0x2A05, 32); + asm.movk(64, dst, 0x3D38, 48); + compareAssembly(); + } + + /** + * Compares assembly generated by the macro assembler to the hand-generated assembly. + */ + private void compareAssembly() { + byte[] expected = asm.close(true); + byte[] actual = masm.close(true); + assertArrayEquals(expected, actual); + } + + /** + * Compare constant values with corresponding hex strings. + */ + @Test + public void testConstantHexRepresentation() { + checkInt(0, "0"); + checkInt(-1, "FFFFFFFF"); + checkInt(0x4B95_0000, "4B950000"); + checkInt(0xEE2A, "EE2A"); + checkInt(0x31C2_FFFF, "31C2FFFF"); + checkInt(0xFFFF_5678, "FFFF5678"); + checkInt(0xB39F_01CC, "B39F01CC"); + checkLong(0L, "0"); + checkLong(-1L, "FFFFFFFFFFFFFFFF"); + checkLong(0x94DDL, "94DD"); + checkLong(0x351C_0000_7B7BL, "351C00007B7B"); + checkLong(0x9012_ABCD_3333_0000L, "9012ABCD33330000"); + checkLong(0xFFFFL, "FFFF"); + checkLong(0xFFFF_0001L, "FFFF0001"); + checkLong(0xFFFF_9302_FFFF_CDEFL, "FFFF9302FFFFCDEF"); + checkLong(0x102A_FFFF_FFFF_FFFFL, "102AFFFFFFFFFFFF"); + checkLong(0x9E8C_3A50_0BC9_44F8L, "9E8C3A500BC944F8"); + } + + private static void checkInt(int value, String hexString) { + assertTrue(Integer.toHexString(value).toUpperCase().equals(hexString), "Expected: " + hexString); + } + + private static void checkLong(long value, String hexString) { + assertTrue(Long.toHexString(value).toUpperCase().equals(hexString), "Expected: " + hexString); + } +} diff --git a/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java b/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java index 1760d3b3a438..83f421bc3b8a 100755 --- a/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java +++ b/compiler/src/org.graalvm.compiler.asm.aarch64/src/org/graalvm/compiler/asm/aarch64/AArch64MacroAssembler.java @@ -335,37 +335,188 @@ public void mov(int size, Register dst, Register src) { } } + /** + * Generates a 32-bit immediate move code sequence. + * + * @param dst general purpose register. May not be null, stackpointer or zero-register. + * @param imm the value to move into the register. + * @param needsImmAnnotation Flag denoting if annotation should be added. + */ + private void mov32(Register dst, int imm, boolean needsImmAnnotation) { + int numMovs = 0; + int pos = position(); + + // Split 32-bit imm into low16 and high16 parts. + int low16 = imm & 0xFFFF; + int high16 = (imm >>> 16) & 0xFFFF; + + // Generate code sequence with a combination of MOVZ or MOVN with MOVK. + if (high16 == 0) { + movz(32, dst, low16, 0); + numMovs = 1; + } else if (high16 == 0xFFFF) { + movn(32, dst, low16 ^ 0xFFFF, 0); + numMovs = 1; + } else if (low16 == 0) { + movz(32, dst, high16, 16); + numMovs = 1; + } else if (low16 == 0xFFFF) { + movn(32, dst, high16 ^ 0xFFFF, 16); + numMovs = 1; + } else { + // Neither of the 2 parts is all-0s or all-1s. Generate 2 instructions. + movz(32, dst, low16, 0); + movk(32, dst, high16, 16); + numMovs = 2; + } + if (needsImmAnnotation) { + annotateImmediateMovSequence(pos, numMovs); + } + } + /** * Generates a 64-bit immediate move code sequence. * * @param dst general purpose register. May not be null, stackpointer or zero-register. * @param imm the value to move into the register - * @param annotateImm Flag denoting if annotation should be added. + * @param needsImmAnnotation Flag denoting if annotation should be added. */ - private void mov64(Register dst, long imm, boolean annotateImm) { - // We have to move all non zero parts of the immediate in 16-bit chunks + private void mov64(Register dst, long imm, boolean needsImmAnnotation) { int numMovs = 0; int pos = position(); - boolean firstMove = true; - for (int offset = 0; offset < 64; offset += 16) { - int chunk = (int) (imm >> offset) & NumUtil.getNbitNumberInt(16); + int[] chunks = new int[4]; + int zeroCount = 0; + int negCount = 0; + + // Split 64-bit imm into 4 chunks and count the numbers of all-0 and all-1 chunks. + for (int i = 0; i < 4; i++) { + int chunk = (int) ((imm >>> (i * 16)) & 0xFFFFL); if (chunk == 0) { - continue; + zeroCount++; + } else if (chunk == 0xFFFF) { + negCount++; } - if (firstMove) { - movz(64, dst, chunk, offset); - firstMove = false; - } else { - movk(64, dst, chunk, offset); + chunks[i] = chunk; + } + + // Generate code sequence with a combination of MOVZ or MOVN with MOVK. + if (zeroCount == 4) { + // Generate only one MOVZ. + movz(64, dst, 0, 0); + numMovs = 1; + } else if (negCount == 4) { + // Generate only one MOVN. + movn(64, dst, 0, 0); + numMovs = 1; + } else if (zeroCount == 3) { + // Generate only one MOVZ. + for (int i = 0; i < 4; i++) { + if (chunks[i] != 0) { + movz(64, dst, chunks[i], i * 16); + break; + } + } + numMovs = 1; + } else if (negCount == 3) { + // Generate only one MOVN. + for (int i = 0; i < 4; i++) { + if (chunks[i] != 0xFFFF) { + movn(64, dst, chunks[i] ^ 0xFFFF, i * 16); + break; + } + } + numMovs = 1; + } else if (zeroCount == 2) { + // Generate one MOVZ and one MOVK. + int i; + for (i = 0; i < 4; i++) { + if (chunks[i] != 0) { + movz(64, dst, chunks[i], i * 16); + break; + } + } + for (int k = i + 1; k < 4; k++) { + if (chunks[k] != 0) { + movk(64, dst, chunks[k], k * 16); + break; + } + } + numMovs = 2; + } else if (negCount == 2) { + // Generate one MOVN and one MOVK. + int i; + for (i = 0; i < 4; i++) { + if (chunks[i] != 0xFFFF) { + movn(64, dst, chunks[i] ^ 0xFFFF, i * 16); + break; + } + } + for (int k = i + 1; k < 4; k++) { + if (chunks[k] != 0xFFFF) { + movk(64, dst, chunks[k], k * 16); + break; + } } - ++numMovs; + numMovs = 2; + } else if (zeroCount == 1) { + // Generate one MOVZ and two MOVKs. + int i; + for (i = 0; i < 4; i++) { + if (chunks[i] != 0) { + movz(64, dst, chunks[i], i * 16); + break; + } + } + int numMovks = 0; + for (int k = i + 1; k < 4; k++) { + if (chunks[k] != 0) { + movk(64, dst, chunks[k], k * 16); + numMovks++; + } + } + assert numMovks == 2; + numMovs = 3; + } else if (negCount == 1) { + // Generate one MOVN and two MOVKs. + int i; + for (i = 0; i < 4; i++) { + if (chunks[i] != 0xFFFF) { + movn(64, dst, chunks[i] ^ 0xFFFF, i * 16); + break; + } + } + int numMovks = 0; + for (int k = i + 1; k < 4; k++) { + if (chunks[k] != 0xFFFF) { + movk(64, dst, chunks[k], k * 16); + numMovks++; + } + } + assert numMovks == 2; + numMovs = 3; + } else { + // Generate one MOVZ and three MOVKs + movz(64, dst, chunks[0], 0); + movk(64, dst, chunks[1], 16); + movk(64, dst, chunks[2], 32); + movk(64, dst, chunks[3], 48); + numMovs = 4; } - assert !firstMove; - if (annotateImm) { + if (needsImmAnnotation) { annotateImmediateMovSequence(pos, numMovs); } } + /** + * Loads immediate into register. + * + * @param dst general purpose register. May not be null, zero-register or stackpointer. + * @param imm immediate loaded into register. + */ + public void mov(Register dst, int imm) { + mov(dst, imm, false); + } + /** * Loads immediate into register. * @@ -381,13 +532,30 @@ public void mov(Register dst, long imm) { * * @param dst general purpose register. May not be null, zero-register or stackpointer. * @param imm immediate loaded into register. - * @param annotateImm Flag to signal of the immediate value should be annotated. + * @param needsImmAnnotation Flag to signal of the immediate value should be annotated. + */ + public void mov(Register dst, int imm, boolean needsImmAnnotation) { + if (imm == 0) { + mov(32, dst, zr); + } else if (isLogicalImmediate(imm)) { + or(32, dst, zr, imm); + } else { + mov32(dst, imm, needsImmAnnotation); + } + } + + /** + * Loads immediate into register. + * + * @param dst general purpose register. May not be null, zero-register or stackpointer. + * @param imm immediate loaded into register. + * @param needsImmAnnotation Flag to signal of the immediate value should be annotated. */ - public void mov(Register dst, long imm, boolean annotateImm) { + public void mov(Register dst, long imm, boolean needsImmAnnotation) { assert dst.getRegisterCategory().equals(CPU); if (imm == 0L) { movx(dst, zr); - } else if (LogicalImmediateTable.isRepresentable(true, imm) != LogicalImmediateTable.Representable.NO) { + } else if (isLogicalImmediate(imm)) { or(64, dst, zr, imm); } else if (imm >> 32 == -1L && (int) imm < 0 && LogicalImmediateTable.isRepresentable((int) imm) != LogicalImmediateTable.Representable.NO) { // If the higher 32-bit are 1s and the sign bit of the lower 32-bits is set *and* we can @@ -397,20 +565,10 @@ public void mov(Register dst, long imm, boolean annotateImm) { mov(dst, (int) imm); sxt(64, 32, dst, dst); } else { - mov64(dst, imm, annotateImm); + mov64(dst, imm, needsImmAnnotation); } } - /** - * Loads immediate into register. - * - * @param dst general purpose register. May not be null, zero-register or stackpointer. - * @param imm immediate loaded into register. - */ - public void mov(Register dst, int imm) { - mov(dst, imm & 0xFFFF_FFFFL); - } - /** * Generates a 48-bit immediate move code sequence. The immediate may later be updated by * HotSpot. @@ -434,9 +592,9 @@ public void movNativeAddress(Register dst, long imm) { * * @param dst general purpose register. May not be null, stackpointer or zero-register. * @param imm The immediate address - * @param annotateImm Flag to signal of the immediate value should be annotated. + * @param needsImmAnnotation Flag to signal of the immediate value should be annotated. */ - public void movNativeAddress(Register dst, long imm, boolean annotateImm) { + public void movNativeAddress(Register dst, long imm, boolean needsImmAnnotation) { assert (imm & 0xFFFF_0000_0000_0000L) == 0; // We have to move all non zero parts of the immediate in 16-bit chunks boolean firstMove = true; @@ -450,7 +608,7 @@ public void movNativeAddress(Register dst, long imm, boolean annotateImm) { movk(64, dst, chunk, offset); } } - if (annotateImm) { + if (needsImmAnnotation) { annotateImmediateMovSequence(pos, 3); } assert !firstMove;