1 /*
   2  * Copyright (c) 2017, 2019, 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 {
 126             int haystackLength = sourceCount - (targetCount - 2);
 127             int offset = fromIndex;
 128             while (offset < haystackLength) {
 129                 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, haystackLength, offset, StringUTF16Substitutions.getChar(target, 0),
 130                                 StringUTF16Substitutions.getChar(target, 1));
 131                 if (indexOfResult < 0) {
 132                     return -1;
 133                 }
 134                 offset = indexOfResult;
 135                 Pointer cmpSourcePointer = charOffsetPointer(source, offset);
 136                 Pointer targetPointer = pointer(target);
 137                 if (targetCount == 2 || ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char)) {
 138                     return offset;
 139                 }
 140                 offset++;
 141             }
 142             return -1;
 143         }
 144     }
 145 
 146     @MethodSubstitution
 147     public static int indexOfLatin1Unsafe(byte[] source, int sourceCount, byte[] target, int targetCount, int fromIndex) {
 148         ReplacementsUtil.runtimeAssert(fromIndex >= 0, "StringUTF16.indexOfLatin1Unsafe invalid args: fromIndex negative");
 149         ReplacementsUtil.runtimeAssert(targetCount > 0, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount <= 0");
 150         ReplacementsUtil.runtimeAssert(targetCount <= target.length, "StringUTF16.indexOfLatin1Unsafe invalid args: targetCount > length(target)");
 151         ReplacementsUtil.runtimeAssert(sourceCount >= targetCount, "StringUTF16.indexOfLatin1Unsafe invalid args: sourceCount < targetCount");
 152         if (targetCount == 1) {
 153             return AMD64ArrayIndexOf.indexOf1Char(source, sourceCount, fromIndex, (char) Byte.toUnsignedInt(target[0]));
 154         } else {
 155             int haystackLength = sourceCount - (targetCount - 2);
 156             int offset = fromIndex;
 157             while (offset < haystackLength) {
 158                 int indexOfResult = AMD64ArrayIndexOf.indexOfTwoConsecutiveChars(source, haystackLength, offset, (char) Byte.toUnsignedInt(target[0]), (char) Byte.toUnsignedInt(target[1]));
 159                 if (indexOfResult < 0) {
 160                     return -1;
 161                 }
 162                 offset = indexOfResult;
 163                 Pointer cmpSourcePointer = charOffsetPointer(source, offset);
 164                 Pointer targetPointer = pointer(target);
 165                 if (targetCount == 2 || ArrayRegionEqualsNode.regionEquals(cmpSourcePointer, targetPointer, targetCount, JavaKind.Char, JavaKind.Byte)) {
 166                     return offset;
 167                 }
 168                 offset++;
 169             }
 170             return -1;
 171         }
 172     }
 173 
 174     /**
 175      * Intrinsic for {@code java.lang.StringUTF16.compress([CI[BII)I}.
 176      *
 177      * <pre>
 178      * @HotSpotIntrinsicCandidate
 179      * public static int compress(char[] src, int src_indx, byte[] dst, int dst_indx, int len)
 180      * </pre>
 181      */
 182     @MethodSubstitution
 183     public static int compress(char[] src, int srcIndex, byte[] dest, int destIndex, int len) {
 184         checkLimits(src.length, srcIndex, dest.length, destIndex, len);
 185 
 186         Pointer srcPointer = Word.objectToTrackedPointer(src).add(charArrayBaseOffset(INJECTED)).add(srcIndex * charArrayIndexScale(INJECTED));
 187         Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED));
 188         return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Char);
 189     }
 190 
 191     /**
 192      * Intrinsic for {@code }java.lang.StringUTF16.compress([BI[BII)I}.
 193      *
 194      * <pre>
 195      * @HotSpotIntrinsicCandidate
 196      * public static int compress(byte[] src, int src_indx, byte[] dst, int dst_indx, int len)
 197      * </pre>
 198      * <p>
 199      * In this variant {@code dest} refers to a byte array containing 2 byte per char so
 200      * {@code srcIndex} and {@code len} are in terms of char elements and have to be scaled by 2
 201      * when referring to {@code src}.
 202      */
 203     @MethodSubstitution
 204     public static int compress(byte[] src, int srcIndex, byte[] dest, int destIndex, int len) {
 205         checkLimits(src.length >> 1, srcIndex, dest.length, destIndex, len);
 206 
 207         Pointer srcPointer = Word.objectToTrackedPointer(src).add(byteArrayBaseOffset(INJECTED)).add(srcIndex * 2 * byteArrayIndexScale(INJECTED));
 208         Pointer destPointer = Word.objectToTrackedPointer(dest).add(byteArrayBaseOffset(INJECTED)).add(destIndex * byteArrayIndexScale(INJECTED));
 209         return AMD64StringUTF16CompressNode.compress(srcPointer, destPointer, len, JavaKind.Byte);
 210     }
 211 
 212     private static void checkLimits(int srcLen, int srcIndex, int destLen, int destIndex, int len) {
 213         if (len < 0 || srcIndex < 0 || (srcIndex + len > srcLen) || destIndex < 0 || (destIndex + len > destLen)) {
 214             DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.BoundsCheckException);
 215         }
 216     }
 217 
 218 }