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
  54     static int byteArrayBaseOffset(@InjectedParameter MetaAccessProvider metaAccess) {
  55         return metaAccess.getArrayBaseOffset(JavaKind.Byte);
  56     }
  57 
  58     @Fold
  59     static int byteArrayIndexScale(@InjectedParameter MetaAccessProvider metaAccess) {
  60         return metaAccess.getArrayIndexScale(JavaKind.Byte);
  61     }
  62 
  63     @Fold
  64     static int charArrayBaseOffset(@InjectedParameter MetaAccessProvider metaAccess) {
  65         return metaAccess.getArrayBaseOffset(JavaKind.Char);
  66     }
  67 
  68     @Fold
  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 
  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
  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
  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
 105     public static int indexOfCharUnsafe(byte[] value, int ch, int fromIndex, int max) {
 106         return AMD64ArrayIndexOf.indexOf1Char(value, max, fromIndex, (char) ch);
 107     }
 108 
 109     private static Word pointer(byte[] target) {
 110         return Word.objectToTrackedPointer(target).add(byteArrayBaseOffset(INJECTED));
 111     }
 112 
 113     private static Word charOffsetPointer(byte[] value, int offset) {
 114         return pointer(value).add(offset * charArrayIndexScale(INJECTED));
 115     }
 116 
 117     @MethodSubstitution
 118     public static int indexOfUnsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex) {
 119         ReplacementsUtil.runtimeAssert(fromIndex >= 0, "StringUTF16.indexOfUnsafe invalid args: fromIndex negative");
 120         ReplacementsUtil.runtimeAssert(targetCount > 0, "StringUTF16.indexOfUnsafe invalid args: targetCount <= 0");
 121         ReplacementsUtil.runtimeAssert(targetCount <= length(target), "StringUTF16.indexOfUnsafe invalid args: targetCount > length(target)");
 122         ReplacementsUtil.runtimeAssert(sourceCount >= targetCount, "StringUTF16.indexOfUnsafe invalid args: sourceCount < targetCount");
 123         if (targetCount == 1) {
 124             return AMD64ArrayIndexOf.indexOf1Char(source, sourceCount, fromIndex, StringUTF16Substitutions.getChar(target, 0));
 125         } else if (targetCount == 2) {
 126             return AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, sourceCount, fromIndex, StringUTF16Substitutions.getChar(target, 0), StringUTF16Substitutions.getChar(target, 1));
 127         } else {
 128             int haystackLength = sourceCount - (targetCount - 2);
 129             int offset = fromIndex;
 130             while (offset < haystackLength) {
 131                 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, haystackLength, offset, StringUTF16Substitutions.getChar(target, 0),
 132                                 StringUTF16Substitutions.getChar(target, 1));
 133                 if (indexOfResult < 0) {
 134                     return -1;
 135                 }
 136                 offset = indexOfResult;
 137                 Pointer cmpSourcePointer = charOffsetPointer(source, offset);
 138                 Pointer targetPointer = pointer(target);
 139                 if (ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char)) {
 140                     return offset;
 141                 }
 142                 offset++;
 143             }
 144             return -1;
 145         }
 146     }
 147 
 148     @MethodSubstitution
 149     public static int indexOfLatin1Unsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex) {
 150         ReplacementsUtil.runtimeAssert(fromIndex >= 0, "StringUTF16.indexOfLatin1Unsafe invalid args: fromIndex negative");
 151         ReplacementsUtil.runtimeAssert(targetCount > 0, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount <= 0");
 152         ReplacementsUtil.runtimeAssert(targetCount <= target.length, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount > length(target)");
 153         ReplacementsUtil.runtimeAssert(sourceCount >= targetCount, "StringUTF16.indexOfLatin1Unsafe invalid args: sourceCount < targetCount");
 154         if (targetCount == 1) {
 155             return AMD64ArrayIndexOf.indexOf1Char(source, sourceCount, fromIndex, (char) Byte.toUnsignedInt(target[0]));
 156         } else if (targetCount == 2) {
 157             return AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, sourceCount, fromIndex, (char) Byte.toUnsignedInt(target[0]), (char) Byte.toUnsignedInt(target[1]));
 158         } else {
 159             int haystackLength = sourceCount - (targetCount - 2);
 160             int offset = fromIndex;
 161             while (offset < haystackLength) {
 162                 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, haystackLength, offset, (char) Byte.toUnsignedInt(target[0]), (char) Byte.toUnsignedInt(target[1]));
 163                 if (indexOfResult < 0) {
 164                     return -1;
 165                 }
 166                 offset = indexOfResult;
 167                 Pointer cmpSourcePointer = charOffsetPointer(source, offset);
 168                 Pointer targetPointer = pointer(target);
 169                 if (ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char, JavaKind.Byte)) {
 170                     return offset;
 171                 }
 172                 offset++;
 173             }
 174             return -1;
 175         }
 176     }
 177 
 178     /**
 179      * Intrinsic for {@code java.lang.StringUTF16.compress([CI[BII)I}.
 180      *
 181      * <pre>
 182      * @HotSpotIntrinsicCandidate
 183      * public static int compress(char[] src, int src_indx, byte[] dst, int dst_indx, int len)
 184      * </pre>
 185      */
 186     @MethodSubstitution
 187     public static int compress(char[] src, int srcIndex, byte[] dest, int destIndex, int len) {
 188         if (len < 0 || srcIndex < 0 || (srcIndex + len > src.length) || destIndex < 0 || (destIndex + len > dest.length)) {
 189             DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.BoundsCheckException);
 190         }
 191 
 192         Pointer srcPointer = Word.objectToTrackedPointer(src).add(charArrayBaseOffset(INJECTED)).add(srcIndex * charArrayIndexScale(INJECTED));
 193         Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED));
 194         return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Char);
 195     }
 196 
 197     /**
 198      * Intrinsic for {@code }java.lang.StringUTF16.compress([BI[BII)I}.
 199      *
 200      * <pre>
 201      * @HotSpotIntrinsicCandidate
 202      * public static int compress(byte[] src, int src_indx, byte[] dst, int dst_indx, int len)
 203      * </pre>
 204      * <p>
 205      * In this variant {@code dest} refers to a byte array containing 2 byte per char so
 206      * {@code srcIndex} and {@code len} are in terms of char elements and have to be scaled by 2
 207      * when referring to {@code src}.
 208      */
 209     @MethodSubstitution
 210     public static int compress(byte[] src, int srcIndex, byte[] dest, int destIndex, int len) {
 211         if (len < 0 || srcIndex < 0 || (srcIndex * 2 + len * 2 > src.length) || destIndex < 0 || (destIndex + len > dest.length)) {
 212             DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.BoundsCheckException);
 213         }
 214 
 215         Pointer srcPointer = Word.objectToTrackedPointer(src).add(byteArrayBaseOffset(INJECTED)).add(srcIndex * 2 * byteArrayIndexScale(INJECTED));
 216         Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED));
 217         return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Byte);
 218     }
 219 
 220 }