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      * &#64;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      * &#64;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