diff --git a/jcl/src/java.base/share/classes/com/ibm/jit/JITHelpers.java b/jcl/src/java.base/share/classes/com/ibm/jit/JITHelpers.java index 09cab7e9e18..9207116d85b 100644 --- a/jcl/src/java.base/share/classes/com/ibm/jit/JITHelpers.java +++ b/jcl/src/java.base/share/classes/com/ibm/jit/JITHelpers.java @@ -165,6 +165,100 @@ public int getJ9ClassFromObject32(Object obj) { } + /* + * To be recognized by the JIT and returns true if the hardware supports SIMD case conversion. + */ + public boolean supportsIntrinsicCaseConversion() { + return false; + } + + /** + * To be used by the JIT when performing SIMD upper case conversion with Latin 1 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toUpperIntrinsicLatin1(byte[] value, byte[] output, int length) { + return false; + } + + /** + * To be used by the JIT when performing SIMD lower case conversion with Latin 1 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toLowerIntrinsicLatin1(byte[] value, byte[] output, int length) { + return false; + } + + /** + * To be used by the JIT when performing SIMD upper case conversion with UTF16 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toUpperIntrinsicUTF16(byte[] value, byte[] output, int length) { + return false; + } + + /** + * To be used by the JIT when performing SIMD lower case conversion with UTF16 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toLowerIntrinsicUTF16(byte[] value, byte[] output, int length) { + return false; + } + /** + * To be used by the JIT when performing SIMD upper case conversion with Latin 1 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toUpperIntrinsicLatin1(char[] value, char[] output, int length) { + return false; + } + + /** + * To be used by the JIT when performing SIMD lower case conversion with Latin 1 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toLowerIntrinsicLatin1(char[] value, char[] output, int length) { + return false; + } + + /** + * To be used by the JIT when performing SIMD upper case conversion with UTF16 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toUpperIntrinsicUTF16(char[] value, char[] output, int length) { + return false; + } + + /** + * To be used by the JIT when performing SIMD lower case conversion with UTF16 strings. + * @param value the underlying array for the source string. + * @param output a new array which will be used for the converted string. + * @param length the number of bytes used to represent the string. + * @return True if intrinsic conversion was successful, false if fallback conversion must be used. + */ + public boolean toLowerIntrinsicUTF16(char[] value, char[] output, int length) { + return false; + } + /* * sun.misc.Unsafe.get* and put* have to generate internal control flow for correctness due to different object shapes. The JIT emitted sequences * checks for and handles: 1) standard fields in normal objects 2) static fields from classes 3) entries from arrays This sequence is branchy and diff --git a/jcl/src/java.base/share/classes/java/lang/String.java b/jcl/src/java.base/share/classes/java/lang/String.java index 7a415e9fa7c..0bcadcfefa4 100644 --- a/jcl/src/java.base/share/classes/java/lang/String.java +++ b/jcl/src/java.base/share/classes/java/lang/String.java @@ -71,7 +71,6 @@ private void checkLastChar(char lastChar) { /*[IF Sidecar19-SE]*/ // DO NOT CHANGE OR MOVE THIS LINE // IT MUST BE THE FIRST THING IN THE INITIALIZATION - private static final boolean STRING_OPT_IN_HW = StrCheckHWAvailable(); private static final long serialVersionUID = -6849794470754667710L; /** @@ -878,17 +877,6 @@ public String(String string) { hashCode = string.hashCode; } - /** - * Creates a new string from the with specified length - * - * @param numChars - * length of new string - */ - private String(int numChars) { - value = new byte[numChars * 2]; - coder = UTF16; - } - /** * Creates a string from the contents of a StringBuffer. * @@ -2709,15 +2697,26 @@ public String toLowerCase(Locale locale) { return this; } - if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ - String output = new String(lengthInternal()); - if (toLowerHWOptimized(output)) - return output; - } + int sLength = lengthInternal(); if (enableCompression && (null == compressionFlag || coder == LATIN1)) { + + if (helpers.supportsIntrinsicCaseConversion() && language == "en") { //$NON-NLS-1$ + byte[] output = new byte[sLength << coder]; + + if (helpers.toLowerIntrinsicLatin1(value, output, sLength)) { + return new String(output, LATIN1); + } + } return StringLatin1.toLowerCase(this, value, locale); } else { + if (helpers.supportsIntrinsicCaseConversion() && language == "en") { //$NON-NLS-1$ + byte[] output = new byte[sLength << coder]; + + if (helpers.toLowerIntrinsicUTF16(value, output, sLength * 2)) { + return new String(output, UTF16); + } + } return StringUTF16.toLowerCase(this, value, locale); } } @@ -2755,15 +2754,26 @@ public String toUpperCase(Locale locale) { return this; } - if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ - String output = new String(lengthInternal()); - if (toUpperHWOptimized(output)) - return output; - } + int sLength = lengthInternal(); if (enableCompression && (null == compressionFlag || coder == LATIN1)) { + + if (helpers.supportsIntrinsicCaseConversion() && language == "en") { //$NON-NLS-1$ + byte[] output = new byte[sLength << coder]; + + if (helpers.toUpperIntrinsicLatin1(value, output, sLength)) { + return new String(output, LATIN1); + } + } return StringLatin1.toUpperCase(this, value, locale); } else { + if (helpers.supportsIntrinsicCaseConversion() && language == "en") { //$NON-NLS-1$ + byte[] output = new byte[sLength << coder]; + + if (helpers.toUpperIntrinsicUTF16(value, output, sLength * 2)) { + return new String(output, UTF16); + } + } return StringUTF16.toUpperCase(this, value, locale); } } @@ -3811,33 +3821,6 @@ public static String join(CharSequence delimiter, Iterable= 0) && (begin <= end) && (end <= length)) { return; @@ -3946,7 +3929,6 @@ public String repeat(int count) { /*[ELSE] Sidecar19-SE*/ // DO NOT CHANGE OR MOVE THIS LINE // IT MUST BE THE FIRST THING IN THE INITIALIZATION - private static final boolean STRING_OPT_IN_HW = StrCheckHWAvailable(); private static final long serialVersionUID = -6849794470754667710L; /** @@ -4728,22 +4710,6 @@ public String(String string) { hashCode = string.hashCode; } - /** - * Creates a new string from the with specified length - * - * @param numChars - * length of new string - */ - private String(int numChars) { - if (enableCompression) { - value = new char[numChars]; - count = numChars | uncompressedBit; - } else { - value = new char[numChars]; - count = numChars; - } - } - /** * Creates a string from the contents of a StringBuffer. * @@ -6745,10 +6711,20 @@ public String toLowerCase(Locale locale) { return this; } - if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ - String output = new String(lengthInternal()); - if (toLowerHWOptimized(output)) - return output; + if (helpers.supportsIntrinsicCaseConversion() && language == "en") { //$NON-NLS-1$ + int sLength = lengthInternal(); + + if (enableCompression && (null == compressionFlag || count >= 0)) { + char[] output = new char[(sLength + 1) / 2]; + if (helpers.toLowerIntrinsicLatin1(value, output, sLength)) { + return new String(output, 0, sLength, true); + } + } else { + char[] output = new char[sLength]; + if (helpers.toLowerIntrinsicUTF16(value, output, sLength * 2)) { + return new String(output, 0, sLength, false); + } + } } return toLowerCaseCore(language); @@ -7070,10 +7046,20 @@ public String toUpperCase(Locale locale) { return this; } - if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ - String output = new String(lengthInternal()); - if (toUpperHWOptimized(output)) - return output; + if (helpers.supportsIntrinsicCaseConversion() && language == "en") { //$NON-NLS-1$ + int sLength = lengthInternal(); + + if (enableCompression && (null == compressionFlag || count >= 0)) { + char[] output = new char[(sLength + 1) / 2]; + if (helpers.toUpperIntrinsicLatin1(value, output, sLength)){ + return new String(output, 0, sLength, true); + } + } else { + char[] output = new char[sLength]; + if (helpers.toUpperIntrinsicUTF16(value, output, sLength * 2)){ + return new String(output, 0, sLength, false); + } + } } return toUpperCaseCore(language); @@ -8270,32 +8256,5 @@ public static String join(CharSequence delimiter, IterablesetMaxOnsiteCacheSlotForInstanceOf(4); #endif - // TODO: Disable string case conversion acceleration temporarily as the code generators need to be updated to - // support the new String object layout - self()->setOption(TR_DisableSIMDStringCaseConv); - // Process the deterministic mode if (TR::Options::_deterministicMode == -1) // not yet set { diff --git a/runtime/compiler/env/VMJ9.cpp b/runtime/compiler/env/VMJ9.cpp index 2aaef930c3a..b50bd0187da 100644 --- a/runtime/compiler/env/VMJ9.cpp +++ b/runtime/compiler/env/VMJ9.cpp @@ -3085,12 +3085,10 @@ bool TR_J9VMBase::supressInliningRecognizedInitialCallee(TR_CallSite* callsite, dontInlineRecognizedMethod = true; } break; - case TR::java_lang_String_toUpperHWOptimizedCompressed: - case TR::java_lang_String_toLowerHWOptimizedCompressed: - case TR::java_lang_String_toUpperHWOptimizedDecompressed: - case TR::java_lang_String_toLowerHWOptimizedDecompressed: - case TR::java_lang_String_toUpperHWOptimized: - case TR::java_lang_String_toLowerHWOptimized: + case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1: + case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicLatin1: + case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicUTF16: + case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16: if(comp->cg()->getSupportsInlineStringCaseConversion()) { dontInlineRecognizedMethod = true; @@ -3332,12 +3330,10 @@ int TR_J9VMBase::checkInlineableTarget (TR_CallTarget* target, TR_CallSite* call #endif if ( - rm == TR::java_lang_String_toUpperHWOptimizedCompressed || - rm == TR::java_lang_String_toLowerHWOptimizedDecompressed || - rm == TR::java_lang_String_toLowerHWOptimized || - rm == TR::java_lang_String_toUpperHWOptimizedCompressed || - rm == TR::java_lang_String_toLowerHWOptimizedDecompressed || - rm == TR::java_lang_String_toLowerHWOptimized || + rm == TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1 || + rm == TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16 || + rm == TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1 || + rm == TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16 || rm == TR::java_lang_String_compressedArrayCopy_BIBII || rm == TR::java_lang_String_compressedArrayCopy_BICII || diff --git a/runtime/compiler/env/j9method.cpp b/runtime/compiler/env/j9method.cpp index 3d01dc27be9..0feaea76cb1 100644 --- a/runtime/compiler/env/j9method.cpp +++ b/runtime/compiler/env/j9method.cpp @@ -2972,13 +2972,6 @@ TR_ResolvedJ9Method::TR_ResolvedJ9Method(TR_OpaqueMethodBlock * aMethod, TR_Fron {x(TR::java_lang_String_compress, "compress", "([C[BII)I")}, {x(TR::java_lang_String_compressNoCheck, "compressNoCheck", "([C[BII)V")}, {x(TR::java_lang_String_andOR, "andOR", "([CII)I")}, - {x(TR::java_lang_String_toUpperHWOptimizedCompressed, "toUpperHWOptimizedCompressed", "(Ljava/lang/String;)Z")}, - {x(TR::java_lang_String_toUpperHWOptimizedDecompressed,"toUpperHWOptimizedDecompressed","(Ljava/lang/String;)Z")}, - {x(TR::java_lang_String_toUpperHWOptimized, "toUpperHWOptimized","(Ljava/lang/String;)Z")}, - {x(TR::java_lang_String_toLowerHWOptimizedCompressed, "toLowerHWOptimizedCompressed", "(Ljava/lang/String;)Z")}, - {x(TR::java_lang_String_toLowerHWOptimizedDecompressed,"toLowerHWOptimizedDecompressed", "(Ljava/lang/String;)Z")}, - {x(TR::java_lang_String_toLowerHWOptimized, "toLowerHWOptimized", "(Ljava/lang/String;)Z")}, - {x(TR::java_lang_String_StrHWAvailable, "StrHWAvailable", "()Z")}, {x(TR::java_lang_String_unsafeCharAt, "unsafeCharAt", "(I)C")}, {x(TR::java_lang_String_split_str_int, "split", "(Ljava/lang/String;I)[Ljava/lang/String;")}, {x(TR::java_lang_String_getChars_charArray, "getChars", "(II[CI)V")}, @@ -3321,7 +3314,16 @@ TR_ResolvedJ9Method::TR_ResolvedJ9Method(TR_OpaqueMethodBlock * aMethod, TR_Fron {x(TR::com_ibm_jit_JITHelpers_byteToCharUnsigned, "byteToCharUnsigned", "(B)C")}, {x(TR::com_ibm_jit_JITHelpers_acmplt, "acmplt", "(Ljava/lang/Object;Ljava/lang/Object;)Z")}, {x(TR::com_ibm_jit_JITHelpers_getClassInitializeStatus, "getClassInitializeStatus", "(Ljava/lang/Class;)I")}, - { TR::unknownMethod} + {x(TR::com_ibm_jit_JITHelpers_supportsIntrinsicCaseConversion, "supportsIntrinsicCaseConversion", "()Z")}, + {x(TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1, "toUpperIntrinsicLatin1", "([B[BI)Z")}, + {x(TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1, "toUpperIntrinsicLatin1", "([C[CI)Z")}, + {x(TR::com_ibm_jit_JITHelpers_toLowerIntrinsicLatin1, "toLowerIntrinsicLatin1", "([B[BI)Z")}, + {x(TR::com_ibm_jit_JITHelpers_toLowerIntrinsicLatin1, "toLowerIntrinsicLatin1", "([C[CI)Z")}, + {x(TR::com_ibm_jit_JITHelpers_toUpperIntrinsicUTF16, "toUpperIntrinsicUTF16", "([B[BI)Z")}, + {x(TR::com_ibm_jit_JITHelpers_toUpperIntrinsicUTF16, "toUpperIntrinsicUTF16", "([C[CI)Z")}, + {x(TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16, "toLowerIntrinsicUTF16", "([B[BI)Z")}, + {x(TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16, "toLowerIntrinsicUTF16", "([C[CI)Z")}, + { TR::unknownMethod} }; static X StringUTF16Methods[] = diff --git a/runtime/compiler/ilgen/IlGenerator.cpp b/runtime/compiler/ilgen/IlGenerator.cpp index 501d8d70b06..067d3b25ab9 100644 --- a/runtime/compiler/ilgen/IlGenerator.cpp +++ b/runtime/compiler/ilgen/IlGenerator.cpp @@ -211,9 +211,9 @@ bool TR_J9ByteCodeIlGenerator::internalGenIL() } } - if (recognizedMethod == TR::java_lang_String_StrHWAvailable && !TR::Compiler->om.canGenerateArraylets()) + if (recognizedMethod == TR::com_ibm_jit_JITHelpers_supportsIntrinsicCaseConversion && !TR::Compiler->om.canGenerateArraylets()) { - if (performTransformation(comp(), "O^O IlGenerator: Generate java/lang/String.StrHWAvailable\n")) + if (performTransformation(comp(), "O^O IlGenerator: Generate com/ibm/jit/JITHelpers.supportsIntrinsicCaseConversion\n")) { genHWOptimizedStrProcessingAvailable(); return true; diff --git a/runtime/compiler/ilgen/Walker.cpp b/runtime/compiler/ilgen/Walker.cpp index 8f4a0712737..e5f4f813ba4 100644 --- a/runtime/compiler/ilgen/Walker.cpp +++ b/runtime/compiler/ilgen/Walker.cpp @@ -5023,7 +5023,7 @@ break } } - if(symbol->getRecognizedMethod() == TR::java_lang_String_StrHWAvailable) + if(symbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_supportsIntrinsicCaseConversion) { if (cg()->getSupportsInlineStringCaseConversion()) constToLoad = 1; @@ -5035,12 +5035,10 @@ break } if (cg()->getSupportsInlineStringCaseConversion() && - (symbol->getRecognizedMethod() == TR::java_lang_String_toUpperHWOptimizedCompressed || - symbol->getRecognizedMethod() == TR::java_lang_String_toLowerHWOptimizedCompressed || - symbol->getRecognizedMethod() == TR::java_lang_String_toUpperHWOptimizedDecompressed || - symbol->getRecognizedMethod() == TR::java_lang_String_toLowerHWOptimizedDecompressed || - symbol->getRecognizedMethod() == TR::java_lang_String_toUpperHWOptimized || - symbol->getRecognizedMethod() == TR::java_lang_String_toLowerHWOptimized)) + (symbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1 || + symbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_toLowerIntrinsicLatin1 || + symbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_toUpperIntrinsicUTF16 || + symbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16)) { isDirectCall = true; } diff --git a/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp b/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp index 54ea18baad7..9e7c47c7a87 100644 --- a/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp +++ b/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp @@ -42,98 +42,6 @@ #include "codegen/CodeGenerator.hpp" // for CodeGenerator #include "optimizer/TransformUtil.hpp" -/* - * \brief transform the arguments of case conversion methods to make the call node friendly to code generator - * - * \details - * The original case conversion method call has 2 children: the src string obj and the dst string obj which will be - * the converted string obj. - * After the transformation, the 2 children are replaced with 3 new children: the src string array address, the dst string - * array address and the string length. The code generator can then focus on converting the actual content of the array and - * doesn't need to calculate the various offsets on different platforms. - * - * Before the transformation: - * icall - * =>aload src string obj - * =>aload dst string obj - * - * After the transformation: - * icall - * =>aladd - * =>aloadi String.value - * =>aload src string obj - * lonst arrayheaderoffset - * =>aladd - * =>aloadi String.value - * =>aload dst string obj - * lonst arrayheaderoffset - * =>aloadi String.count - * =>aload src string obj - */ -void J9::RecognizedCallTransformer::processCaseConversion(TR::Node* node) - { - TR::Node *srcStringObject = node->getFirstChild(); - TR::Node *dstStringObject = node->getSecondChild(); - - /* Offset of java/lang/String.value */ - uint32_t stringValueFieldOffset = 0; - char *valueFieldString = NULL; - TR_OpaqueClassBlock *stringClass = comp()->fej9()->getClassFromSignature("Ljava/lang/String;", strlen("Ljava/lang/String;"), comp()->getCurrentMethod(), true); - - if (comp()->fej9()->getInstanceFieldOffset(stringClass, "value", "[B") != ~0) - { - stringValueFieldOffset = comp()->fej9()->getInstanceFieldOffsetIncludingHeader("Ljava/lang/String;", "value", "[B", comp()->getCurrentMethod()); - valueFieldString = "java/lang/String.value [B"; - } - else - { - TR_ASSERT(comp()->fej9()->getInstanceFieldOffset(stringClass, "value", "[C") != ~0, "can't find java/lang/String.value field"); - stringValueFieldOffset = comp()->fej9()->getInstanceFieldOffsetIncludingHeader("Ljava/lang/String;", "value", "[C", comp()->getCurrentMethod()); - valueFieldString = "java/lang/String.value [C"; - } - - TR::SymbolReference *stringValueFieldSymRef = comp()->getSymRefTab()->findOrFabricateShadowSymbol(comp()->getMethodSymbol(), - TR::Symbol::Java_lang_String_value, - TR::Address, - stringValueFieldOffset, - false /* isVolatile */, - true /* isPrivate */, - true /* isFinal */, - valueFieldString); - - TR::Node* srcArrayObject = TR::Node::createWithSymRef(node, TR::aloadi, 1, srcStringObject, stringValueFieldSymRef); - TR::Node* dstArrayObject = TR::Node::createWithSymRef(node, TR::aloadi, 1, dstStringObject, stringValueFieldSymRef); - - TR::Node *srcArray = TR::Compiler->target.is64Bit() ? - TR::Node::create(node, TR::aladd, 2, srcArrayObject, TR::Node::lconst(srcArrayObject, TR::Compiler->om.contiguousArrayHeaderSizeInBytes())): - TR::Node::create(node, TR::aiadd, 2, srcArrayObject, TR::Node::iconst(srcArrayObject, TR::Compiler->om.contiguousArrayHeaderSizeInBytes())); - - TR::Node *dstArray = TR::Compiler->target.is64Bit() ? - TR::Node::create(node, TR::aladd, 2, dstArrayObject, TR::Node::lconst(dstArrayObject, TR::Compiler->om.contiguousArrayHeaderSizeInBytes())): - TR::Node::create(node, TR::aiadd, 2, dstArrayObject, TR::Node::iconst(dstArrayObject, TR::Compiler->om.contiguousArrayHeaderSizeInBytes())); - - node->setAndIncChild(0, srcArray); - srcStringObject->recursivelyDecReferenceCount(); - node->setAndIncChild(1, dstArray); - dstStringObject->recursivelyDecReferenceCount(); - - /* Offset of java/lang/String.count */ - uint32_t stringCountFieldOffset = comp()->fej9()->getInstanceFieldOffsetIncludingHeader("Ljava/lang/String;", "count", "I", comp()->getCurrentMethod()); - TR::SymbolReference *stringCountFieldSymRef = comp()->getSymRefTab()->findOrFabricateShadowSymbol(comp()->getMethodSymbol(), - TR::Symbol::Java_lang_String_count, - TR::Int32, - stringCountFieldOffset, - false /* isVolatile */, - true /* isPrivate */, - true /* isFinal */, - "java/lang/String.count I"); - - TR::Node *stringLength = TR::Node::createWithSymRef(node, TR::iloadi, 1, srcStringObject, stringCountFieldSymRef); - TR::Node *newChildren[1]; - newChildren[0] = stringLength; - node->addChildren(newChildren, 1); - } - void J9::RecognizedCallTransformer::processIntrinsicFunction(TR::TreeTop* treetop, TR::Node* node, TR::ILOpCodes opcode) { TR::Node::recreate(node, opcode); @@ -156,13 +64,6 @@ bool J9::RecognizedCallTransformer::isInlineable(TR::TreeTop* treetop) case TR::java_lang_Math_max_L: case TR::java_lang_Math_min_L: return TR::Compiler->target.cpu.isX86() && !comp()->getOption(TR_DisableMaxMinOptimization); - case TR::java_lang_String_toLowerHWOptimized: - case TR::java_lang_String_toLowerHWOptimizedDecompressed: - case TR::java_lang_String_toLowerHWOptimizedCompressed: - case TR::java_lang_String_toUpperHWOptimized: - case TR::java_lang_String_toUpperHWOptimizedDecompressed: - case TR::java_lang_String_toUpperHWOptimizedCompressed: - return comp()->cg()->getSupportsInlineStringCaseConversion() && TR::Compiler->target.cpu.isX86(); default: return false; } @@ -200,14 +101,6 @@ void J9::RecognizedCallTransformer::transform(TR::TreeTop* treetop) case TR::java_lang_Math_min_L: processIntrinsicFunction(treetop, node, TR::lmin); break; - case TR::java_lang_String_toLowerHWOptimized: - case TR::java_lang_String_toLowerHWOptimizedDecompressed: - case TR::java_lang_String_toUpperHWOptimized: - case TR::java_lang_String_toUpperHWOptimizedDecompressed: - case TR::java_lang_String_toLowerHWOptimizedCompressed: - case TR::java_lang_String_toUpperHWOptimizedCompressed: - processCaseConversion(node); - break; default: break; } diff --git a/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp b/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp index cc2b3cd7ce4..eb14af497fc 100644 --- a/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp +++ b/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp @@ -41,7 +41,6 @@ class RecognizedCallTransformer : public OMR::RecognizedCallTransformer private: void processIntrinsicFunction(TR::TreeTop* treetop, TR::Node* node, TR::ILOpCodes opcode); - void processCaseConversion(TR::Node* node); }; } diff --git a/runtime/compiler/x/codegen/J9TreeEvaluator.cpp b/runtime/compiler/x/codegen/J9TreeEvaluator.cpp index d4bfaadee05..195d8eb16d4 100644 --- a/runtime/compiler/x/codegen/J9TreeEvaluator.cpp +++ b/runtime/compiler/x/codegen/J9TreeEvaluator.cpp @@ -14115,16 +14115,14 @@ J9::X86::TreeEvaluator::directCallEvaluator(TR::Node *node, TR::CodeGenerator *c case TR::java_lang_String_andOR: return TR::TreeEvaluator::andORStringEvaluator(node, cg); - case TR::java_lang_String_toUpperHWOptimized: - case TR::java_lang_String_toUpperHWOptimizedDecompressed: - return TR::TreeEvaluator::decompressedStringToUpperCaseEvalutor(node, cg); - case TR::java_lang_String_toUpperHWOptimizedCompressed: - return TR::TreeEvaluator::compressedStringToUpperCaseEvalutor(node, cg); - case TR::java_lang_String_toLowerHWOptimized: - case TR::java_lang_String_toLowerHWOptimizedDecompressed: - return TR::TreeEvaluator::decompressedStringToLowerCaseEvalutor(node, cg); - case TR::java_lang_String_toLowerHWOptimizedCompressed: - return TR::TreeEvaluator::compressedStringToUpperCaseEvalutor(node, cg); + case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicUTF16: + return TR::TreeEvaluator::toUpperIntrinsicUTF16Evaluator(node, cg); + case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1: + return TR::TreeEvaluator::toUpperIntrinsicLatin1Evaluator(node, cg); + case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16: + return TR::TreeEvaluator::toLowerIntrinsicUTF16Evaluator(node, cg); + case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicLatin1: + return TR::TreeEvaluator::toLowerIntrinsicLatin1Evaluator(node, cg); default: break; } @@ -14446,7 +14444,7 @@ class J9::X86::TreeEvaluator::CaseConversionManager { }; TR::Register * -J9::X86::TreeEvaluator::compressedStringToUpperCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::toUpperIntrinsicLatin1Evaluator(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(true /* isCompressedString */, false /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); @@ -14454,21 +14452,21 @@ J9::X86::TreeEvaluator::compressedStringToUpperCaseEvalutor(TR::Node *node, TR:: TR::Register * -J9::X86::TreeEvaluator::compressedStringToLowerCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::toLowerIntrinsicLatin1Evaluator(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(true/* isCompressedString */, true /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); } TR::Register * -J9::X86::TreeEvaluator::decompressedStringToUpperCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::toUpperIntrinsicUTF16Evaluator(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(false /* isCompressedString */, false /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); } TR::Register * -J9::X86::TreeEvaluator::decompressedStringToLowerCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::toLowerIntrinsicUTF16Evaluator(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(false /* isCompressedString */, true /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); @@ -14505,17 +14503,16 @@ TR::Register * J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGenerator *cg, CaseConversionManager &manager) { #define iComment(str) if (debug) debug->addInstructionComment(cursor, (const_cast(str))); - TR_ASSERT(node->getNumChildren() == 3, "node has incorrect number of children"); TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions((uint8_t)14, (uint8_t)14, cg); - TR::Register *srcArray = cg->evaluate(node->getFirstChild()); + TR::Register *srcArray = cg->evaluate(node->getChild(1)); deps->addPostCondition(srcArray, TR::RealRegister::NoReg, cg); deps->addPreCondition(srcArray, TR::RealRegister::NoReg, cg); - TR::Register *dstArray = cg->evaluate(node->getSecondChild()); + TR::Register *dstArray = cg->evaluate(node->getChild(2)); deps->addPostCondition(dstArray, TR::RealRegister::NoReg, cg); deps->addPreCondition(dstArray, TR::RealRegister::NoReg, cg); - TR::Register *length = cg->intClobberEvaluate(node->getThirdChild()); + TR::Register *length = cg->intClobberEvaluate(node->getChild(3)); deps->addPostCondition(length, TR::RealRegister::NoReg, cg); deps->addPreCondition(length, TR::RealRegister::NoReg, cg); @@ -14535,7 +14532,8 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener TR_Debug *debug = cg->getDebug(); TR::Instruction * cursor = NULL; - uint32_t strideSize = 16; + uint32_t strideSize = 16; + uintptrj_t headerSize = TR::Compiler->om.contiguousArrayHeaderSizeInBytes(); static uint16_t MINUS1[] = { @@ -14570,11 +14568,7 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener generateRegRegInstruction(MOVRegReg(), node, result, dstArray, cg); - // # charater to # byte conversion - if (!manager.isCompressedString()) // non compressedString using char[] which is 2 bytes per charactor - cursor = generateRegImmInstruction(SHLRegImm1(), node, length, 1, cg); iComment("# character to # byte conversion"); - - // initialize the loop counter + // initialize the loop counter cursor = generateRegRegInstruction(XORRegReg(), node, counter, counter, cg); iComment("initialize loop counter"); //calculate the residueStartLength. Later instructions compare the counter with this length and decide when to jump to the residue handling sequence @@ -14596,7 +14590,7 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener generateRegRegInstruction(CMPRegReg(), node, counter, residueStartLength, cg); generateLabelInstruction(JGE4, node, residueStartLabel, cg); - auto srcArrayMemRef = generateX86MemoryReference(srcArray, counter, 0, 0, cg); + auto srcArrayMemRef = generateX86MemoryReference(srcArray, counter, 0, headerSize, cg); generateRegMemInstruction(MOVDQURegMem, node, xmmRegArrayContentCopy0, srcArrayMemRef, cg); //detect invalid charactors @@ -14628,7 +14622,7 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener generateRegRegInstruction(manager.isCompressedString()? PSUBBRegReg: PSUBWRegReg, node, xmmRegArrayContentCopy2, xmmRegArrayContentCopy1, cg); - auto dstArrayMemRef = generateX86MemoryReference(dstArray, counter, 0, 0, cg); + auto dstArrayMemRef = generateX86MemoryReference(dstArray, counter, 0, headerSize, cg); generateMemRegInstruction(MOVDQUMemReg, node, dstArrayMemRef, xmmRegArrayContentCopy2, cg); generateRegImmInstruction(ADDRegImms(), node, counter, strideSize, cg); generateLabelInstruction(JMP4, node, caseConversionMainLoopLabel, cg); @@ -14637,7 +14631,7 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener generateLabelInstruction(LABEL, node, residueStartLabel, cg); generateRegRegInstruction(CMPRegReg(), node, counter, length, cg); generateLabelInstruction(JGE4, node, endLabel, cg); - srcArrayMemRef = generateX86MemoryReference(srcArray, counter, 0, 0, cg); + srcArrayMemRef = generateX86MemoryReference(srcArray, counter, 0, headerSize, cg); generateRegMemInstruction( manager.isCompressedString()? MOVZXReg4Mem1: MOVZXReg4Mem2, node, singleChar, srcArrayMemRef, cg); // use unsigned compare to detect invalid range @@ -14661,7 +14655,7 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener generateLabelInstruction(LABEL, node, storeToArrayLabel, cg); - dstArrayMemRef = generateX86MemoryReference(dstArray, counter, 0, 0, cg); + dstArrayMemRef = generateX86MemoryReference(dstArray, counter, 0, headerSize, cg); generateMemRegInstruction(manager.isCompressedString()? S1MemReg: S2MemReg, node, dstArrayMemRef, singleChar, cg); generateRegImmInstruction(ADDRegImms(), node, counter, manager.isCompressedString()? 1: 2, cg); generateLabelInstruction(JMP4, node, residueStartLabel, cg); @@ -14687,9 +14681,10 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener cg->stopUsingRegister(xmmRegArrayContentCopy2); - cg->decReferenceCount(node->getFirstChild()); - cg->decReferenceCount(node->getSecondChild()); - cg->decReferenceCount(node->getThirdChild()); + cg->decReferenceCount(node->getChild(0)); + cg->decReferenceCount(node->getChild(1)); + cg->decReferenceCount(node->getChild(2)); + cg->decReferenceCount(node->getChild(3)); return result; } diff --git a/runtime/compiler/x/codegen/J9TreeEvaluator.hpp b/runtime/compiler/x/codegen/J9TreeEvaluator.hpp index a0a16fb6d9a..2cd85de5c3f 100644 --- a/runtime/compiler/x/codegen/J9TreeEvaluator.hpp +++ b/runtime/compiler/x/codegen/J9TreeEvaluator.hpp @@ -113,10 +113,10 @@ class OMR_EXTENSIBLE TreeEvaluator: public J9::TreeEvaluator static TR::Register *compressStringEvaluator(TR::Node *node, TR::CodeGenerator *cg, bool japaneseMethod); static TR::Register *compressStringNoCheckEvaluator(TR::Node *node, TR::CodeGenerator *cg, bool japaneseMethod); static TR::Register *andORStringEvaluator(TR::Node *node, TR::CodeGenerator *cg); - static TR::Register *decompressedStringToUpperCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg); - static TR::Register *decompressedStringToLowerCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg); - static TR::Register *compressedStringToUpperCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg); - static TR::Register *compressedStringToLowerCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *toUpperIntrinsicUTF16Evaluator(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *toLowerIntrinsicUTF16Evaluator(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *toUpperIntrinsicLatin1Evaluator(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *toLowerIntrinsicLatin1Evaluator(TR::Node *node, TR::CodeGenerator *cg); class CaseConversionManager; static TR::Register *stringCaseConversionHelper(TR::Node *node, TR::CodeGenerator *cg, CaseConversionManager& manager); diff --git a/runtime/compiler/z/codegen/J9CodeGenerator.cpp b/runtime/compiler/z/codegen/J9CodeGenerator.cpp index 78762a69002..c3fe6722dea 100644 --- a/runtime/compiler/z/codegen/J9CodeGenerator.cpp +++ b/runtime/compiler/z/codegen/J9CodeGenerator.cpp @@ -3935,8 +3935,8 @@ extern TR::Register *inlineBigDecimalUnscaledValue(TR::Node * node, TR::CodeGene extern TR::Register *inlineBigDecimalFromPackedConverter(TR::Node * node, TR::CodeGenerator * cg); extern TR::Register *inlineBigDecimalToPackedConverter(TR::Node * node, TR::CodeGenerator * cg); -extern TR::Register *inlineToUpper(TR::Node * node, TR::CodeGenerator * cg, bool isCompressedString); -extern TR::Register *inlineToLower(TR::Node * node, TR::CodeGenerator * cg, bool isCompressedString); +extern TR::Register *toUpperIntrinsic(TR::Node * node, TR::CodeGenerator * cg, bool isCompressedString); +extern TR::Register *toLowerIntrinsic(TR::Node * node, TR::CodeGenerator * cg, bool isCompressedString); extern TR::Register *inlineDoubleMax(TR::Node *node, TR::CodeGenerator *cg); extern TR::Register *inlineDoubleMin(TR::Node *node, TR::CodeGenerator *cg); @@ -4204,19 +4204,17 @@ J9::Z::CodeGenerator::inlineDirectCall( { switch (methodSymbol->getRecognizedMethod()) { - case TR::java_lang_String_toUpperHWOptimized: - case TR::java_lang_String_toUpperHWOptimizedDecompressed: - resultReg = inlineToUpper(node, cg, false); + case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicUTF16: + resultReg = toUpperIntrinsic(node, cg, false); return true; - case TR::java_lang_String_toUpperHWOptimizedCompressed: - resultReg = inlineToUpper(node, cg, true); + case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1: + resultReg = toUpperIntrinsic(node, cg, true); return true; - case TR::java_lang_String_toLowerHWOptimized: - case TR::java_lang_String_toLowerHWOptimizedDecompressed: - resultReg = inlineToLower(node, cg, false); + case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16: + resultReg = toLowerIntrinsic(node, cg, false); return true; - case TR::java_lang_String_toLowerHWOptimizedCompressed: - resultReg = inlineToLower(node, cg, true); + case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicLatin1: + resultReg = toLowerIntrinsic(node, cg, true); return true; default: break; diff --git a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp index 4f428e0ca33..011ad8266d5 100644 --- a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp +++ b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp @@ -211,405 +211,249 @@ doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp) return node->getRegister(); } - -/** - * This evaluator is used to perform string toUpper and toLower conversion. - * - * This JIT HW optimized conversion helper is deisgned to convert strings that contains only valid characters, as - * specified by the ranges below: - * - * 1. Lower case English letters a-z [0x61, 0x7a] and their corresponding A-Z range [0x41, 0x5a] - * 2. Lower case Latin letters [0xe0, 0xfe], excluding the multiplication sign 0xf7. The corresponding capital Latin letters are in [0xc0, 0xde] excluding the division sign 0xd7 - * - * Invalid char range is defined as: - * A). For both toUpper and toLower conversion, a string contains invalid char if it contains anything above 0x00ff. - * - * If a string contains characters (char above 0x00fe), HW optimized routine will return NULL and fall back to the software implementation, which is able to convert a broader range of characters. - * - * This helper is broken up into 6 sections: - * 1. Setup - * Registers - * Ranges for vector char search - * Dependencies - * Zero out registers - * 2. Length calculations - * Align to 16 - * Get residue (if any) - * 3. Process residue (since < 16 is probably the most common case) - * 4. Process 16 bytes at a time in a loop - * 5. Handle invalid codepoints - * 6. Cleanup - * - * This does not support discontiguous arrays (..no reason we can't) and the check is done in ILGen/Walker - */ -extern TR::Register * -caseConversionHelper(TR::Node *node, TR::CodeGenerator *cg, bool isToUpper, bool isCompressedString) + /** \brief + * Attempts to use vector registers to perform SIMD conversion of characters from lowercase to uppercase. + * + * \detail + * Uses vector registers to convert 16 bytes at a time. + * + * \param node + * The node representing the HW optimized toUpper and toLower recognized calls. + * + * \param cg + * The code generator used to generate the instructions. + * + * \param isToUpper + * Boolean representing case conversion, either to upper or to lower. + * + * \param isCompressedString + * Boolean representing the string's compression. + * + * \return + * A register containing the return value of the Java call. The return value + * will be 1 if the entire contents of the input array was translated and 0 if + * we were unable to translate the entire contents of the array (up to the specified length). + */ +TR::Register * caseConversionHelper(TR::Node* node, TR::CodeGenerator* cg, bool isToUpper, bool isCompressedString) { - #define iComment(str) if (debug) debug->addInstructionComment(cursor, (str)); - - // Usually just 2 children... - TR_ASSERT(node->getNumChildren() >= 1 || node->getNumChildren() <= 2, "node has incorrect number of children"); - - - /* ===================== Step 1: SETUP =====================*/ - - - TR::Register * rSrcBase = cg->evaluate(node->getFirstChild()); // Source object - TR::Register * rTgtBase = cg->evaluate(node->getSecondChild()); // Target object - - TR::Register * rSrcValIdx = cg->allocateRegister(); // Used to iterate over input (and output since they overlap) - TR::Register * rSrcVal = cg->allocateRegister(); // The actual char array storing the contents of j.l.S.value of input String - TR::Register * rTgtVal = cg->allocateRegister(); // The actual char array storing the contents of j.l.S.value of output String - - TR::Register * rLen = cg->allocateRegister(); // Number of code-points j.l.S.value.length. Not to be confused with length of the String - TR::Register * rResidue = cg->allocateRegister(); // Residue after alignment of length to 16 - TR::Register * rLoopCounter = rLen; - - TR::Register * vTmp = cg->allocateRegister(TR_VRF); // Temp buffer to store result of VSTRC - TR::Register * vCaseBuf = cg->allocateRegister(TR_VRF); // upper or lower case depending on context - TR::Register * vBuf = cg->allocateRegister(TR_VRF); // Temp buffer used to store output of upper/lower case conversion - TR::Register * vAlphaRange = cg->allocateRegister(TR_VRF); // Alpha range char: the upper/lower limit that VSTRC should be used for comparison - TR::Register * vAlphaCntrl = cg->allocateRegister(TR_VRF); // Alpha range control : the control bits (refer to zPoPs) to let VSTRC know what kind of comparison to make (lt/gt/eq etc) - TR::Register * vInvalidRange = cg->allocateRegister(TR_VRF); // Invalid code-point range: the "mu" code-point and everyone above 0xFE - TR::Register * vInvalidCntrl = cg->allocateRegister(TR_VRF); // Invalid code-point control - TR::Register * vOffset = cg->allocateRegister(TR_VRF); // Constant positive integral offset between upper/lower code-points for Unicode - - TR_Debug * debug = cg->getDebug(); - TR::Compilation * comp = cg->comp(); - - const int sizeOfVector = 16; // in Bytes. Should really make this dynamic (i.e read from some machine() query since this can increase in future) - const int numPostDeps = 15; - const int caseOffset = 0x20; - const int elementSizeMask = (isCompressedString) ? 0x0 : 0x1; // byte or halfword - - TR::Instruction* cursor = NULL; - bool usesCompressedrefs = comp->useCompressedPointers(); - int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift(); - const int offsetOfContigField = cg->fej9()->getOffsetOfContiguousArraySizeField(); - const int offsetOfDiscontigField = cg->fej9()->getOffsetOfDiscontiguousArraySizeField(); - const bool is64 = TR::Compiler->target.is64Bit(); - - TR::RegisterDependencyConditions * regDeps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, numPostDeps, cg); - - regDeps->addPostCondition(rSrcVal ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(rSrcBase ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(rTgtBase ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(rTgtVal ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(rSrcValIdx ,TR::RealRegister::AssignAny); - - regDeps->addPostCondition(rLen ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(rResidue ,TR::RealRegister::AssignAny); - - regDeps->addPostCondition(vTmp ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(vCaseBuf ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(vBuf ,TR::RealRegister::AssignAny); - - regDeps->addPostCondition(vOffset ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(vAlphaRange ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(vAlphaCntrl ,TR::RealRegister::AssignAny); - - regDeps->addPostCondition(vInvalidRange ,TR::RealRegister::AssignAny); - regDeps->addPostCondition(vInvalidCntrl ,TR::RealRegister::AssignAny); - - if (cg->supportsHighWordFacility() && - !comp->getOption(TR_DisableHighWordRA) && - TR::Compiler->target.is64Bit()) - { - rResidue->setIs64BitReg(true); - rLen->setIs64BitReg(true); - rSrcValIdx->setIs64BitReg(true); - rTgtVal->setIs64BitReg(true); - rSrcVal->setIs64BitReg(true); - } - - // byte [] java.lang.String.value the raw byte array - // int java.lang.String.count the count i.e num of characters. A Unicode point is 1 character. Surrogate's are 2 characters. - // valueConentOffset the offset of actual content in a char array. - int32_t stringValOffset, stringCountOffset, valueContentOffset; - - // TODO (Filip): This is a workaround for Java 829 performance as we switched to using a byte[] backing array in String*. Remove this workaround once obsolete. - TR_OpaqueClassBlock *stringClass = cg->fej9()->getClassFromSignature("Ljava/lang/String;", strlen("Ljava/lang/String;"), comp->getCurrentMethod(), true); - - if (cg->fej9()->getInstanceFieldOffset(stringClass, "value", "[B") != ~0) - { - stringValOffset = cg->fej9()->getInstanceFieldOffsetIncludingHeader("Ljava/lang/String;", "value", "[B", comp->getCurrentMethod()); - } - else - { - stringValOffset = cg->fej9()->getInstanceFieldOffsetIncludingHeader("Ljava/lang/String;", "value", "[C", comp->getCurrentMethod()); - } - - stringCountOffset = cg->fej9()->getInstanceFieldOffsetIncludingHeader("Ljava/lang/String;", "count", "I" , comp->getCurrentMethod()); - valueContentOffset = TR::Compiler->om.contiguousArrayHeaderSizeInBytes(); - - TR::LabelSymbol * processNext16Bytes = TR::LabelSymbol::create(cg->trHeapMemory(),cg); - TR::LabelSymbol * doneLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg); - TR::LabelSymbol * handleInvalidChars = TR::LabelSymbol::create(cg->trHeapMemory(),cg); - TR::LabelSymbol * coreLoopSetup = TR::LabelSymbol::create(cg->trHeapMemory(),cg); - - // Load this.value for source string and target string - if (usesCompressedrefs) - { - cursor = generateRXInstruction (cg, TR::InstOpCode::LLGF, node, rSrcVal, generateS390MemoryReference(rSrcBase, stringValOffset, cg)); - if (shiftAmount != 0 ) generateRSInstruction (cg, TR::InstOpCode::SLLG, node, rSrcVal, rSrcVal, shiftAmount); - - cursor = generateRXInstruction (cg, TR::InstOpCode::LLGF, node, rTgtVal, generateS390MemoryReference(rTgtBase, stringValOffset, cg)); - if (shiftAmount != 0 ) generateRSInstruction (cg, TR::InstOpCode::SLLG, node, rTgtVal, rTgtVal, shiftAmount); - } - else - { - cursor = generateRXInstruction (cg, TR::InstOpCode::getLoadOpCode(), node, rSrcVal, generateS390MemoryReference(rSrcBase, stringValOffset, cg)); - cursor = generateRXInstruction (cg, TR::InstOpCode::getLoadOpCode(), node, rTgtVal, generateS390MemoryReference(rTgtBase, stringValOffset, cg)); - } iComment("<- output.value"); - - // Load this.count. Skip 0 check since since that in done in Java - cursor = generateRXInstruction (cg, is64 ? TR::InstOpCode::LLGF : TR::InstOpCode::L, node, rLen, generateS390MemoryReference(rSrcBase, stringCountOffset, cg)); iComment("<- input.count"); - - // Bail out if string is larger than INT_MAX32/2 since # string to # byte conversion will cause overflow. - // We could have used 64 bit reg instructions and reg for 31bit JVM with proper sign/zero extension, - // but using this keeps the icache footprint down by a little bit. Also enables us to use the HPRs. - if (!is64) - { - cursor = generateRIInstruction (cg, TR::InstOpCode::TMLH, node, rLen, (uint16_t) 0x8000); - cursor = generateS390BranchInstruction (cg, TR::InstOpCode::BRC , TR::InstOpCode::COND_MASK2, node, handleInvalidChars); - cursor->setStartInternalControlFlow(); - } - - // Update output.count - cursor = generateRXInstruction (cg, TR::InstOpCode::ST, node, rLen, generateS390MemoryReference(rTgtBase, stringCountOffset, cg)); iComment("<- output.count"); - - // Conservatively zero out the ranges but we definitely need to zero out the control. - generateVRIaInstruction (cg, TR::InstOpCode::VGBM, node, vAlphaRange , 0, 0 /*unused*/); - generateVRIaInstruction (cg, TR::InstOpCode::VGBM, node, vAlphaCntrl , 0, 0 /*unused*/); - generateVRIaInstruction (cg, TR::InstOpCode::VGBM, node, vInvalidCntrl, 0, 0 /*unused*/); - generateVRIaInstruction (cg, TR::InstOpCode::VGBM, node, vInvalidRange, 0, 0 /*unused*/); - - - /* ===================== Step 1: Setup (alphabet and control ranges) =====================*/ - /* - * alphaRange = isToUpper ? 0x0061007a00e000f600f800feL : 0x0041005a00c000d600d800deL; - * alphaCntrl = 0xa000c000a000c00000a000c0L; - * - * invalidRange = 0x00ff00ff00000000L - * invalidCntrl = 0x2000200000000000L - * - * This works like x <= val AND val <= y OR m <= val AND val <= n where x, y, m, n are valid alphabet range chars and - * val = any element sized val in vector buffer - * For the invalid case, we do p = val OR p > q; where p and q are the invalid ranges and val = any element sized val in vector buffer - * - * The following vAlphaRange, vInvalidRange, and vAlphaCntrl setup instructions load 16 byte ranges used for - * our VSTRC's without reading from memory. - * Encode ranges within the instructions instead of loading from memory since that will need relocation for AOT - * Problem with this setup is the inability to reduce WAW's by annulling in h/w. - */ - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaRange, isToUpper ? 0x617a : 0x415a, 0x0); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaRange, isToUpper ? 0xe0f6 : 0xc0d6, 0x1); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaRange, isToUpper ? 0xf8fe : 0xd8de, 0x2); iComment("alphabet ranges"); - if(!isCompressedString) - { - generateVRRaInstruction (cg, TR::InstOpCode::VUPLH, node, vAlphaRange, vAlphaRange, 0, 0, 0, 0); - } - - if(isCompressedString) - { - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xa0c0, 0x0); iComment("alphabet control. >='s and <='s"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xa0c0, 0x1); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xa0c0, 0x2); + TR::Register* sourceRegister = cg->evaluate(node->getChild(1)); + TR::Register* destRegister = cg->evaluate(node->getChild(2)); + TR::Register* lengthRegister = cg->gprClobberEvaluate(node->getChild(3)); + + TR::Register* addressOffset = cg->allocateRegister(); + TR::Register* loadLength = cg->allocateRegister(); + // Loopcounter register for number of 16 byte conversions, when it is used, the length is not needed anymore + TR::Register* loopCounter = lengthRegister; + + TR::Register* charBufferVector = cg->allocateRegister(TR_VRF); + TR::Register* selectionVector = cg->allocateRegister(TR_VRF); + TR::Register* modifiedCaseVector = cg->allocateRegister(TR_VRF); + TR::Register* charOffsetVector = cg->allocateRegister(TR_VRF); + TR::Register* alphaRangeVector = cg->allocateRegister(TR_VRF); + TR::Register* alphaCondVector = cg->allocateRegister(TR_VRF); + TR::Register* invalidRangeVector = cg->allocateRegister(TR_VRF); + TR::Register* invalidCondVector = cg->allocateRegister(TR_VRF); + + TR::LabelSymbol* fullVectorConversion = TR::LabelSymbol::create(cg->trHeapMemory(), cg); + TR::LabelSymbol* doneLabel = TR::LabelSymbol::create(cg->trHeapMemory(), cg); + TR::LabelSymbol* success = TR::LabelSymbol::create(cg->trHeapMemory(), cg); + TR::LabelSymbol* handleInvalidChars = TR::LabelSymbol::create(cg->trHeapMemory(), cg); + TR::LabelSymbol* loop = TR::LabelSymbol::create(cg->trHeapMemory(), cg); + + TR::Instruction* cursor; + + const int elementSizeMask = (isCompressedString) ? 0x0 : 0x1; // byte or halfword mask + const int8_t sizeOfVector = 16; + const bool is64 = TR::Compiler->target.is64Bit(); + uintptrj_t headerSize = TR::Compiler->om.contiguousArrayHeaderSizeInBytes(); + + TR::RegisterDependencyConditions * regDeps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 13, cg); + regDeps->addPostCondition(sourceRegister, TR::RealRegister::AssignAny); + regDeps->addPostCondition(destRegister, TR::RealRegister::AssignAny); + regDeps->addPostCondition(lengthRegister, TR::RealRegister::AssignAny); + regDeps->addPostCondition(addressOffset, TR::RealRegister::AssignAny); + regDeps->addPostCondition(loadLength, TR::RealRegister::AssignAny); + regDeps->addPostCondition(charBufferVector, TR::RealRegister::AssignAny); + regDeps->addPostCondition(selectionVector, TR::RealRegister::AssignAny); + regDeps->addPostCondition(modifiedCaseVector, TR::RealRegister::AssignAny); + regDeps->addPostCondition(charOffsetVector, TR::RealRegister::AssignAny); + regDeps->addPostCondition(alphaRangeVector, TR::RealRegister::AssignAny); + regDeps->addPostCondition(alphaCondVector, TR::RealRegister::AssignAny); + regDeps->addPostCondition(invalidRangeVector, TR::RealRegister::AssignAny); + regDeps->addPostCondition(invalidCondVector, TR::RealRegister::AssignAny); + + generateRRInstruction(cg, TR::InstOpCode::getXORRegOpCode(), node, addressOffset, addressOffset); + + generateVRIaInstruction(cg, TR::InstOpCode::VGBM, node, alphaRangeVector, 0, 0); + generateVRIaInstruction(cg, TR::InstOpCode::VGBM, node, alphaCondVector, 0, 0); + generateVRIaInstruction(cg, TR::InstOpCode::VGBM, node, invalidRangeVector, 0, 0); + generateVRIaInstruction(cg, TR::InstOpCode::VGBM, node, invalidCondVector, 0, 0); + + // Letters a-z (0x61-0x7A) when to upper and A-Z (0x41-0x5A) when to lower + generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, alphaRangeVector, isToUpper ? 0x617A : 0x415A, 0x0); + // Characters àáâãäåæçèéêëìíîïðñòóôõö (0xE0-0xF6) when to upper and ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ (0xC0-0xD6) when to lower + generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, alphaRangeVector, isToUpper ? 0xE0F6 : 0xC0D6, 0x1); + // Characters øùúûüýþ (0xF8-0xFE) when to upper and ØÙÚÛÜÝÞ (0xD8-0xDE) when to lower + generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, alphaRangeVector, isToUpper ? 0xF8FE : 0xD8DE, 0X2); + if (!isCompressedString) + { + generateVRRaInstruction(cg, TR::InstOpCode::VUPLH, node, alphaRangeVector, alphaRangeVector, 0, 0, 0, 0); + } + + // Condition codes for >= (bits 0 and 2) and <= (bits 0 and 1) + if (isCompressedString) + { + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XA0C0, 0X0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XA0C0, 0X1); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XA0C0, 0X2); } else { - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xa000, 0x0); iComment("alphabet control. >='s and <='s"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xc000, 0x1); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xa000, 0x2); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xc000, 0x3); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xa000, 0x4); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vAlphaCntrl, 0xc000, 0x5); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XA000, 0X0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XC000, 0X1); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XA000, 0X2); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XC000, 0X3); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XA000, 0X4); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, alphaCondVector, 0XC000, 0X5); } - // Can't toUpper \u00df (capital sharp s) nor \u00b5 (mu). Applicable to both compressed and decompressed toUpper. - // \u00df toUpper becomes -> \u0053 \u0053 - // \u00b5 toUpper becomes -> \u039c - if(isToUpper && isCompressedString) + // Can't toUpper \u00df (capital sharp s) nor \u00b5 (mu) with a simple addition of 0x20 + // Condition code equal for capital sharp and mu (bit 0=0x80) and greater than (bit 2=0x20) for codes larger than 0xFF + if (isToUpper) { - // Compressed string toUpper - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidRange, 0xdfdf, 0x0); iComment("invalid alphabet range"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidRange, 0xb5b5, 0x1); - - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x8080, 0x0); iComment("invalid alphabet range control"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x8080, 0x1); - } - else if(isToUpper && !isCompressedString) - { - // Decompressed string toUpper - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidRange, 0xdfdf, 0x0); iComment("invalid alphabet range"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidRange, 0xb5b5, 0x1); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidRange, 0xffff, 0x2); - generateVRRaInstruction (cg, TR::InstOpCode::VUPLH, node, vInvalidRange, vInvalidRange, 0, 0, 0, 0); + if (isCompressedString) + { + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0xdfdf, 0x0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0xb5b5, 0x1); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x8080, 0x0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x8080, 0x1); + } + else + { + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0xdfdf, 0x0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0xb5b5, 0x1); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0xffff, 0x2); + generateVRRaInstruction(cg, TR::InstOpCode::VUPLH, node, invalidRangeVector, invalidRangeVector, 0, 0, 0, 0); - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x8000, 0x0); iComment("invalid alphabet range control"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x8000, 0x1); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x8000, 0x2); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x8000, 0x3); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x2000, 0x4); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x2000, 0x5); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x8000, 0x0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x8000, 0x1); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x8000, 0x2); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x8000, 0x3); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x2000, 0x4); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x2000, 0x5); + } } - else if(!isToUpper && !isCompressedString) + else if (!isToUpper && !isCompressedString) { - // Decompressed strings toLower. Only need to make sure that no character is above 0xff - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidRange, 0x00ff, 0x0); iComment("invalid alphabet range"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidRange, 0x00ff, 0x1); - - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x2000, 0x0); iComment("invalid alphabet control"); - generateVRIaInstruction (cg, TR::InstOpCode::VLEIH, node, vInvalidCntrl, 0x2000, 0x1); - } - - // Constant used to convert to upper/lower for Unicode code-points. This is a vector of halfwords 0x0020. - cursor = generateVRIaInstruction (cg, TR::InstOpCode::VREPI, node, vOffset, static_cast(caseOffset), elementSizeMask); iComment("offset to add/subtract lower<->upper conv"); - + // to lower is only invalid when values are greater than 0xFF + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0x00FF, 0x0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0x00FF, 0x1); - /* ===================== Step 2: Length calculations (rResidue and 16-byte alignment) =====================*/ - - - // Add string val offset to save additions in core loop - cursor = generateRXInstruction (cg, TR::InstOpCode::getLoadAddressOpCode(), node, rSrcVal, generateS390MemoryReference(rSrcVal, NULL, valueContentOffset, cg), cursor); iComment("source.val += strValOffset + source.offset"); - cursor = generateRXInstruction (cg, TR::InstOpCode::getLoadAddressOpCode(), node, rTgtVal, generateS390MemoryReference(rTgtVal, valueContentOffset, cg), cursor); iComment("tgt.val += strValOffset"); - - // The core loop works in multiples of 16 so we need to align the length to 16, and also load the rResidue so we can operate on them separately - if(!isCompressedString) - { - // convert num_of_char to num_of_bytes we need to process - cursor = generateRSInstruction (cg, TR::InstOpCode::getShiftLeftLogicalSingleOpCode(), node, rLen, rLen, 1); iComment("string length : char -> bytes"); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x2000, 0x0); + generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x2000, 0x1); } - cursor = generateRRInstruction (cg, TR::InstOpCode::getLoadRegOpCode(), node, rResidue, rLen); - cursor = generateRILInstruction (cg, TR::InstOpCode::NILF, node, rResidue, 0xF); iComment("calc residue (can eq length if length < 16)"); - cursor = generateS390BranchInstruction (cg, TR::InstOpCode::BRC , TR::InstOpCode::COND_BZ, node, coreLoopSetup); iComment("len perfect multiple of 16, goto main loop (rare)"); - - // We already set start ICF at the branch after the TMLH so don't do it here, to prevent nested ICF issues - if (is64) - cursor->setStartInternalControlFlow(); - // VLL and VSTL are provided with an index not with a count (length) so we need to offset by 1. - cursor = generateRILInstruction (cg, TR::InstOpCode::getSubtractLogicalImmOpCode(), node, rResidue, 1); iComment("adjust residue for VLL/VSTL (length -> index)"); + // Constant value of 0x20, used to convert between upper and lower + generateVRIaInstruction(cg, TR::InstOpCode::VREPI, node, charOffsetVector, static_cast(0x20), elementSizeMask); - /* ===================== Step 3: Residue processing =====================*/ + generateRRInstruction(cg, TR::InstOpCode::LR, node, loadLength, lengthRegister); + generateRILInstruction(cg, TR::InstOpCode::NILF, node, loadLength, 0xF); + cursor = generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BZ, node, fullVectorConversion); + cursor->setStartInternalControlFlow(); - // This is the core case conv logic. Since we unroll the loop to a multiple of 16, we need to handle residue here. - // Residue is also length if length < 16 - // We could have handled residue in the hot-loop by using VSTL always, but that comes at the expense of having a VLGV + mul x 2 in the hot-path + // VLL and VSTL take an index, not a count, so subtract the count by 1 + generateRILInstruction(cg, TR::InstOpCode::SLFI, node, loadLength, 1); - // Process residue - cursor = generateVRSbInstruction (cg, TR::InstOpCode::VLL, node, vBuf, rResidue, generateS390MemoryReference(rSrcVal, 0, cg)); iComment("====== PROCESS RESIDUE ======"); + generateVRSbInstruction(cg, TR::InstOpCode::VLL, node, charBufferVector, loadLength, generateS390MemoryReference(sourceRegister, addressOffset, headerSize, cg)); - // Check for invalid characters - if(!isCompressedString) + // Check for invalid characters, go to fallback individual character conversion implementation + if (!isCompressedString) { - cursor = generateVRRdInstruction (cg, TR::InstOpCode::VSTRC, node, vTmp, vBuf, vInvalidRange, vInvalidCntrl, 0x1, elementSizeMask); iComment("check for invalid codepoints"); - cursor = generateS390BranchInstruction (cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_CC1, node, handleInvalidChars); + generateVRRdInstruction(cg, TR::InstOpCode::VSTRC, node, selectionVector, charBufferVector, invalidRangeVector, invalidCondVector, 0x1 , elementSizeMask); + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_CC1, node, handleInvalidChars); } - // Check for alphabets that whose case we can convert - cursor = generateVRRdInstruction (cg, TR::InstOpCode::VSTRC, node, vTmp, vBuf, vAlphaRange, vAlphaCntrl, 0x4, elementSizeMask); iComment("search for case to convert"); - cursor = generateVRRcInstruction (cg, isToUpper ? TR::InstOpCode::VS : TR::InstOpCode::VA, node, vCaseBuf, vBuf, vOffset, 0x0, 0x0, elementSizeMask); iComment("offset for case conversion"); + generateVRRdInstruction(cg, TR::InstOpCode::VSTRC, node, selectionVector, charBufferVector, alphaRangeVector, alphaCondVector, 0x4, elementSizeMask); + generateVRRcInstruction(cg, isToUpper ? TR::InstOpCode::VS : TR::InstOpCode::VA, node, modifiedCaseVector, charBufferVector, charOffsetVector, 0x0, 0x0, elementSizeMask); + generateVRReInstruction(cg, TR::InstOpCode::VSEL, node, modifiedCaseVector, modifiedCaseVector, charBufferVector, selectionVector); - // Merge the case converted characters with the non-case converted characters and write to output String - cursor = generateVRReInstruction (cg, TR::InstOpCode::VSEL, node, vCaseBuf, vCaseBuf, vBuf, vTmp); iComment("only replace converted chars"); - cursor = generateVRSbInstruction (cg, TR::InstOpCode::VSTL, node, vCaseBuf, rResidue, generateS390MemoryReference(rTgtVal, 0, cg), 0); + generateVRSbInstruction(cg, TR::InstOpCode::VSTL, node, modifiedCaseVector, loadLength, generateS390MemoryReference(destRegister, addressOffset, headerSize, cg), 0); - // If len =< 16 then we're done else continue to main loop that does 16 byte at a time - cursor = generateRIEInstruction (cg, is64 ? TR::InstOpCode::CGIJ : TR::InstOpCode::CIJ, node, rLen, (int8_t) sizeOfVector, doneLabel, TR::InstOpCode::COND_BNH); iComment("len <= 16 ? we're done..") - cursor = generateRILInstruction (cg, is64 ? TR::InstOpCode::ALGFI : TR::InstOpCode::ALFI, node, rResidue, 1); iComment("re-adjust residue for VLL/VSTL (length -> index)"); + // Increment index by the remainder then add 1, since the loadLength contains the highest index, we must go one past that + generateRIEInstruction(cg, TR::InstOpCode::AHIK, node, addressOffset, loadLength, 1); - /* ===================== Step 4.0: Setup for core-loop =====================*/ + generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, fullVectorConversion); - // lazy evaluation done for loop counter since it isn't needed for strlen < 16 - cursor = generateS390LabelInstruction (cg, TR::InstOpCode::LABEL, node, coreLoopSetup); iComment("====== LOOP SETUP ======"); - cursor = generateRSInstruction (cg, TR::InstOpCode::getShiftRightLogicalSingleOpCode(), node, rLen, rLen, 4); iComment("reduce len to multiple of 16 (loop counter)"); + generateRIEInstruction(cg, TR::InstOpCode::CIJ, node, lengthRegister, sizeOfVector, success, TR::InstOpCode::COND_BL); - // We do this so we can move src index and target index together. It also saves the need of an additional register at the cost of an extra cycle. - // The index will begin at 0 and increment in multiples of 16 - cursor = generateRXInstruction (cg, TR::InstOpCode::getLoadAddressOpCode(), node, rSrcVal, generateS390MemoryReference(rSrcVal, rResidue, 0, cg)); iComment("source.val += residue"); - cursor = generateRXInstruction (cg, TR::InstOpCode::getLoadAddressOpCode(), node, rTgtVal, generateS390MemoryReference(rTgtVal, rResidue, 0 ,cg)); iComment("tgt.val += residue"); + // Set the loopCounter to the amount of groups of 16 bytes left, ignoring already accounted for remainder + generateRSInstruction(cg, TR::InstOpCode::SRL, node, loopCounter, loopCounter, 4); - // Start with index=0 into the src array - cursor = generateRRInstruction (cg, TR::InstOpCode::getXORRegOpCode(), node, rSrcValIdx, rSrcValIdx); iComment("reset srx/tgt index"); + generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, loop); - /* ===================== Step 4: Core loop - 16 byte at a time processing =====================*/ + generateVRXInstruction(cg, TR::InstOpCode::VL, node, charBufferVector, generateS390MemoryReference(sourceRegister, addressOffset, headerSize, cg)); - cursor = generateS390LabelInstruction (cg, TR::InstOpCode::LABEL, node, processNext16Bytes); iComment("====== CORE LOOP ======"); - cursor = generateVRXInstruction (cg, TR::InstOpCode::VL, node, vBuf, generateS390MemoryReference(rSrcVal, rSrcValIdx, 0, cg)); - - // Check for invalid characters - if(!isCompressedString) + if (!isCompressedString) { - cursor = generateVRRdInstruction (cg, TR::InstOpCode::VSTRC, node, vTmp, vBuf, vInvalidRange, vInvalidCntrl, 0x1 , elementSizeMask); iComment("check for invalid codepoints"); - cursor = generateS390BranchInstruction (cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_CC1, node, handleInvalidChars); + generateVRRdInstruction(cg, TR::InstOpCode::VSTRC, node, selectionVector, charBufferVector, invalidRangeVector, invalidCondVector, 0x1 , elementSizeMask); + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_CC1, node, handleInvalidChars); } - // Check for alphabets whose case we can convert - cursor = generateVRRdInstruction (cg, TR::InstOpCode::VSTRC, node, vTmp, vBuf, vAlphaRange, vAlphaCntrl, 0x4, elementSizeMask); iComment("search for case to convert"); - cursor = generateVRRcInstruction (cg, isToUpper ? TR::InstOpCode::VS : TR::InstOpCode::VA, node, vCaseBuf, vBuf, vOffset, 0x0, 0x0, elementSizeMask); iComment("offset for case conversion"); - - // Merge the case converted characters with the non-case converted characters and write to output String - cursor = generateVRReInstruction (cg, TR::InstOpCode::VSEL, node, vCaseBuf, vCaseBuf, vBuf, vTmp); iComment("only replace converted chars"); - cursor = generateVRXInstruction (cg, TR::InstOpCode::VST, node, vCaseBuf, generateS390MemoryReference(rTgtVal, rSrcValIdx, 0, cg), 0, cursor); - - // Increment source index by 16, decrement loop counter by 1 and start over if needed - cursor = generateRXInstruction (cg, TR::InstOpCode::getLoadAddressOpCode(), node, rSrcValIdx, generateS390MemoryReference(rSrcValIdx, sizeOfVector, cg), cursor); iComment("src/dst index + = 16"); - cursor = generateS390BranchInstruction (cg, TR::InstOpCode::BRCT, node, rLoopCounter, processNext16Bytes); iComment("iter-- and branch if not 0"); + generateVRRdInstruction(cg, TR::InstOpCode::VSTRC, node, selectionVector, charBufferVector, alphaRangeVector, alphaCondVector, 0x4, elementSizeMask); + generateVRRcInstruction(cg, isToUpper ? TR::InstOpCode::VS : TR::InstOpCode::VA, node, modifiedCaseVector, charBufferVector, charOffsetVector, 0x0, 0x0, elementSizeMask); + generateVRReInstruction(cg, TR::InstOpCode::VSEL, node, modifiedCaseVector, modifiedCaseVector, charBufferVector, selectionVector); - cursor = generateS390BranchInstruction (cg, TR::InstOpCode::BRC , TR::InstOpCode::COND_BRC, node, doneLabel); iComment("skip over invalid char handling"); + generateVRXInstruction(cg, TR::InstOpCode::VST, node, modifiedCaseVector, generateS390MemoryReference(destRegister, addressOffset, headerSize, cg), 0); - /* ===================== Step 5: Invalid codepoint (and discontiguous array) handling - return null reference to Java =====================*/ + generateRXInstruction(cg, TR::InstOpCode::getLoadAddressOpCode(), node, addressOffset, generateS390MemoryReference(addressOffset, sizeOfVector, cg)); + generateS390BranchInstruction(cg, TR::InstOpCode::BRCT, node, loopCounter, loop); - cursor = generateS390LabelInstruction (cg, TR::InstOpCode::LABEL, node, handleInvalidChars); iComment("====== INVALID CHAR HANDLING ======"); - cg->generateDebugCounter(isToUpper? "z13/simd/toUpper/null" : "z13/simd/toLower/null", 1, TR::DebugCounter::Cheap); + cursor = generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, success); - cursor = generateRRInstruction (cg, TR::InstOpCode::getXORRegOpCode(), node, rTgtBase, rTgtBase); + generateRIInstruction(cg, TR::InstOpCode::LHI, node, lengthRegister, 1); + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, node, doneLabel); - /* ===================== Step 6: Done - Perform cleanup =====================*/ + generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, handleInvalidChars); + cg->generateDebugCounter(isToUpper? "z13/simd/toUpper/null" : "z13/simd/toLower/null", 1, TR::DebugCounter::Cheap); + generateRRInstruction(cg, TR::InstOpCode::XR, node, lengthRegister, lengthRegister); - cursor = generateS390LabelInstruction (cg, TR::InstOpCode::LABEL, node, doneLabel); + cursor = generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, doneLabel, regDeps); cursor->setEndInternalControlFlow(); - cursor->setDependencyConditions(regDeps); - - cg->stopUsingRegister(rSrcVal); - cg->stopUsingRegister(rTgtVal); - cg->stopUsingRegister(rSrcValIdx); - - cg->stopUsingRegister(rLen); - cg->stopUsingRegister(rResidue); - cg->stopUsingRegister(vTmp); - cg->stopUsingRegister(vCaseBuf); - cg->stopUsingRegister(vBuf); - cg->stopUsingRegister(vAlphaRange); - cg->stopUsingRegister(vOffset); - cg->stopUsingRegister(vAlphaCntrl); - cg->stopUsingRegister(vInvalidRange); - cg->stopUsingRegister(vInvalidCntrl); + cg->stopUsingRegister(addressOffset); + cg->stopUsingRegister(loadLength); + cg->stopUsingRegister(charBufferVector); + cg->stopUsingRegister(selectionVector); + cg->stopUsingRegister(modifiedCaseVector); + cg->stopUsingRegister(charOffsetVector); + cg->stopUsingRegister(alphaRangeVector); + cg->stopUsingRegister(alphaCondVector); + cg->stopUsingRegister(invalidRangeVector); + cg->stopUsingRegister(invalidCondVector); - node->setRegister(rTgtBase); + node->setRegister(lengthRegister); - cg->decReferenceCount(node->getFirstChild()); - cg->decReferenceCount(node->getSecondChild()); + cg->decReferenceCount(node->getChild(0)); + cg->decReferenceCount(node->getChild(1)); + cg->decReferenceCount(node->getChild(2)); + cg->decReferenceCount(node->getChild(3)); return node->getRegister(); - #undef iComment } extern TR::Register * -inlineToUpper(TR::Node *node, TR::CodeGenerator *cg, bool isCompressedString) +toUpperIntrinsic(TR::Node *node, TR::CodeGenerator *cg, bool isCompressedString) { cg->generateDebugCounter("z13/simd/toUpper", 1, TR::DebugCounter::Free); return caseConversionHelper(node, cg, true, isCompressedString); } extern TR::Register * -inlineToLower(TR::Node *node, TR::CodeGenerator *cg, bool isCompressedString) +toLowerIntrinsic(TR::Node *node, TR::CodeGenerator *cg, bool isCompressedString) { cg->generateDebugCounter("z13/simd/toLower", 1, TR::DebugCounter::Free); return caseConversionHelper(node, cg, false, isCompressedString); diff --git a/test/functional/Java8andUp/playlist.xml b/test/functional/Java8andUp/playlist.xml index 7d03c3b4e43..34ba222d721 100644 --- a/test/functional/Java8andUp/playlist.xml +++ b/test/functional/Java8andUp/playlist.xml @@ -1020,11 +1020,11 @@ - JCL_Test_JIT_Helper_SE80 + JCL_Test_JITHelpers_SE80 $(JAVA_COMMAND) $(JVM_OPTIONS) \ -Xbootclasspath/a:$(Q)$(TEST_RESROOT)$(D)TestResources.jar$(Q) \ -cp $(Q)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \ - org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames JCL_TEST_JIT_Helper \ + org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames JCL_TEST_JITHelpers \ -groups $(TEST_GROUP) \ -excludegroups $(DEFAULT_EXCLUDE); \ $(TEST_STATUS) @@ -1040,11 +1040,11 @@ - JCL_Test_JIT_Helper + JCL_Test_JITHelpers $(JAVA_COMMAND) $(JVM_OPTIONS) --add-exports=java.base/com.ibm.jit=ALL-UNNAMED \ - -Xbootclasspath/a:$(Q)$(TEST_RESROOT)$(D)TestResources.jar$(Q) \ + --add-opens=java.base/com.ibm.jit=ALL-UNNAMED -Xbootclasspath/a:$(Q)$(TEST_RESROOT)$(D)TestResources.jar$(Q) \ -cp $(Q)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)GeneralTest.jar$(Q) \ - org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames JCL_TEST_JIT_Helper \ + org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames JCL_TEST_JITHelpers \ -groups $(TEST_GROUP) \ -excludegroups $(DEFAULT_EXCLUDE); \ $(TEST_STATUS) diff --git a/test/functional/Java8andUp/src/org/openj9/test/com/ibm/jit/Test_JITHelpers.java b/test/functional/Java8andUp/src/org/openj9/test/com/ibm/jit/Test_JITHelpers.java index d12c918e6af..17708ac8390 100644 --- a/test/functional/Java8andUp/src/org/openj9/test/com/ibm/jit/Test_JITHelpers.java +++ b/test/functional/Java8andUp/src/org/openj9/test/com/ibm/jit/Test_JITHelpers.java @@ -24,10 +24,13 @@ import org.testng.annotations.Test; import org.testng.AssertJUnit; +import org.testng.Assert; import java.io.FileInputStream; import java.io.InputStream; import com.ibm.jit.JITHelpers; import org.openj9.test.com.ibm.jit.Test_JITHelpersImpl; +import java.util.Arrays; +import java.lang.reflect.Field; @Test(groups = { "level.sanity" }) @@ -53,4 +56,207 @@ public static void test_getSuperclass() { } + private static final com.ibm.jit.JITHelpers helpers = getJITHelpers(); + private static com.ibm.jit.JITHelpers getJITHelpers() { + + try { + Field f = com.ibm.jit.JITHelpers.class.getDeclaredField("helpers"); + f.setAccessible(true); + return (com.ibm.jit.JITHelpers) f.get(null); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private static int[] edgecaseLengths = new int[]{0, 1, 4, 7, 8, 9, 15, 16, 17, 31, 32, 33}; + + private static char[] lowercaseLatin1Char = {0x6162, 0x6364, 0x6566, 0x6768, 0x696a, 0x6b6c, 0x6d6e, 0x6f70, 0x7172, 0x7374, 0x7576, 0x7778, 0x797a, + (char)0xe0e1, (char)0xe2e3, (char)0xe4e5, (char)0xe600}; + private static char[] uppercaseLatin1Char = {0x4142, 0x4344, 0x4546, 0x4748, 0x494a, 0x4b4c, 0x4d4e, 0x4f50, 0x5152, 0x5354, 0x5556, 0x5758, 0x595a, + (char)0xc0c1, (char)0xc2c3, (char)0xc4c5, (char)0xc600}; + private static char[] lowercaseUTF16Char = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6}; + private static char[] uppercaseUTF16Char = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6}; + + private static byte[] lowercaseLatin1Byte = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76,0x77, 0x78, 0x79, 0x7a, (byte)0xe0, (byte)0xe1, (byte)0xe2, (byte)0xe3, (byte)0xe4, (byte)0xe5, (byte)0xe6}; + private static byte[] uppercaseLatin1Byte = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, + 0x52, 0x53, 0x54, 0x55, 0x56,0x57, 0x58, 0x59, 0x5a, (byte)0xc0, (byte)0xc1, (byte)0xc2, (byte)0xc3, (byte)0xc4, (byte)0xc5,(byte)0xc6}; + private static byte[] lowercaseUTF16BigEndianByte = {0x00, 0x61, 0x0, 0x62, 0x0, 0x63, 0x0, 0x64, 0x0, 0x65, 0x0, 0x66, 0x0, 0x67, 0x0, 0x68, 0x0, + 0x69, 0x0, 0x6a, 0x0, 0x6b, 0x0, 0x6c, 0x0, 0x6d, 0x0, 0x6e, 0x0, 0x6f, 0x0, 0x70, 0x0, 0x71, 0x0, 0x72, 0x0, 0x73, 0x0, 0x74, 0x0, + 0x75, 0x0, 0x76, 0x0, 0x77, 0x0, 0x78, 0x0, 0x79, 0x0, 0x7a, 0x0, (byte)0xe0, 0x0, (byte)0xe1, 0x0, (byte)0xe2, 0x0, (byte)0xe3, + 0x0, (byte)0xe4, 0x0, (byte)0xe5, 0x0, (byte)0xe6}; + private static byte[] uppercaseUTF16BigEndianByte = {0x0, 0x41, 0x0, 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0, 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, + 0x49, 0x0, 0x4a, 0x0, 0x4b, 0x0,0x4c, 0x0, 0x4d, 0x0, 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0, 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0, + 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0, 0x5a, 0x0, (byte)0xc0, 0x0, (byte)0xc1, 0x0, (byte)0xc2, 0x0, (byte)0xc3, 0x0, (byte)0xc4, 0x0, + (byte)0xc5, 0x0, (byte)0xc6}; + private static byte[] lowercaseUTF16LittleEndianByte = {0x61, 0x0, 0x62, 0x0, 0x63, 0x0, 0x64, 0x0, 0x65, 0x0, 0x66, 0x0, 0x67, 0x0, 0x68, 0x0, 0x69, + 0x0, 0x6a, 0x0, 0x6b, 0x0, 0x6c, 0x0, 0x6d, 0x0, 0x6e, 0x0, 0x6f, 0x0, 0x70, 0x0, 0x71, 0x0, 0x72, 0x0, 0x73, 0x0, 0x74, 0x0, 0x75, 0x0, + 0x76, 0x0, 0x77, 0x0, 0x78, 0x0, 0x79, 0x0, 0x7a, 0x0, (byte)0xe0, 0x0, (byte)0xe1, 0x0, (byte)0xe2, 0x0, (byte)0xe3, 0x0, (byte)0xe4, + 0x0, (byte)0xe5, 0x0, (byte)0xe6, 0x0}; + private static byte[] uppercaseUTF16LittleEndianByte = {0x41, 0x0, 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0, 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0, + 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0, 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0, 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0, 0x56, 0x0, + 0x57, 0x0, 0x58, 0x0, 0x59, 0x0, 0x5a, 0x0, (byte)0xc0, 0x0, (byte)0xc1, 0x0, (byte)0xc2, 0x0, (byte)0xc3, 0x0, (byte)0xc4, 0x0, (byte)0xc5, + 0x0, (byte)0xc6, 0x0}; + + /** + * @tests com.ibm.jit.JITHelpers#(char[], char[], int) + */ + public static void test_toUpperIntrinsicUTF16() { + char[] buffer; + + for (int j : edgecaseLengths){ + buffer = new char[j]; + if (helpers.toUpperIntrinsicUTF16(Arrays.copyOfRange(lowercaseUTF16Char, 0, j), buffer, j * 2)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(uppercaseUTF16Char, 0, j)), + "UTF16 JITHelper upper case conversion with char arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } + + /** + * @tests com.ibm.jit.JITHelpers#toUpperIntrinsicUTF16(byte[], byte[], int) + */ + public static void test_toUpperIntrinsicUTF16two() { + byte[] buffer; + + for (int j : edgecaseLengths) { + buffer = new byte[j * 2]; + if (helpers.toUpperIntrinsicUTF16(Arrays.copyOfRange(lowercaseUTF16BigEndianByte, 0, j * 2), buffer, j * 2)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(uppercaseUTF16BigEndianByte, 0, j * 2)), + "UTF16 JITHelper upper case conversion with big endian byte arrays of " + j + " letters failed"); + } + buffer.getClass(); + + if (helpers.toUpperIntrinsicUTF16(Arrays.copyOfRange(lowercaseUTF16LittleEndianByte, 0, j * 2), buffer, j * 2)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(uppercaseUTF16LittleEndianByte, 0, j * 2)), + "UTF16 JITHelper upper case conversion with little endian byte arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } + + /** + * @tests com.ibm.jit.JITHelpers#toLowerIntrinsicUTF16(char[], char[], int) + */ + public static void test_toLowerIntrinsicUTF16() { + char[] buffer; + + for (int j : edgecaseLengths) { + buffer = new char[j]; + if (helpers.toLowerIntrinsicUTF16(Arrays.copyOfRange(uppercaseUTF16Char, 0, j), buffer, j * 2)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(lowercaseUTF16Char, 0, j)), + "UTF16 JITHelper lower case conversion with byte arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } + + /** + * @tests com.ibm.jit.JITHelpers#toLowerIntrinsicUTF16(byte[], byte[], int) + */ + public static void test_toLowerIntrinsicUTF16two() { + byte[] buffer; + + for (int j : edgecaseLengths) { + buffer = new byte[j * 2]; + if (helpers.toLowerIntrinsicUTF16(Arrays.copyOfRange(uppercaseUTF16BigEndianByte, 0, j * 2), buffer, j * 2)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(lowercaseUTF16BigEndianByte, 0, j * 2)), + "UTF16 JITHelper lower case conversion with big endian byte arrays of " + j + " letters failed"); + } + buffer.getClass(); + + if (helpers.toLowerIntrinsicUTF16(Arrays.copyOfRange(uppercaseUTF16LittleEndianByte, 0, j * 2), buffer, j * 2)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(lowercaseUTF16LittleEndianByte, 0, j * 2)), + "UTF16 JITHelper lower case conversion with little endian byte arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } + + /** + * @tests com.ibm.jit.JITHelpers#toLowerIntrinsicLatin1(char[], char[], int) + */ + public static void test_toUpperIntrinsicLatin1() { + char[] buffer; + char[] source, converted; + + for (int j : edgecaseLengths) { + source = Arrays.copyOfRange(lowercaseLatin1Char, 0, (j + 1) / 2); + converted = Arrays.copyOfRange(uppercaseLatin1Char, 0, (j + 1) / 2); + + if (j % 2 == 1) { + source[source.length - 1] &= 0xff00; + converted[source.length - 1] &= 0xff00; + } + + buffer = new char[(j + 1) / 2]; + if (helpers.toUpperIntrinsicLatin1(source, buffer, j)) { + Assert.assertTrue(Arrays.equals(buffer, converted), + "Latin 1 JITHelper upper case conversion with char arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } + + /** + * @tests com.ibm.jit.JITHelpers#toUpperIntrinsicLatin1(byte[], byte[], int) + */ + public static void test_toUpperIntrinsicLatin1two() { + byte[] buffer; + + for (int j : edgecaseLengths) { + buffer = new byte[j]; + if (helpers.toUpperIntrinsicLatin1(Arrays.copyOfRange(lowercaseLatin1Byte, 0, j), buffer, j)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(uppercaseLatin1Byte, 0, j)), + "Latin 1 JITHelper upper case conversion with byte arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } + + /** + * @tests com.ibm.jit.JITHelpers#toLowerIntrinsicLatin1(char[], char[], int) + */ + public static void test_toLowerIntrinsicLatin1() { + char[] buffer; + char[] source, converted; + + for (int j : edgecaseLengths) { + source = Arrays.copyOfRange(uppercaseLatin1Char, 0, (j + 1) / 2); + converted = Arrays.copyOfRange(lowercaseLatin1Char, 0, (j + 1) / 2); + + if (j % 2 == 1) { + source[source.length - 1] &= 0xff00; + converted[source.length - 1] &= 0xff00; + } + + buffer = new char[(j + 1) / 2]; + if (helpers.toLowerIntrinsicLatin1(source, buffer, j)) { + Assert.assertTrue(Arrays.equals(buffer, converted), + "Latin 1 JITHelper lower case conversion with char arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } + + /** + * @tests com.ibm.jit.JITHelpers#toLowerIntrinsicLatin1(byte[], byte[], int) + */ + public static void test_toLowerIntrinsicLatin1two() { + byte[] buffer; + + for (int j : edgecaseLengths) { + buffer = new byte[j]; + if (helpers.toLowerIntrinsicLatin1(Arrays.copyOfRange(uppercaseLatin1Byte, 0, j), buffer, j)) { + Assert.assertTrue(Arrays.equals(buffer, Arrays.copyOfRange(lowercaseLatin1Byte, 0, j)), + "Latin 1 JITHelper lower case conversion with byte arrays of " + j + " letters failed"); + } + buffer.getClass(); + } + } } diff --git a/test/functional/Java8andUp/src/org/openj9/test/java/lang/Test_String.java b/test/functional/Java8andUp/src/org/openj9/test/java/lang/Test_String.java index 25cce889c27..7a700ded33e 100755 --- a/test/functional/Java8andUp/src/org/openj9/test/java/lang/Test_String.java +++ b/test/functional/Java8andUp/src/org/openj9/test/java/lang/Test_String.java @@ -1114,6 +1114,37 @@ public void test_toCharArray() { public void test_toLowerCase() { AssertJUnit.assertTrue("toLowerCase case conversion did not succeed", hwuc.toLowerCase().equals(hwlc)); + // Long strings and strings of lengths near multiples of 16 bytes to test vector HW acceleration edge cases. + Assert.assertEquals("THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG".toLowerCase(), "the quick brown fox jumps over the lazy dog", + "toLowerCase case conversion failed"); + Assert.assertEquals("".toLowerCase(), "", + "toLowerCase case conversion failed"); + Assert.assertEquals("A".toLowerCase(), "a", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCD".toLowerCase(), "abcd", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFG".toLowerCase(), "abcdefg", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGH".toLowerCase(), "abcdefgh", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGHI".toLowerCase(), "abcdefghi", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGHIJKLMNO".toLowerCase(), "abcdefghijklmno", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGHIJKLMNOP".toLowerCase(), "abcdefghijklmnop", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGHIJKLMNOPQ".toLowerCase(), "abcdefghijklmnopq", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C0\u00C1\u00c2\u00C3\u00C4".toLowerCase(), + "abcdefghijklmnopqrstuvwxyz\u00E0\u00E1\u00E2\u00E3\u00E4", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C0\u00C1\u00c2\u00C3\u00C4\u00C5".toLowerCase(), + "abcdefghijklmnopqrstuvwxyz\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5", + "toLowerCase case conversion failed"); + Assert.assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C0\u00C1\u00c2\u00C3\u00C4\u00C5\u00C6".toLowerCase(), + "abcdefghijklmnopqrstuvwxyz\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6", + "toLowerCase case conversion failed"); + /* [PR 111901] toLowerCase(), toUpperCase() should use codePoints */ for (int i = 0x10000; i < 0x10FFFF; i++) { StringBuilder builder = new StringBuilder(); @@ -1377,6 +1408,37 @@ public void test_toUpperCase() { AssertJUnit.assertTrue("Wrong conversion", "\u00df".toUpperCase().equals("SS")); + // Long strings and strings of lengths near multiples of 16 bytes to test vector HW acceleration edge cases. + Assert.assertEquals("the quick brown fox jumps over the lazy dog".toUpperCase(), "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG", + "toUpperCase case conversion failed"); + Assert.assertEquals("".toUpperCase(), "", + "toUpperCase case conversion failed"); + Assert.assertEquals("a".toUpperCase(), "A", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcd".toUpperCase(), "ABCD", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefg".toUpperCase(), "ABCDEFG", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefgh".toUpperCase(), "ABCDEFGH", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefghi".toUpperCase(), "ABCDEFGHI", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefghijklmno".toUpperCase(), "ABCDEFGHIJKLMNO", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefghijklmnop".toUpperCase(), "ABCDEFGHIJKLMNOP", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefghijklmnopq".toUpperCase(), "ABCDEFGHIJKLMNOPQ", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefghijklmnopqrstuvwxyz\u00E0\u00E1\u00E2\u00E3\u00E4".toUpperCase(), + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C0\u00C1\u00c2\u00C3\u00C4", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefghijklmnopqrstuvwxyz\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5".toUpperCase(), + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C0\u00C1\u00c2\u00C3\u00C4\u00C5", + "toUpperCase case conversion failed"); + Assert.assertEquals("abcdefghijklmnopqrstuvwxyz\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6".toUpperCase(), + "ABCDEFGHIJKLMNOPQRSTUVWXYZ\u00C0\u00C1\u00c2\u00C3\u00C4\u00C5\u00C6", + "toUpperCase case conversion failed"); + /* * [PR CMVC 79544 "a\u00df\u1f56".toUpperCase() throws * IndexOutOfBoundsException @@ -1447,7 +1509,6 @@ public void test_toUpperCase3() { } } - /** * @tests java.util.Locale#getLanguage() */ diff --git a/test/functional/Java8andUp/testng.xml b/test/functional/Java8andUp/testng.xml index 4b30ec36cf0..1afd61212e4 100644 --- a/test/functional/Java8andUp/testng.xml +++ b/test/functional/Java8andUp/testng.xml @@ -217,7 +217,7 @@ - +