diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysFillTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysFillTest.java new file mode 100644 index 000000000000..85ac9d7c330c --- /dev/null +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysFillTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018, Oracle and/or its 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 jdk.graal.compiler.replacements.test; + +import java.util.Arrays; + +import org.junit.Test; + +import jdk.graal.compiler.nodes.Invoke; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; +import jdk.graal.compiler.replacements.ConstantBindingParameterPlugin; +import jdk.graal.compiler.replacements.nodes.ArrayFillNode; +import jdk.vm.ci.code.InstalledCode; +import jdk.vm.ci.meta.ResolvedJavaMethod; + +public class ArraysFillTest extends ArraysSubstitutionsTestBase { + private static final int[] LENGTHS = {0, 1, 2, 3, 4, 5, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 31, 65, 127, 255, 1023, 1024, 1025}; + + private void testFills(Class type, Object constant) { + for (int length : LENGTHS) { + testFillsSubstitution(new ArraysFillTestConfig(type, length, constant)); + } + } + + @Test + public void testFillBooleanFalse() { + testFills(boolean.class, false); + } + + @Test + public void testFillBooleanTrue() { + testFills(boolean.class, true); + } + + @Test + public void testFillCharA() { + testFills(char.class, 'A'); + } + + @Test + public void testFillChar0() { + testFills(char.class, '0'); + } + + @Test + public void testFillByteMin() { + testFills(byte.class, Byte.MIN_VALUE); + } + + @Test + public void testFillByteMax() { + testFills(byte.class, Byte.MAX_VALUE); + } + + @Test + public void testFillShortMin() { + testFills(short.class, Short.MIN_VALUE); + } + + @Test + public void testFillShortMax() { + testFills(short.class, Short.MAX_VALUE); + } + + @Test + public void testFillIntMin() { + testFills(int.class, Integer.MIN_VALUE); + } + + @Test + public void testFillIntMax() { + testFills(int.class, Integer.MAX_VALUE); + } + + @Test + public void testFillLongMin() { + testFills(long.class, Long.MIN_VALUE); + } + + @Test + public void testFillLongMax() { + testFills(long.class, Long.MAX_VALUE); + } + + @Test + public void testFillFloatMin() { + testFills(float.class, Float.MIN_VALUE); + } + + @Test + public void testFillFloatMax() { + testFills(float.class, Float.MAX_VALUE); + } + + @Test + public void testFillDoubleMin() { + testFills(double.class, Double.MIN_VALUE); + } + + @Test + public void testFillDoubleMax() { + testFills(double.class, Double.MAX_VALUE); + } + + private Object[] constantArgs; + + @Override + protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) { + if (constantArgs != null) { + ConstantBindingParameterPlugin constantBinding = new ConstantBindingParameterPlugin(constantArgs, this.getMetaAccess(), this.getSnippetReflection()); + conf.getPlugins().appendParameterPlugin(constantBinding); + } + return super.editGraphBuilderConfiguration(conf); + } + + protected void testFillsSubstitution(ArraysFillTestConfig config) { + ResolvedJavaMethod realMethod = getResolvedJavaMethod(Arrays.class, "fill", config.parameterType()); + ResolvedJavaMethod testMethod = getResolvedJavaMethod(config.testMethodName()); + StructuredGraph graph = testGraph(config.testMethodName()); + + // Check to see if the resulting graph contains the expected node + StructuredGraph replacement = getReplacements().getInlineSubstitution(realMethod, 0, false, Invoke.InlineControl.Normal, false, null, graph.allowAssumptions(), graph.getOptions()); + if (replacement == null) { + assertInGraph(graph, ArrayFillNode.class); + } + + // Force compilation + InstalledCode code = getCode(testMethod, null, true); + assert code != null; + + Object array1 = config.newArray(); + Object array2 = config.newArray(); + Object array3 = config.newArray(); + + invokeSafe(realMethod, null, array1, config.getConstant()); + invokeSafe(testMethod, null, array2, config.getConstant()); + executeVarargsSafe(code, array3, config.getConstant()); + + // Verify that the original method and the substitution produce the same value + assertDeepEquals(array1, array2); + + // Verify that the generated code and the original produce the same value + assertDeepEquals(array2, array3); + } +} diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysFillTestConfig.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysFillTestConfig.java new file mode 100644 index 000000000000..885b69e2a7e8 --- /dev/null +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysFillTestConfig.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018, Oracle and/or its 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 jdk.graal.compiler.replacements.test; + +import jdk.graal.compiler.replacements.test.ArraysSubstitutionsTestBase.ArrayBuilder; + +class ArraysFillTestConfig { + private Class type = null; + private Object constant = null; + private int length = 0; + + private Object parameterTypes[] = { + new Class[]{boolean[].class, boolean.class}, + new Class[]{byte[].class, byte.class}, + new Class[]{char[].class, char.class}, + new Class[]{short[].class, short.class}, + new Class[]{int[].class, int.class}, + new Class[]{long[].class, long.class}, + new Class[]{float[].class, float.class}, + new Class[]{double[].class, double.class} + }; + + private ArrayBuilder builders[] = { + ArraysSubstitutionsTestBase::booleanArray, + ArraysSubstitutionsTestBase::byteArray, + ArraysSubstitutionsTestBase::charArray, + ArraysSubstitutionsTestBase::shortArray, + ArraysSubstitutionsTestBase::intArray, + ArraysSubstitutionsTestBase::longArray, + ArraysSubstitutionsTestBase::floatArray, + ArraysSubstitutionsTestBase::doubleArray, + }; + + private String testMethodNames[] = { + "arraysFillBoolean", + "arraysFillByte", + "arraysFillChar", + "arraysFillShort", + "arraysFillInt", + "arraysFillLong", + "arraysFillFloat", + "arraysFillDouble", + }; + + public ArraysFillTestConfig(Class type, int length, Object constant) { + this.type = type; + this.length = length; + this.constant = constant; + } + + public String testMethodName() throws IllegalArgumentException { + return testMethodNames[index()]; + } + + public Object newArray() throws IllegalArgumentException { + ArrayBuilder builder = builders[index()]; + return builder.newArray(this.length, 0, 0); + } + + public Class[] parameterType() throws IllegalArgumentException { + return (Class[]) parameterTypes[index()]; + } + + public Object getConstant() { + return this.constant; + } + + private int index() { + if (type == boolean.class) { + return 0; + } else if (type == byte.class) { + return 1; + } else if (type == char.class) { + return 2; + } else if (type == short.class) { + return 3; + } else if (type == int.class) { + return 4; + } else if (type == long.class) { + return 5; + } else if (type == float.class) { + return 6; + } else if (type == double.class) { + return 7; + } else { + throw new IllegalArgumentException("bla"); + } + } +} \ No newline at end of file diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysSubstitutionsTestBase.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysSubstitutionsTestBase.java index 2e0ce944624e..fd973daa1f96 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysSubstitutionsTestBase.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/replacements/test/ArraysSubstitutionsTestBase.java @@ -63,6 +63,38 @@ public static boolean arraysEqualsLong(long[] a, long[] b) { return Arrays.equals(a, b); } + public static void arraysFillBoolean(boolean[] a, boolean b) { + Arrays.fill(a, b); + } + + public static void arraysFillByte(byte[] a, byte b) { + Arrays.fill(a, b); + } + + public static void arraysFillChar(char[] a, char b) { + Arrays.fill(a, b); + } + + public static void arraysFillShort(short[] a, short b) { + Arrays.fill(a, b); + } + + public static void arraysFillInt(int[] a, int b) { + Arrays.fill(a, b); + } + + public static void arraysFillLong(long[] a, long b) { + Arrays.fill(a, b); + } + + public static void arraysFillFloat(float[] a, float b) { + Arrays.fill(a, b); + } + + public static void arraysFillDouble(double[] a, double b) { + Arrays.fill(a, b); + } + interface ArrayBuilder { Object newArray(int length, int firstValue, int lastValue); } @@ -150,4 +182,32 @@ static long[] longArray(int length, int firstValue, int lastValue) { } return arr; } + + static float[] floatArray(int length, float firstValue, float lastValue) { + float[] arr = new float[length]; + for (int i = 0; i < length; i++) { + arr[i] = i; + } + if (length > 0) { + arr[0] = firstValue; + } + if (length > 1) { + arr[length - 1] = lastValue; + } + return arr; + } + + static double[] doubleArray(int length, double firstValue, double lastValue) { + double[] arr = new double[length]; + for (int i = 0; i < length; i++) { + arr[i] = i; + } + if (length > 0) { + arr[0] = firstValue; + } + if (length > 1) { + arr[length - 1] = lastValue; + } + return arr; + } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Assembler.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Assembler.java index cbfa840921c7..6fdc357a765a 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Assembler.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Assembler.java @@ -2462,6 +2462,21 @@ public void bfm(int size, Register dst, Register src, int r, int s) { bitfieldInstruction(BFM, dst, src, r, s, generalFromSize(size)); } + /** + * C6.2.30 Bitfield insert. + * + * @param size register size. Has to be 32 or 64. + * @param dst general purpose register. May not be null, stackpointer or zero-register. + * @param src general purpose register. May not be null, stackpointer or zero-register. + * @param lsb start index of target register to override with bits from src register. + * @param width number of bits to copy from src register. + */ + public void bfi(int size, Register dst, Register src, int lsb, int width) { + assert verifySizeAndRegistersRR(size, dst, src); + + bfm(size, dst, src, ((size - lsb) & (size - 1)), (width - 1)); + } + /** * C6.2.337 Unsigned bitfield move. * diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/aarch64/AArch64LIRGenerator.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/aarch64/AArch64LIRGenerator.java index e987e922814f..aca952ad0ef1 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/aarch64/AArch64LIRGenerator.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/aarch64/AArch64LIRGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2024, Oracle and/or its 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 @@ -55,6 +55,7 @@ import jdk.graal.compiler.lir.aarch64.AArch64ArithmeticOp; import jdk.graal.compiler.lir.aarch64.AArch64ArrayCompareToOp; import jdk.graal.compiler.lir.aarch64.AArch64ArrayCopyWithConversionsOp; +import jdk.graal.compiler.lir.aarch64.AArch64ArrayFillOp; import jdk.graal.compiler.lir.aarch64.AArch64ArrayEqualsOp; import jdk.graal.compiler.lir.aarch64.AArch64ArrayIndexOfOp; import jdk.graal.compiler.lir.aarch64.AArch64ArrayRegionCompareToOp; @@ -623,6 +624,11 @@ public void emitArrayCopyWithConversion(EnumSet runtimeCheckedCPUFeatures, Va emitConvertNullToZero(arrayDst), asAllocatable(offsetDst), emitConvertNullToZero(arraySrc), asAllocatable(offsetSrc), asAllocatable(length), asAllocatable(dynamicStrides))); } + @Override + public void emitArrayFill(JavaKind kind, EnumSet runtimeCheckedCPUFeatures, Value array, Value arrayBaseOffset, Value length, Value value) { + append(new AArch64ArrayFillOp(kind, emitConvertNullToZero(array), asAllocatable(arrayBaseOffset), asAllocatable(length), asAllocatable(value))); + } + @Override public Variable emitArrayEquals(JavaKind kind, EnumSet runtimeCheckedCPUFeatures, Value arrayA, Value offsetA, Value arrayB, Value offsetB, Value length) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hightiercodegen/NodeLowerer.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hightiercodegen/NodeLowerer.java index f265c0be4994..aa6cd4ab3598 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hightiercodegen/NodeLowerer.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hightiercodegen/NodeLowerer.java @@ -97,6 +97,7 @@ import jdk.graal.compiler.nodes.java.StoreIndexedNode; import jdk.graal.compiler.nodes.memory.ReadNode; import jdk.graal.compiler.nodes.spi.ValueProxy; +import jdk.graal.compiler.replacements.nodes.ArrayFillNode; import jdk.graal.compiler.replacements.nodes.ArrayEqualsNode; import jdk.graal.compiler.replacements.nodes.BasicArrayCopyNode; import jdk.graal.compiler.replacements.nodes.BinaryMathIntrinsicNode; @@ -255,6 +256,8 @@ protected void dispatch(Node node) { lower((InstanceOfNode) node); } else if (node instanceof InstanceOfDynamicNode) { lower((InstanceOfDynamicNode) node); + } else if (node instanceof ArrayFillNode) { + lower((ArrayFillNode) node); } else if (node instanceof ArrayEqualsNode) { lower((ArrayEqualsNode) node); } else if (node instanceof NewMultiArrayNode) { @@ -407,6 +410,8 @@ protected void handleUnknownNodeType(Node node) { protected abstract void lower(ArrayEqualsNode node); + protected abstract void lower(ArrayFillNode node); + protected abstract void lower(InstanceOfDynamicNode node); protected abstract void lower(InstanceOfNode node); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java index 81207382c02b..ba91bdb41583 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/meta/HotSpotHostForeignCallsProvider.java @@ -140,6 +140,7 @@ import jdk.graal.compiler.replacements.nodes.AESNode; import jdk.graal.compiler.replacements.nodes.ArrayCompareToForeignCalls; import jdk.graal.compiler.replacements.nodes.ArrayCopyWithConversionsForeignCalls; +import jdk.graal.compiler.replacements.nodes.ArrayFillNode; import jdk.graal.compiler.replacements.nodes.ArrayEqualsForeignCalls; import jdk.graal.compiler.replacements.nodes.ArrayEqualsWithMaskForeignCalls; import jdk.graal.compiler.replacements.nodes.ArrayIndexOfForeignCalls; @@ -684,6 +685,7 @@ protected void registerMathStubs(GraalHotSpotVMConfig hotSpotVMConfig, HotSpotPr private void registerSnippetStubs(HotSpotProviders providers, OptionValues options) { linkSnippetStubs(providers, options, IntrinsicStubsGen::new, ArrayIndexOfForeignCalls.STUBS); + linkSnippetStubs(providers, options, IntrinsicStubsGen::new, ArrayFillNode.STUBS); linkSnippetStubs(providers, options, IntrinsicStubsGen::new, ArrayEqualsForeignCalls.STUBS); linkSnippetStubs(providers, options, IntrinsicStubsGen::new, ArrayEqualsWithMaskForeignCalls.STUBS); linkSnippetStubs(providers, options, IntrinsicStubsGen::new, ArrayCompareToForeignCalls.STUBS); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/stubs/IntrinsicStubs.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/stubs/IntrinsicStubs.java index 28fe0569432b..de38d9700849 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/stubs/IntrinsicStubs.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/stubs/IntrinsicStubs.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its 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 @@ -31,6 +31,7 @@ import jdk.graal.compiler.replacements.nodes.ArrayCompareToNode; import jdk.graal.compiler.replacements.nodes.ArrayCopyWithConversionsNode; import jdk.graal.compiler.replacements.nodes.ArrayEqualsNode; +import jdk.graal.compiler.replacements.nodes.ArrayFillNode; import jdk.graal.compiler.replacements.nodes.ArrayIndexOfNode; import jdk.graal.compiler.replacements.nodes.ArrayRegionCompareToNode; import jdk.graal.compiler.replacements.nodes.ArrayRegionEqualsNode; @@ -55,6 +56,7 @@ @GeneratedStubsHolder(targetVM = "hotspot", sources = { ArrayIndexOfNode.class, ArrayEqualsNode.class, + ArrayFillNode.class, ArrayRegionEqualsNode.class, ArrayRegionEqualsWithMaskNode.class, ArrayCompareToNode.class, diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/aarch64/AArch64ArrayFillOp.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/aarch64/AArch64ArrayFillOp.java new file mode 100644 index 000000000000..fa6d9cf4db42 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/aarch64/AArch64ArrayFillOp.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2024, Oracle and/or its 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 jdk.graal.compiler.lir.aarch64; + +import static jdk.graal.compiler.asm.aarch64.AArch64MacroAssembler.PREFERRED_LOOP_ALIGNMENT; +import static jdk.graal.compiler.asm.aarch64.AArch64Address.AddressingMode.IMMEDIATE_PAIR_SIGNED_SCALED; +import static jdk.graal.compiler.asm.aarch64.AArch64Address.AddressingMode.IMMEDIATE_POST_INDEXED; +import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.REG; +import static jdk.vm.ci.aarch64.AArch64.r10; +import static jdk.vm.ci.aarch64.AArch64.r5; +import static jdk.vm.ci.aarch64.AArch64.r6; +import static jdk.vm.ci.aarch64.AArch64.r7; +import static jdk.vm.ci.aarch64.AArch64.r8; +import static jdk.vm.ci.aarch64.AArch64.r9; +import static jdk.vm.ci.code.ValueUtil.asRegister; + +import jdk.graal.compiler.asm.Label; +import jdk.graal.compiler.asm.aarch64.AArch64Address; +import jdk.graal.compiler.asm.aarch64.AArch64Assembler.ConditionFlag; +import jdk.graal.compiler.asm.aarch64.AArch64Assembler.ShiftType; +import jdk.graal.compiler.asm.aarch64.AArch64MacroAssembler; +import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.lir.LIRInstructionClass; +import jdk.graal.compiler.lir.Opcode; +import jdk.graal.compiler.lir.asm.CompilationResultBuilder; +import jdk.vm.ci.aarch64.AArch64Kind; +import jdk.vm.ci.code.Register; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.Value; + +/** + * Emits code which fills an array with a constant value. The assembly code in this intrinsic was + * based in the HotSpot's version of the same intrinsic. + */ +@Opcode("ARRAYS_FILL") +public final class AArch64ArrayFillOp extends AArch64ComplexVectorOp { + public static final LIRInstructionClass TYPE = LIRInstructionClass.create(AArch64ArrayFillOp.class); + + private JavaKind elementType; + @Alive({REG}) protected Value array; + @Alive({REG}) protected Value arrayBaseOffset; + @Alive({REG}) protected Value length; + @Alive({REG}) protected Value value; + @Temp protected Value[] temps; + + public AArch64ArrayFillOp(JavaKind kind, Value array, Value arrayBaseOffset, Value length, Value value) { + super(TYPE); + + GraalError.guarantee(array.getPlatformKind() == AArch64Kind.QWORD, "pointer value expected"); + GraalError.guarantee(length.getPlatformKind() == AArch64Kind.DWORD, "integer value expected in 'length'"); + GraalError.guarantee(value.getPlatformKind() == AArch64Kind.DWORD, "integer value expected in 'value'."); + + this.elementType = kind; + this.array = array; + this.arrayBaseOffset = arrayBaseOffset; + this.length = length; + this.value = value; + + this.temps = new Value[]{ + r5.asValue(), + r6.asValue(), + r7.asValue(), + r8.asValue(), + r9.asValue(), + r10.asValue()}; + } + + @Override + @SuppressWarnings("fallthrough") + public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { + int shift = -1; + int operationWidthB = 8; + int operationWidthH = 16; + int operationWidthW = 32; + int operationWidthDW = 64; + + Register targetArray = r7; + Register valueToFillWith = r8; + Register numberOfElements = r9; + Register numberOfEightByteWords = r10; + + Label fillElementsLabel = new Label(); + Label skipAlign1Label = new Label(); + Label skipAlign2Label = new Label(); + Label skipAlign4Label = new Label(); + Label fill2Label = new Label(); + Label fill4Label = new Label(); + Label doneLabel = new Label(); + + masm.add(operationWidthDW, targetArray, asRegister(this.array), asRegister(this.arrayBaseOffset)); + masm.mov(operationWidthDW, valueToFillWith, asRegister(value)); + masm.mov(operationWidthDW, numberOfElements, asRegister(length)); + + // Will jump to fillElementsLabel if there are less than 8 bytes to fill in target array. + // Before jumping, adjust valueToFillWith to contain the 'pattern' to fill target + // array with. + switch (this.elementType) { + case JavaKind.Boolean: + case JavaKind.Byte: + shift = 0; + masm.compare(operationWidthW, numberOfElements, 8); + masm.bfi(operationWidthW, valueToFillWith, valueToFillWith, 8, 8); + masm.bfi(operationWidthW, valueToFillWith, valueToFillWith, 16, 16); + + // jump to fillElementsLabel if numberOfElements < 8 elements + masm.branchConditionally(ConditionFlag.LO, fillElementsLabel); + break; + case JavaKind.Short: + // Fallthrough + case JavaKind.Char: + shift = 1; + masm.compare(operationWidthW, numberOfElements, 4); + masm.bfi(operationWidthW, valueToFillWith, valueToFillWith, 16, 16); + // jump to fillElementsLabel if numberOfElements < 4 elements + masm.branchConditionally(ConditionFlag.LO, fillElementsLabel); + break; + case JavaKind.Int: + case JavaKind.Float: + shift = 2; + masm.compare(operationWidthW, numberOfElements, 2); + // jump to fillElementsLabel if numberOfElements < 2 elements + masm.branchConditionally(ConditionFlag.LO, fillElementsLabel); + break; + case JavaKind.Long: + case JavaKind.Double: + shift = 3; + masm.compare(operationWidthW, numberOfElements, 1); + // jump to doneLabel if numberOfElements < 1 elements + masm.branchConditionally(ConditionFlag.LO, doneLabel); + break; + default: + GraalError.shouldNotReachHere("Should not reach here."); + } + + // Align source address at 8 bytes address boundary. + switch (this.elementType) { + case JavaKind.Boolean: + case JavaKind.Byte: + masm.tbz(targetArray, 0, skipAlign1Label); + masm.str(operationWidthB, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthB, IMMEDIATE_POST_INDEXED, targetArray, 1)); + masm.sub(operationWidthW, numberOfElements, numberOfElements, 1); + masm.bind(skipAlign1Label); + // Fallthrough + case JavaKind.Short: + // Fallthrough + case JavaKind.Char: + masm.tbz(targetArray, 1, skipAlign2Label); + masm.str(operationWidthH, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthH, IMMEDIATE_POST_INDEXED, targetArray, 2)); + masm.sub(operationWidthW, numberOfElements, numberOfElements, 2 >> shift); + masm.bind(skipAlign2Label); + // Fallthrough + case JavaKind.Int: + case JavaKind.Float: + masm.tbz(targetArray, 2, skipAlign4Label); + masm.str(operationWidthW, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthW, IMMEDIATE_POST_INDEXED, targetArray, 4)); + masm.sub(operationWidthW, numberOfElements, numberOfElements, 4 >> shift); + masm.bind(skipAlign4Label); + break; + case JavaKind.Long: + case JavaKind.Double: + break; + default: + GraalError.shouldNotReachHere("Should not reach here."); + } + + // Divide numberOfElements by 2^(3-shift), i.e., divide numberOfElements by the + // number of elements that fit into an 8 byte word. + masm.lsr(operationWidthW, numberOfEightByteWords, numberOfElements, 3 - shift); + + // If valueToFillWith isn't already 64 bits we'll make it so + if (this.elementType != JavaKind.Long && this.elementType != JavaKind.Double) { + masm.bfi(operationWidthDW, valueToFillWith, valueToFillWith, 32, 32); + } + + // numberOfElements = numberOfElements - numberOfEightByteWords * elementsByEightByteWord + masm.sub(operationWidthW, numberOfElements, numberOfElements, numberOfEightByteWords, ShiftType.LSL, 3 - shift); + + // fill numberOfEightByteWords bytes of the target array + fillWords(masm, targetArray, numberOfEightByteWords, valueToFillWith); + + // Remaining numberOfElements is less than 8 bytes. Fill it by a single store. + // Note that the total length is no less than 8 bytes. + if (this.elementType == JavaKind.Byte || this.elementType == JavaKind.Boolean || this.elementType == JavaKind.Short || this.elementType == JavaKind.Char) { + masm.cbz(operationWidthW, numberOfElements, doneLabel); + masm.add(operationWidthDW, targetArray, targetArray, numberOfElements, ShiftType.LSL, shift); + masm.str(operationWidthDW, valueToFillWith, masm.makeAddress(operationWidthDW, targetArray, -8)); + masm.jmp(doneLabel); + } + + // Handle copies less than 8 bytes. + masm.bind(fillElementsLabel); + switch (this.elementType) { + case JavaKind.Boolean: + case JavaKind.Byte: + masm.tbz(numberOfElements, 0, fill2Label); + masm.str(operationWidthB, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthB, IMMEDIATE_POST_INDEXED, targetArray, 1)); + masm.bind(fill2Label); + masm.tbz(numberOfElements, 1, fill4Label); + masm.str(operationWidthH, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthH, IMMEDIATE_POST_INDEXED, targetArray, 2)); + masm.bind(fill4Label); + masm.tbz(numberOfElements, 2, doneLabel); + masm.str(operationWidthW, valueToFillWith, AArch64Address.createBaseRegisterOnlyAddress(operationWidthW, targetArray)); + break; + case JavaKind.Short: + case JavaKind.Char: + masm.tbz(numberOfElements, 0, fill4Label); + masm.str(operationWidthH, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthH, IMMEDIATE_POST_INDEXED, targetArray, 2)); + masm.bind(fill4Label); + masm.tbz(numberOfElements, 1, doneLabel); + masm.str(operationWidthW, valueToFillWith, AArch64Address.createBaseRegisterOnlyAddress(operationWidthW, targetArray)); + break; + case JavaKind.Int: + case JavaKind.Float: + masm.cbz(operationWidthW, numberOfElements, doneLabel); + masm.str(operationWidthW, valueToFillWith, AArch64Address.createBaseRegisterOnlyAddress(operationWidthW, targetArray)); + break; + case JavaKind.Long: + case JavaKind.Double: + break; + default: + GraalError.shouldNotReachHere("Should not reach here."); + } + masm.bind(doneLabel); + } + + /** + * Algorithm: + * + * + * if (cnt == 0) return ; + * if ((p & 8) != 0) *p++ = v; + * + * scratch1 = cnt & 14; + * cnt -= scratch1; + * p += scratch1; + * + * switch (scratch1 / 2) { + * do { + * cnt -= 16; + * p[-16] = v; + * p[-15] = v; + * case 7: + * p[-14] = v; + * p[-13] = v; + * case 6: + * p[-12] = v; + * p[-11] = v; + * // ... + * case 1: + * p[-2] = v; + * p[-1] = v; + * case 0: + * p += 16; + * } while (cnt); + * } + * + * if ((cnt & 1) == 1) { + * *p++ = v; + * } + * + * + * Base will point to the end of the buffer after filling. + * + * @param masm + * @param targetArray Address of a buffer to be filled, 8 bytes aligned. + * @param numberOfEightByteWords Count in 8-byte unit. + * @param valueToFillWith Value to be filled with. + */ + @SuppressWarnings("static-method") + private void fillWords(AArch64MacroAssembler masm, Register targetArray, Register numberOfEightByteWords, Register valueToFillWith) { + int operationWidthDW = 64; + int unroll = 8; + + Label finishedLabel = new Label(); + Label skipLabel = new Label(); + Label entryLabel = new Label(); + Label loopHeadLabel = new Label(); + + // If nothing to do just jump to finishedLabel + masm.cbz(operationWidthDW, numberOfEightByteWords, finishedLabel); + + // Because we didn't jump in the previous instruction then we certainly + // have at least 8 bytes to fill in the target array. + + masm.tbz(targetArray, 3, skipLabel); + masm.str(operationWidthDW, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthDW, IMMEDIATE_POST_INDEXED, targetArray, 8)); + masm.sub(operationWidthDW, numberOfEightByteWords, numberOfEightByteWords, 1); + masm.bind(skipLabel); + + masm.and(operationWidthDW, r5, numberOfEightByteWords, (unroll - 1) * 2); + masm.sub(operationWidthDW, numberOfEightByteWords, numberOfEightByteWords, r5); + masm.add(operationWidthDW, targetArray, targetArray, r5, ShiftType.LSL, 3); + masm.adr(r6, entryLabel); + masm.sub(operationWidthDW, r6, r6, r5, ShiftType.LSL, 1); + masm.jmp(r6); + + masm.align(PREFERRED_LOOP_ALIGNMENT); + masm.bind(loopHeadLabel); + masm.add(operationWidthDW, targetArray, targetArray, unroll * 16); + for (int i = -unroll; i < 0; i++) { + masm.stp(operationWidthDW, valueToFillWith, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthDW, IMMEDIATE_PAIR_SIGNED_SCALED, targetArray, i * 16)); + } + masm.bind(entryLabel); + masm.subs(operationWidthDW, numberOfEightByteWords, numberOfEightByteWords, unroll * 2); + masm.branchConditionally(ConditionFlag.GE, loopHeadLabel); + + masm.tbz(numberOfEightByteWords, 0, finishedLabel); + masm.str(operationWidthDW, valueToFillWith, AArch64Address.createImmediateAddress(operationWidthDW, IMMEDIATE_POST_INDEXED, targetArray, 8)); + masm.bind(finishedLabel); + } +} diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/gen/LIRGeneratorTool.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/gen/LIRGeneratorTool.java index ce3cf3591bb5..41183f4773d7 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/gen/LIRGeneratorTool.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/lir/gen/LIRGeneratorTool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, Oracle and/or its 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 @@ -232,6 +232,11 @@ default Variable emitVectorizedHashCode(EnumSet runtimeCheckedCPUFeatures, Va throw GraalError.unimplemented("vectorizedHashCode substitution is not implemented on this architecture"); // ExcludeFromJacocoGeneratedReport } + @SuppressWarnings("unused") + default void emitArrayFill(JavaKind commonElementKind, EnumSet runtimeCheckedCPUFeatures, Value array, Value arrayBaseOffset, Value length, Value value) { + throw GraalError.unimplemented("Arrays.fill substitution is not implemented on this architecture"); // ExcludeFromJacocoGeneratedReport + } + @SuppressWarnings("unused") default Variable emitArrayEquals(JavaKind commonElementKind, EnumSet runtimeCheckedCPUFeatures, Value arrayA, Value offsetA, Value arrayB, Value offsetB, Value length) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/StandardGraphBuilderPlugins.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/StandardGraphBuilderPlugins.java index cfd551a4a559..c336653769e4 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/StandardGraphBuilderPlugins.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/StandardGraphBuilderPlugins.java @@ -189,6 +189,7 @@ import jdk.graal.compiler.replacements.nodes.AESNode; import jdk.graal.compiler.replacements.nodes.AESNode.CryptMode; import jdk.graal.compiler.replacements.nodes.ArrayEqualsNode; +import jdk.graal.compiler.replacements.nodes.ArrayFillNode; import jdk.graal.compiler.replacements.nodes.BigIntegerMulAddNode; import jdk.graal.compiler.replacements.nodes.BigIntegerMultiplyToLenNode; import jdk.graal.compiler.replacements.nodes.BigIntegerSquareToLenNode; @@ -276,7 +277,7 @@ public static void registerInvocationPlugins(SnippetReflectionProvider snippetRe registerThreadPlugins(plugins, replacements); if (supportsStubBasedPlugins) { - registerArraysPlugins(plugins, replacements); + registerArraysPlugins(plugins, replacements, lowerer.getTarget().arch); registerAESPlugins(plugins, replacements, lowerer.getTarget().arch); registerGHASHPlugin(plugins, replacements, lowerer.getTarget().arch); registerBigIntegerPlugins(plugins, replacements); @@ -412,6 +413,31 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec }); } + public static class ArrayFillInvocationPlugin extends InvocationPlugin { + private final JavaKind kind; + + public ArrayFillInvocationPlugin(JavaKind kind, Type... argumentTypes) { + super("fill", argumentTypes); + this.kind = kind; + } + + @SuppressWarnings("try") + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode array, ValueNode value) { + ConstantNode arrayBaseOffset = ConstantNode.forLong(b.getMetaAccess().getArrayBaseOffset(this.kind), b.getGraph()); + ValueNode nonNullArray = b.nullCheckedValue(array, DeoptimizationAction.None); + ValueNode arrayLength = b.add(new ArrayLengthNode(nonNullArray)); + ValueNode castedValue = value; + if (this.kind == JavaKind.Float) { + castedValue = ReinterpretNode.create(JavaKind.Int, value, NodeView.DEFAULT); + } else if (this.kind == JavaKind.Double) { + castedValue = ReinterpretNode.create(JavaKind.Long, value, NodeView.DEFAULT); + } + b.add(new ArrayFillNode(nonNullArray, arrayBaseOffset, arrayLength, castedValue, this.kind)); + return true; + } + } + public static class ArrayEqualsInvocationPlugin extends InvocationPlugin { private final JavaKind kind; @@ -497,7 +523,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec } } - private static void registerArraysPlugins(InvocationPlugins plugins, Replacements replacements) { + private static void registerArraysPlugins(InvocationPlugins plugins, Replacements replacements, Architecture arch) { Registration r = new Registration(plugins, Arrays.class, replacements); r.register(new ArrayEqualsInvocationPlugin(JavaKind.Boolean, boolean[].class, boolean[].class)); r.register(new ArrayEqualsInvocationPlugin(JavaKind.Byte, byte[].class, byte[].class)); @@ -505,6 +531,15 @@ private static void registerArraysPlugins(InvocationPlugins plugins, Replacement r.register(new ArrayEqualsInvocationPlugin(JavaKind.Char, char[].class, char[].class)); r.register(new ArrayEqualsInvocationPlugin(JavaKind.Int, int[].class, int[].class)); r.register(new ArrayEqualsInvocationPlugin(JavaKind.Long, long[].class, long[].class)); + + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Boolean, boolean[].class, boolean.class)); + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Byte, byte[].class, byte.class)); + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Char, char[].class, char.class)); + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Short, short[].class, short.class)); + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Int, int[].class, int.class)); + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Float, float[].class, float.class)); + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Long, long[].class, long.class)); + r.registerConditional(ArrayFillNode.isSupported(arch), new ArrayFillInvocationPlugin(JavaKind.Double, double[].class, double.class)); } private static void registerArrayPlugins(InvocationPlugins plugins, Replacements replacements) { diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/nodes/ArrayFillNode.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/nodes/ArrayFillNode.java new file mode 100644 index 000000000000..8670596c54b2 --- /dev/null +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/nodes/ArrayFillNode.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2024, Oracle and/or its 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 jdk.graal.compiler.replacements.nodes; + +import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.HAS_SIDE_EFFECT; + +import java.util.EnumSet; + +import org.graalvm.word.LocationIdentity; +import org.graalvm.word.Pointer; + +import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor; +import jdk.graal.compiler.core.common.type.StampFactory; +import jdk.graal.compiler.graph.NodeClass; +import jdk.graal.compiler.lir.GenerateStub; +import jdk.graal.compiler.nodeinfo.InputType; +import jdk.graal.compiler.nodeinfo.NodeCycles; +import jdk.graal.compiler.nodeinfo.NodeInfo; +import jdk.graal.compiler.nodeinfo.NodeSize; +import jdk.graal.compiler.nodes.FrameState; +import jdk.graal.compiler.nodes.NamedLocationIdentity; +import jdk.graal.compiler.nodes.StateSplit; +import jdk.graal.compiler.nodes.ValueNode; +import jdk.graal.compiler.nodes.memory.MemoryAccess; +import jdk.graal.compiler.nodes.spi.NodeLIRBuilderTool; +import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.code.Architecture; +import jdk.vm.ci.meta.JavaKind; + +// JaCoCo Exclude + +/** + * Fills in an array with a given constant value. + */ +@NodeInfo(cycles = NodeCycles.CYCLES_UNKNOWN, size = NodeSize.SIZE_UNKNOWN, allowedUsageTypes = InputType.Memory) +public class ArrayFillNode extends MemoryKillStubIntrinsicNode + implements StateSplit, MemoryAccess { + + public static final NodeClass TYPE = NodeClass.create(ArrayFillNode.class); + private static final ForeignCallDescriptor BOOLEAN_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Boolean); + private static final ForeignCallDescriptor BYTE_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Byte); + private static final ForeignCallDescriptor CHAR_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Char); + private static final ForeignCallDescriptor SHORT_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Short); + private static final ForeignCallDescriptor INT_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Int); + private static final ForeignCallDescriptor LONG_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Long); + private static final ForeignCallDescriptor FLOAT_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Float); + private static final ForeignCallDescriptor DOUBLE_ARRAY_FILL_STUB = createForeignCallDescriptor(JavaKind.Double); + + public static final ForeignCallDescriptor[] STUBS = { + BOOLEAN_ARRAY_FILL_STUB, + BYTE_ARRAY_FILL_STUB, + CHAR_ARRAY_FILL_STUB, + SHORT_ARRAY_FILL_STUB, + INT_ARRAY_FILL_STUB, + LONG_ARRAY_FILL_STUB, + FLOAT_ARRAY_FILL_STUB, + DOUBLE_ARRAY_FILL_STUB, + }; + + protected JavaKind elementKind; + + @Input protected ValueNode arrayBase; + @Input protected ValueNode offsetToFirstElement; + @Input protected ValueNode arrayLength; + @Input protected ValueNode valueToFillWith; + + public ArrayFillNode(ValueNode arrayBase, ValueNode offsetToFirstElement, ValueNode arrayLength, ValueNode valueToFillWith, @ConstantNodeParameter JavaKind elementKind) { + super(TYPE, StampFactory.forVoid(), null, LocationIdentity.any()); + this.arrayBase = arrayBase; + this.offsetToFirstElement = offsetToFirstElement; + this.arrayLength = arrayLength; + this.valueToFillWith = valueToFillWith; + this.elementKind = elementKind; + } + + public ArrayFillNode(ValueNode arrayBase, ValueNode offsetToFirstElement, ValueNode arrayLength, ValueNode valueToFillWith, @ConstantNodeParameter JavaKind elementKind, + @ConstantNodeParameter EnumSet runtimeCheckedCPUFeatures) { + super(TYPE, StampFactory.forVoid(), runtimeCheckedCPUFeatures, LocationIdentity.any()); + this.arrayBase = arrayBase; + this.offsetToFirstElement = offsetToFirstElement; + this.arrayLength = arrayLength; + this.valueToFillWith = valueToFillWith; + this.elementKind = elementKind; + } + + @NodeIntrinsic + @GenerateStub(name = "booleanArrayFill", parameters = {"Boolean"}) + @GenerateStub(name = "byteArrayFill", parameters = {"Byte"}) + @GenerateStub(name = "charArrayFill", parameters = {"Char"}) + @GenerateStub(name = "shortArrayFill", parameters = {"Short"}) + @GenerateStub(name = "intArrayFill", parameters = {"Int"}) + @GenerateStub(name = "longArrayFill", parameters = {"Long"}) + @GenerateStub(name = "floatArrayFill", parameters = {"Float"}) + @GenerateStub(name = "doubleArrayFill", parameters = {"Double"}) + public static native void fill(Pointer array, long offset, int length, int value, + @ConstantNodeParameter JavaKind kind); + + @NodeIntrinsic + public static native void fill(Pointer array, long offset, int length, int value, + @ConstantNodeParameter JavaKind kind, + @ConstantNodeParameter EnumSet runtimeCheckedCPUFeatures); + + @Override + public ForeignCallDescriptor getForeignCallDescriptor() { + switch (this.elementKind) { + case Boolean: + return BOOLEAN_ARRAY_FILL_STUB; + case Byte: + return BYTE_ARRAY_FILL_STUB; + case Char: + return CHAR_ARRAY_FILL_STUB; + case Short: + return SHORT_ARRAY_FILL_STUB; + case Int: + return INT_ARRAY_FILL_STUB; + case Long: + return LONG_ARRAY_FILL_STUB; + case Float: + return FLOAT_ARRAY_FILL_STUB; + case Double: + return DOUBLE_ARRAY_FILL_STUB; + default: + return null; + } + } + + @Override + public ValueNode[] getForeignCallArguments() { + return new ValueNode[]{arrayBase, offsetToFirstElement, arrayLength, valueToFillWith}; + } + + @Override + public void emitIntrinsic(NodeLIRBuilderTool gen) { + gen.getLIRGeneratorTool().emitArrayFill(elementKind, getRuntimeCheckedCPUFeatures(), gen.operand(arrayBase), gen.operand(offsetToFirstElement), gen.operand(arrayLength), + gen.operand(valueToFillWith)); + } + + @Override + public LocationIdentity getLocationIdentity() { + return locationIdentity; + } + + @Override + public boolean hasSideEffect() { + return true; + } + + @Override + public LocationIdentity[] getKilledLocationIdentities() { + return new LocationIdentity[]{NamedLocationIdentity.getArrayLocation(this.elementKind)}; + } + + public static boolean isSupported(Architecture arch) { + return (arch instanceof AArch64); + } + + @Override + public boolean canBeEmitted(Architecture arch) { + return isSupported(arch); + } + + @Override + public FrameState stateAfter() { + return stateAfter; + } + + @Override + public void setStateAfter(FrameState x) { + updateUsages(this.stateAfter, x); + this.stateAfter = x; + } + + private static ForeignCallDescriptor createForeignCallDescriptor(JavaKind kind) { + LocationIdentity[] locs = new LocationIdentity[]{NamedLocationIdentity.getArrayLocation(kind)}; + Class[] argTypes = new Class[]{Pointer.class, long.class, int.class, int.class}; + String name = kind.getJavaName() + "ArrayFill"; + + return new ForeignCallDescriptor(name, void.class, argTypes, HAS_SIDE_EFFECT, locs, false, false); + } +} diff --git a/compiler/src/org.graalvm.micro.benchmarks/src/micro/benchmarks/ArrayFillBenchmark.java b/compiler/src/org.graalvm.micro.benchmarks/src/micro/benchmarks/ArrayFillBenchmark.java new file mode 100644 index 000000000000..824118cbeda7 --- /dev/null +++ b/compiler/src/org.graalvm.micro.benchmarks/src/micro/benchmarks/ArrayFillBenchmark.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2024, Oracle and/or its 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 micro.benchmarks; + +import java.util.Arrays; +import java.util.Random; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.infra.Blackhole; + +/** + * Benchmarks for java.util.Arrays.fill. + */ +@State(Scope.Thread) +public class ArrayFillBenchmark extends BenchmarkBase { + @Param({"16", "128", "1024", "4096"}) private int size; + + // Just a random index that we'll use to feed to bh.consume + public int indexCheck; + + // Target arrays + public boolean[] booleans; + public byte[] bytes; + public char[] chars; + public short[] shorts; + public int[] ints; + public long[] longs; + public float[] floats; + public double[] doubles; + + @Setup + public void setup() { + Random rnd = new Random(); + + indexCheck = rnd.nextInt(size); + booleans = new boolean[size]; + bytes = new byte[size]; + chars = new char[size]; + shorts = new short[size]; + ints = new int[size]; + longs = new long[size]; + floats = new float[size]; + doubles = new double[size]; + } + + @Benchmark + public void fillBooleans(Blackhole bh) { + Arrays.fill(booleans, Boolean.TRUE); + bh.consume(booleans[indexCheck]); + } + + @Benchmark + public void fillBytes(Blackhole bh) { + Arrays.fill(bytes, Byte.MAX_VALUE); + bh.consume(bytes[indexCheck]); + } + + @Benchmark + public void fillChars(Blackhole bh) { + Arrays.fill(chars, Character.MAX_VALUE); + bh.consume(chars[indexCheck]); + } + + @Benchmark + public void fillShorts(Blackhole bh) { + Arrays.fill(shorts, Short.MAX_VALUE); + bh.consume(shorts[indexCheck]); + } + + @Benchmark + public void fillInts(Blackhole bh) { + Arrays.fill(ints, Integer.MAX_VALUE); + bh.consume(ints[indexCheck]); + } + + @Benchmark + public void fillLongs(Blackhole bh) { + Arrays.fill(longs, Long.MAX_VALUE); + bh.consume(longs[indexCheck]); + } + + @Benchmark + public void fillFloats(Blackhole bh) { + Arrays.fill(floats, Float.MAX_VALUE); + bh.consume(floats[indexCheck]); + } + + @Benchmark + public void fillDoubles(Blackhole bh) { + Arrays.fill(doubles, Double.MAX_VALUE); + bh.consume(doubles[indexCheck]); + } +}