1 /*
2  * Copyright (c) 2017, 2018, 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 jdk.vm.ci.meta.DeoptimizationAction;
28 import jdk.vm.ci.meta.DeoptimizationReason;
29 import jdk.vm.ci.meta.JavaKind;
30 import jdk.vm.ci.meta.MetaAccessProvider;
31 import org.graalvm.compiler.api.replacements.ClassSubstitution;
32 import org.graalvm.compiler.api.replacements.Fold;
33 import org.graalvm.compiler.api.replacements.Fold.InjectedParameter;
34 import org.graalvm.compiler.api.replacements.MethodSubstitution;
35 import org.graalvm.compiler.nodes.DeoptimizeNode;
36 import org.graalvm.compiler.replacements.ReplacementsUtil;
37 import org.graalvm.compiler.replacements.StringUTF16Substitutions;
38 import org.graalvm.compiler.replacements.nodes.ArrayCompareToNode;
39 import org.graalvm.compiler.replacements.nodes.ArrayRegionEqualsNode;
40 import org.graalvm.compiler.word.Word;
41 import jdk.internal.vm.compiler.word.Pointer;
42 
43 // JaCoCo Exclude
44 
45 /**
46  * Substitutions for {@code java.lang.StringUTF16} methods.
47  * <p>
48  * Since JDK 9.
49  */
50 @ClassSubstitution(className = "java.lang.StringUTF16", optional = true)
51 public class AMD64StringUTF16Substitutions {
52 
53     @Fold
byteArrayBaseOffset(@njectedParameter MetaAccessProvider metaAccess)54     static int byteArrayBaseOffset(@InjectedParameter MetaAccessProvider metaAccess) {
55         return metaAccess.getArrayBaseOffset(JavaKind.Byte);
56     }
57 
58     @Fold
byteArrayIndexScale(@njectedParameter MetaAccessProvider metaAccess)59     static int byteArrayIndexScale(@InjectedParameter MetaAccessProvider metaAccess) {
60         return metaAccess.getArrayIndexScale(JavaKind.Byte);
61     }
62 
63     @Fold
charArrayBaseOffset(@njectedParameter MetaAccessProvider metaAccess)64     static int charArrayBaseOffset(@InjectedParameter MetaAccessProvider metaAccess) {
65         return metaAccess.getArrayBaseOffset(JavaKind.Char);
66     }
67 
68     @Fold
charArrayIndexScale(@njectedParameter MetaAccessProvider metaAccess)69     static int charArrayIndexScale(@InjectedParameter MetaAccessProvider metaAccess) {
70         return metaAccess.getArrayIndexScale(JavaKind.Char);
71     }
72 
73     /**
74      * Marker value for the {@link InjectedParameter} injected parameter.
75      */
76     static final MetaAccessProvider INJECTED = null;
77 
length(byte[] value)78     public static int length(byte[] value) {
79         return value.length >> 1;
80     }
81 
82     /**
83      * @param value is char[]
84      * @param other is char[]
85      */
86     @MethodSubstitution
compareTo(byte[] value, byte[] other)87     public static int compareTo(byte[] value, byte[] other) {
88         return ArrayCompareToNode.compareTo(value, other, value.length, other.length, JavaKind.Char, JavaKind.Char);
89     }
90 
91     /**
92      * @param value is char[]
93      * @param other is byte[]
94      */
95     @MethodSubstitution
compareToLatin1(byte[] value, byte[] other)96     public static int compareToLatin1(byte[] value, byte[] other) {
97         /*
98          * Swapping array arguments because intrinsic expects order to be byte[]/char[] but kind
99          * arguments stay in original order.
100          */
101         return ArrayCompareToNode.compareTo(other, value, other.length, value.length, JavaKind.Char, JavaKind.Byte);
102     }
103 
104     @MethodSubstitution
indexOfCharUnsafe(byte[] value, int ch, int fromIndex, int max)105     public static int indexOfCharUnsafe(byte[] value, int ch, int fromIndex, int max) {
106         Pointer sourcePointer = charOffsetPointer(value, fromIndex);
107         int result = AMD64ArrayIndexOf.indexOf1Char(sourcePointer, max - fromIndex, (char) ch);
108         if (result != -1) {
109             return result + fromIndex;
110         }
111         return result;
112     }
113 
pointer(byte[] target)114     private static Word pointer(byte[] target) {
115         return Word.objectToTrackedPointer(target).add(byteArrayBaseOffset(INJECTED));
116     }
117 
charOffsetPointer(byte[] value, int offset)118     private static Word charOffsetPointer(byte[] value, int offset) {
119         return pointer(value).add(offset * charArrayIndexScale(INJECTED));
120     }
121 
122     @MethodSubstitution
indexOfUnsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex)123     public static int indexOfUnsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex) {
124         ReplacementsUtil.runtimeAssert(fromIndex >= 0, "StringUTF16.indexOfUnsafe invalid args: fromIndex negative");
125         ReplacementsUtil.runtimeAssert(targetCount > 0, "StringUTF16.indexOfUnsafe invalid args: targetCount <= 0");
126         ReplacementsUtil.runtimeAssert(targetCount <= length(target), "StringUTF16.indexOfUnsafe invalid args: targetCount > length(target)");
127         ReplacementsUtil.runtimeAssert(sourceCount >= targetCount, "StringUTF16.indexOfUnsafe invalid args: sourceCount < targetCount");
128         int totalOffset = fromIndex;
129         if (targetCount == 1) {
130             Pointer sourcePointer = charOffsetPointer(source, totalOffset);
131             int indexOfResult = AMD64ArrayIndexOf.indexOf1Char(sourcePointer, sourceCount - fromIndex, StringUTF16Substitutions.getChar(target, 0));
132             if (indexOfResult >= 0) {
133                 return indexOfResult + totalOffset;
134             }
135             return indexOfResult;
136         } else if (targetCount == 2) {
137             Pointer sourcePointer = charOffsetPointer(source, totalOffset);
138             int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(sourcePointer, sourceCount - fromIndex, StringUTF16Substitutions.getChar(target, 0),
139                             StringUTF16Substitutions.getChar(target, 1));
140             if (indexOfResult >= 0) {
141                 return indexOfResult + totalOffset;
142             }
143             return indexOfResult;
144         } else {
145             int haystackLength = sourceCount - (fromIndex + (targetCount - 2));
146             while (haystackLength > 0) {
147                 Pointer sourcePointer = charOffsetPointer(source, totalOffset);
148                 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(sourcePointer, haystackLength, StringUTF16Substitutions.getChar(target, 0),
149                                 StringUTF16Substitutions.getChar(target, 1));
150                 if (indexOfResult < 0) {
151                     return -1;
152                 }
153                 totalOffset += indexOfResult;
154                 haystackLength -= (indexOfResult + 1);
155                 Pointer cmpSourcePointer = charOffsetPointer(source, totalOffset);
156                 Pointer targetPointer = pointer(target);
157                 if (ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char)) {
158                     return totalOffset;
159                 }
160                 totalOffset++;
161             }
162             return -1;
163         }
164     }
165 
166     @MethodSubstitution
indexOfLatin1Unsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex)167     public static int indexOfLatin1Unsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex) {
168         ReplacementsUtil.runtimeAssert(fromIndex >= 0, "StringUTF16.indexOfLatin1Unsafe invalid args: fromIndex negative");
169         ReplacementsUtil.runtimeAssert(targetCount > 0, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount <= 0");
170         ReplacementsUtil.runtimeAssert(targetCount <= target.length, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount > length(target)");
171         ReplacementsUtil.runtimeAssert(sourceCount >= targetCount, "StringUTF16.indexOfLatin1Unsafe invalid args: sourceCount < targetCount");
172         int totalOffset = fromIndex;
173         if (targetCount == 1) {
174             Pointer sourcePointer = charOffsetPointer(source, totalOffset);
175             int indexOfResult = AMD64ArrayIndexOf.indexOf1Char(sourcePointer, sourceCount - fromIndex, (char) Byte.toUnsignedInt(target[0]));
176             if (indexOfResult >= 0) {
177                 return indexOfResult + totalOffset;
178             }
179             return indexOfResult;
180         } else if (targetCount == 2) {
181             Pointer sourcePointer = charOffsetPointer(source, totalOffset);
182             int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(sourcePointer, sourceCount - fromIndex, (char) Byte.toUnsignedInt(target[0]), (char) Byte.toUnsignedInt(target[1]));
183             if (indexOfResult >= 0) {
184                 return indexOfResult + totalOffset;
185             }
186             return indexOfResult;
187         } else {
188             int haystackLength = sourceCount - (fromIndex + (targetCount - 2));
189             while (haystackLength > 0) {
190                 Pointer sourcePointer = charOffsetPointer(source, totalOffset);
191                 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(sourcePointer, haystackLength, (char) Byte.toUnsignedInt(target[0]), (char) Byte.toUnsignedInt(target[1]));
192                 if (indexOfResult < 0) {
193                     return -1;
194                 }
195                 totalOffset += indexOfResult;
196                 haystackLength -= (indexOfResult + 1);
197                 Pointer cmpSourcePointer = charOffsetPointer(source, totalOffset);
198                 Pointer targetPointer = pointer(target);
199                 if (ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char, JavaKind.Byte)) {
200                     return totalOffset;
201                 }
202                 totalOffset++;
203             }
204             return -1;
205         }
206     }
207 
208     /**
209      * Intrinsic for {@code java.lang.StringUTF16.compress([CI[BII)I}.
210      *
211      * <pre>
212      * &#64;HotSpotIntrinsicCandidate
213      * public static int compress(char[] src, int src_indx, byte[] dst, int dst_indx, int len)
214      * </pre>
215      */
216     @MethodSubstitution
compress(char[] src, int srcIndex, byte[] dest, int destIndex, int len)217     public static int compress(char[] src, int srcIndex, byte[] dest, int destIndex, int len) {
218         if (len < 0 || srcIndex < 0 || (srcIndex + len > src.length) || destIndex < 0 || (destIndex + len > dest.length)) {
219             DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.BoundsCheckException);
220         }
221 
222         Pointer srcPointer = Word.objectToTrackedPointer(src).add(charArrayBaseOffset(INJECTED)).add(srcIndex * charArrayIndexScale(INJECTED));
223         Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED));
224         return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Char);
225     }
226 
227     /**
228      * Intrinsic for {@code }java.lang.StringUTF16.compress([BI[BII)I}.
229      *
230      * <pre>
231      * &#64;HotSpotIntrinsicCandidate
232      * public static int compress(byte[] src, int src_indx, byte[] dst, int dst_indx, int len)
233      * </pre>
234      * <p>
235      * In this variant {@code dest} refers to a byte array containing 2 byte per char so
236      * {@code srcIndex} and {@code len} are in terms of char elements and have to be scaled by 2
237      * when referring to {@code src}.
238      */
239     @MethodSubstitution
compress(byte[] src, int srcIndex, byte[] dest, int destIndex, int len)240     public static int compress(byte[] src, int srcIndex, byte[] dest, int destIndex, int len) {
241         if (len < 0 || srcIndex < 0 || (srcIndex * 2 + len * 2 > src.length) || destIndex < 0 || (destIndex + len > dest.length)) {
242             DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.BoundsCheckException);
243         }
244 
245         Pointer srcPointer = Word.objectToTrackedPointer(src).add(byteArrayBaseOffset(INJECTED)).add(srcIndex * 2 * byteArrayIndexScale(INJECTED));
246         Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED));
247         return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Byte);
248     }
249 
250 }
251