1 /* 2 * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.replacements.amd64; 26 27 import static org.graalvm.compiler.api.directives.GraalDirectives.LIKELY_PROBABILITY; 28 import static org.graalvm.compiler.api.directives.GraalDirectives.SLOWPATH_PROBABILITY; 29 import static org.graalvm.compiler.api.directives.GraalDirectives.UNLIKELY_PROBABILITY; 30 import static org.graalvm.compiler.api.directives.GraalDirectives.injectBranchProbability; 31 import static org.graalvm.compiler.replacements.ReplacementsUtil.byteArrayBaseOffset; 32 import static org.graalvm.compiler.replacements.ReplacementsUtil.byteArrayIndexScale; 33 import static org.graalvm.compiler.replacements.ReplacementsUtil.charArrayBaseOffset; 34 import static org.graalvm.compiler.replacements.ReplacementsUtil.charArrayIndexScale; 35 36 import org.graalvm.compiler.api.replacements.ClassSubstitution; 37 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter; 38 import org.graalvm.compiler.api.replacements.MethodSubstitution; 39 import org.graalvm.compiler.nodes.DeoptimizeNode; 40 import org.graalvm.compiler.replacements.ReplacementsUtil; 41 import org.graalvm.compiler.replacements.StringUTF16Substitutions; 42 import org.graalvm.compiler.replacements.nodes.ArrayCompareToNode; 43 import org.graalvm.compiler.replacements.nodes.ArrayRegionEqualsNode; 44 import org.graalvm.compiler.word.Word; 45 import jdk.internal.vm.compiler.word.Pointer; 46 47 import jdk.vm.ci.meta.DeoptimizationAction; 48 import jdk.vm.ci.meta.DeoptimizationReason; 49 import jdk.vm.ci.meta.JavaKind; 50 import jdk.vm.ci.meta.MetaAccessProvider; 51 52 // JaCoCo Exclude 53 54 /** 55 * Substitutions for {@code java.lang.StringUTF16} methods. 56 * <p> 57 * Since JDK 9. 58 */ 59 @ClassSubstitution(className = "java.lang.StringUTF16", optional = true) 60 public class AMD64StringUTF16Substitutions { 61 /** 62 * Marker value for the {@link InjectedParameter} injected parameter. 63 */ 64 static final MetaAccessProvider INJECTED = null; 65 length(byte[] value)66 public static int length(byte[] value) { 67 return value.length >> 1; 68 } 69 70 /** 71 * @param value is char[] 72 * @param other is char[] 73 */ 74 @MethodSubstitution compareTo(byte[] value, byte[] other)75 public static int compareTo(byte[] value, byte[] other) { 76 return ArrayCompareToNode.compareTo(value, other, value.length, other.length, JavaKind.Char, JavaKind.Char); 77 } 78 79 /** 80 * @param value is char[] 81 * @param other is byte[] 82 */ 83 @MethodSubstitution compareToLatin1(byte[] value, byte[] other)84 public static int compareToLatin1(byte[] value, byte[] other) { 85 /* 86 * Swapping array arguments because intrinsic expects order to be byte[]/char[] but kind 87 * arguments stay in original order. 88 */ 89 return ArrayCompareToNode.compareTo(other, value, other.length, value.length, JavaKind.Char, JavaKind.Byte); 90 } 91 92 @MethodSubstitution indexOfCharUnsafe(byte[] value, int ch, int fromIndex, int max)93 public static int indexOfCharUnsafe(byte[] value, int ch, int fromIndex, int max) { 94 return AMD64ArrayIndexOf.indexOf1Char(value, max, fromIndex, (char) ch); 95 } 96 pointer(byte[] target)97 private static Word pointer(byte[] target) { 98 return Word.objectToTrackedPointer(target).add(byteArrayBaseOffset(INJECTED)); 99 } 100 charOffsetPointer(byte[] value, int offset)101 private static Word charOffsetPointer(byte[] value, int offset) { 102 return pointer(value).add(offset * charArrayIndexScale(INJECTED)); 103 } 104 105 @MethodSubstitution indexOfUnsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex)106 public static int indexOfUnsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex) { 107 ReplacementsUtil.dynamicAssert(fromIndex >= 0, "StringUTF16.indexOfUnsafe invalid args: fromIndex negative"); 108 ReplacementsUtil.dynamicAssert(targetCount > 0, "StringUTF16.indexOfUnsafe invalid args: targetCount <= 0"); 109 ReplacementsUtil.dynamicAssert(targetCount <= length(target), "StringUTF16.indexOfUnsafe invalid args: targetCount > length(target)"); 110 ReplacementsUtil.dynamicAssert(sourceCount >= targetCount, "StringUTF16.indexOfUnsafe invalid args: sourceCount < targetCount"); 111 if (targetCount == 1) { 112 return AMD64ArrayIndexOf.indexOf1Char(source, sourceCount, fromIndex, StringUTF16Substitutions.getChar(target, 0)); 113 } else { 114 int haystackLength = sourceCount - (targetCount - 2); 115 int offset = fromIndex; 116 while (injectBranchProbability(LIKELY_PROBABILITY, offset < haystackLength)) { 117 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, haystackLength, offset, StringUTF16Substitutions.getChar(target, 0), 118 StringUTF16Substitutions.getChar(target, 1)); 119 if (injectBranchProbability(UNLIKELY_PROBABILITY, indexOfResult < 0)) { 120 return -1; 121 } 122 offset = indexOfResult; 123 if (injectBranchProbability(UNLIKELY_PROBABILITY, targetCount == 2)) { 124 return offset; 125 } else { 126 Pointer cmpSourcePointer = charOffsetPointer(source, offset); 127 Pointer targetPointer = pointer(target); 128 if (injectBranchProbability(UNLIKELY_PROBABILITY, ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char))) { 129 return offset; 130 } 131 } 132 offset++; 133 } 134 return -1; 135 } 136 } 137 138 @MethodSubstitution indexOfLatin1Unsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex)139 public static int indexOfLatin1Unsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex) { 140 ReplacementsUtil.dynamicAssert(fromIndex >= 0, "StringUTF16.indexOfLatin1Unsafe invalid args: fromIndex negative"); 141 ReplacementsUtil.dynamicAssert(targetCount > 0, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount <= 0"); 142 ReplacementsUtil.dynamicAssert(targetCount <= target.length, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount > length(target)"); 143 ReplacementsUtil.dynamicAssert(sourceCount >= targetCount, "StringUTF16.indexOfLatin1Unsafe invalid args: sourceCount < targetCount"); 144 if (targetCount == 1) { 145 return AMD64ArrayIndexOf.indexOf1Char(source, sourceCount, fromIndex, (char) Byte.toUnsignedInt(target[0])); 146 } else { 147 int haystackLength = sourceCount - (targetCount - 2); 148 int offset = fromIndex; 149 while (injectBranchProbability(LIKELY_PROBABILITY, offset < haystackLength)) { 150 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, haystackLength, offset, (char) Byte.toUnsignedInt(target[0]), (char) Byte.toUnsignedInt(target[1])); 151 if (injectBranchProbability(UNLIKELY_PROBABILITY, indexOfResult < 0)) { 152 return -1; 153 } 154 offset = indexOfResult; 155 if (injectBranchProbability(UNLIKELY_PROBABILITY, targetCount == 2)) { 156 return offset; 157 } else { 158 Pointer cmpSourcePointer = charOffsetPointer(source, offset); 159 Pointer targetPointer = pointer(target); 160 if (injectBranchProbability(UNLIKELY_PROBABILITY, ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char, JavaKind.Byte))) { 161 return offset; 162 } 163 } 164 offset++; 165 } 166 return -1; 167 } 168 } 169 170 /** 171 * Intrinsic for {@code java.lang.StringUTF16.compress([CI[BII)I}. 172 * 173 * <pre> 174 * @IntrinsicCandidate 175 * public static int compress(char[] src, int src_indx, byte[] dst, int dst_indx, int len) 176 * </pre> 177 */ 178 @MethodSubstitution compress(char[] src, int srcIndex, byte[] dest, int destIndex, int len)179 public static int compress(char[] src, int srcIndex, byte[] dest, int destIndex, int len) { 180 checkLimits(src.length, srcIndex, dest.length, destIndex, len); 181 182 Pointer srcPointer = Word.objectToTrackedPointer(src).add(charArrayBaseOffset(INJECTED)).add(srcIndex * charArrayIndexScale(INJECTED)); 183 Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED)); 184 return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Char); 185 } 186 187 /** 188 * Intrinsic for {@code }java.lang.StringUTF16.compress([BI[BII)I}. 189 * 190 * <pre> 191 * @IntrinsicCandidate 192 * public static int compress(byte[] src, int src_indx, byte[] dst, int dst_indx, int len) 193 * </pre> 194 * <p> 195 * In this variant {@code dest} refers to a byte array containing 2 byte per char so 196 * {@code srcIndex} and {@code len} are in terms of char elements and have to be scaled by 2 197 * when referring to {@code src}. 198 */ 199 @MethodSubstitution compress(byte[] src, int srcIndex, byte[] dest, int destIndex, int len)200 public static int compress(byte[] src, int srcIndex, byte[] dest, int destIndex, int len) { 201 checkLimits(src.length >> 1, srcIndex, dest.length, destIndex, len); 202 203 Pointer srcPointer = Word.objectToTrackedPointer(src).add(byteArrayBaseOffset(INJECTED)).add(srcIndex * 2 * byteArrayIndexScale(INJECTED)); 204 Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED)); 205 return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Byte); 206 } 207 checkLimits(int srcLen, int srcIndex, int destLen, int destIndex, int len)208 private static void checkLimits(int srcLen, int srcIndex, int destLen, int destIndex, int len) { 209 if (injectBranchProbability(SLOWPATH_PROBABILITY, len < 0) || 210 injectBranchProbability(SLOWPATH_PROBABILITY, srcIndex < 0) || 211 injectBranchProbability(SLOWPATH_PROBABILITY, srcIndex + len > srcLen) || 212 injectBranchProbability(SLOWPATH_PROBABILITY, destIndex < 0) || 213 injectBranchProbability(SLOWPATH_PROBABILITY, destIndex + len > destLen)) { 214 DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.BoundsCheckException); 215 } 216 } 217 218 } 219