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 9207116d85b..09cab7e9e18 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,100 +165,6 @@ 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 0bcadcfefa4..7a415e9fa7c 100644 --- a/jcl/src/java.base/share/classes/java/lang/String.java +++ b/jcl/src/java.base/share/classes/java/lang/String.java @@ -71,6 +71,7 @@ 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; /** @@ -877,6 +878,17 @@ 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. * @@ -2697,26 +2709,15 @@ public String toLowerCase(Locale locale) { return this; } - int sLength = lengthInternal(); + if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ + String output = new String(lengthInternal()); + if (toLowerHWOptimized(output)) + return output; + } 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); } } @@ -2754,26 +2755,15 @@ public String toUpperCase(Locale locale) { return this; } - int sLength = lengthInternal(); + if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ + String output = new String(lengthInternal()); + if (toUpperHWOptimized(output)) + return output; + } 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); } } @@ -3821,6 +3811,33 @@ public static String join(CharSequence delimiter, Iterable= 0) && (begin <= end) && (end <= length)) { return; @@ -3929,6 +3946,7 @@ 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; /** @@ -4710,6 +4728,22 @@ 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. * @@ -6711,20 +6745,10 @@ public String toLowerCase(Locale locale) { return this; } - 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); - } - } + if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ + String output = new String(lengthInternal()); + if (toLowerHWOptimized(output)) + return output; } return toLowerCaseCore(language); @@ -7046,20 +7070,10 @@ public String toUpperCase(Locale locale) { return this; } - 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); - } - } + if (StrHWAvailable() && language == "en") { //$NON-NLS-1$ + String output = new String(lengthInternal()); + if (toUpperHWOptimized(output)) + return output; } return toUpperCaseCore(language); @@ -8256,5 +8270,32 @@ 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 b50bd0187da..2aaef930c3a 100644 --- a/runtime/compiler/env/VMJ9.cpp +++ b/runtime/compiler/env/VMJ9.cpp @@ -3085,10 +3085,12 @@ bool TR_J9VMBase::supressInliningRecognizedInitialCallee(TR_CallSite* callsite, dontInlineRecognizedMethod = true; } break; - 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: + 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: if(comp->cg()->getSupportsInlineStringCaseConversion()) { dontInlineRecognizedMethod = true; @@ -3330,10 +3332,12 @@ int TR_J9VMBase::checkInlineableTarget (TR_CallTarget* target, TR_CallSite* call #endif if ( - 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_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::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 0feaea76cb1..3d01dc27be9 100644 --- a/runtime/compiler/env/j9method.cpp +++ b/runtime/compiler/env/j9method.cpp @@ -2972,6 +2972,13 @@ 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")}, @@ -3314,16 +3321,7 @@ 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")}, - {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} + { TR::unknownMethod} }; static X StringUTF16Methods[] = diff --git a/runtime/compiler/ilgen/IlGenerator.cpp b/runtime/compiler/ilgen/IlGenerator.cpp index 067d3b25ab9..501d8d70b06 100644 --- a/runtime/compiler/ilgen/IlGenerator.cpp +++ b/runtime/compiler/ilgen/IlGenerator.cpp @@ -211,9 +211,9 @@ bool TR_J9ByteCodeIlGenerator::internalGenIL() } } - if (recognizedMethod == TR::com_ibm_jit_JITHelpers_supportsIntrinsicCaseConversion && !TR::Compiler->om.canGenerateArraylets()) + if (recognizedMethod == TR::java_lang_String_StrHWAvailable && !TR::Compiler->om.canGenerateArraylets()) { - if (performTransformation(comp(), "O^O IlGenerator: Generate com/ibm/jit/JITHelpers.supportsIntrinsicCaseConversion\n")) + if (performTransformation(comp(), "O^O IlGenerator: Generate java/lang/String.StrHWAvailable\n")) { genHWOptimizedStrProcessingAvailable(); return true; diff --git a/runtime/compiler/ilgen/Walker.cpp b/runtime/compiler/ilgen/Walker.cpp index e5f4f813ba4..8f4a0712737 100644 --- a/runtime/compiler/ilgen/Walker.cpp +++ b/runtime/compiler/ilgen/Walker.cpp @@ -5023,7 +5023,7 @@ break } } - if(symbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_supportsIntrinsicCaseConversion) + if(symbol->getRecognizedMethod() == TR::java_lang_String_StrHWAvailable) { if (cg()->getSupportsInlineStringCaseConversion()) constToLoad = 1; @@ -5035,10 +5035,12 @@ break } if (cg()->getSupportsInlineStringCaseConversion() && - (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)) + (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)) { isDirectCall = true; } diff --git a/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp b/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp index 9e7c47c7a87..54ea18baad7 100644 --- a/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp +++ b/runtime/compiler/optimizer/J9RecognizedCallTransformer.cpp @@ -42,6 +42,98 @@ #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); @@ -64,6 +156,13 @@ 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; } @@ -101,6 +200,14 @@ 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 eb14af497fc..cc2b3cd7ce4 100644 --- a/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp +++ b/runtime/compiler/optimizer/J9RecognizedCallTransformer.hpp @@ -41,6 +41,7 @@ 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 195d8eb16d4..d4bfaadee05 100644 --- a/runtime/compiler/x/codegen/J9TreeEvaluator.cpp +++ b/runtime/compiler/x/codegen/J9TreeEvaluator.cpp @@ -14115,14 +14115,16 @@ J9::X86::TreeEvaluator::directCallEvaluator(TR::Node *node, TR::CodeGenerator *c case TR::java_lang_String_andOR: return TR::TreeEvaluator::andORStringEvaluator(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); + 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); default: break; } @@ -14444,7 +14446,7 @@ class J9::X86::TreeEvaluator::CaseConversionManager { }; TR::Register * -J9::X86::TreeEvaluator::toUpperIntrinsicLatin1Evaluator(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::compressedStringToUpperCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(true /* isCompressedString */, false /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); @@ -14452,21 +14454,21 @@ J9::X86::TreeEvaluator::toUpperIntrinsicLatin1Evaluator(TR::Node *node, TR::Code TR::Register * -J9::X86::TreeEvaluator::toLowerIntrinsicLatin1Evaluator(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::compressedStringToLowerCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(true/* isCompressedString */, true /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); } TR::Register * -J9::X86::TreeEvaluator::toUpperIntrinsicUTF16Evaluator(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::decompressedStringToUpperCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(false /* isCompressedString */, false /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); } TR::Register * -J9::X86::TreeEvaluator::toLowerIntrinsicUTF16Evaluator(TR::Node *node, TR::CodeGenerator *cg) +J9::X86::TreeEvaluator::decompressedStringToLowerCaseEvalutor(TR::Node *node, TR::CodeGenerator *cg) { CaseConversionManager manager(false /* isCompressedString */, true /* toLowerCase */); return TR::TreeEvaluator::stringCaseConversionHelper(node, cg, manager); @@ -14503,16 +14505,17 @@ 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->getChild(1)); + TR::Register *srcArray = cg->evaluate(node->getFirstChild()); deps->addPostCondition(srcArray, TR::RealRegister::NoReg, cg); deps->addPreCondition(srcArray, TR::RealRegister::NoReg, cg); - TR::Register *dstArray = cg->evaluate(node->getChild(2)); + TR::Register *dstArray = cg->evaluate(node->getSecondChild()); deps->addPostCondition(dstArray, TR::RealRegister::NoReg, cg); deps->addPreCondition(dstArray, TR::RealRegister::NoReg, cg); - TR::Register *length = cg->intClobberEvaluate(node->getChild(3)); + TR::Register *length = cg->intClobberEvaluate(node->getThirdChild()); deps->addPostCondition(length, TR::RealRegister::NoReg, cg); deps->addPreCondition(length, TR::RealRegister::NoReg, cg); @@ -14532,8 +14535,7 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener TR_Debug *debug = cg->getDebug(); TR::Instruction * cursor = NULL; - uint32_t strideSize = 16; - uintptrj_t headerSize = TR::Compiler->om.contiguousArrayHeaderSizeInBytes(); + uint32_t strideSize = 16; static uint16_t MINUS1[] = { @@ -14568,7 +14570,11 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener generateRegRegInstruction(MOVRegReg(), node, result, dstArray, cg); - // initialize the loop counter + // # 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 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 @@ -14590,7 +14596,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, headerSize, cg); + auto srcArrayMemRef = generateX86MemoryReference(srcArray, counter, 0, 0, cg); generateRegMemInstruction(MOVDQURegMem, node, xmmRegArrayContentCopy0, srcArrayMemRef, cg); //detect invalid charactors @@ -14622,7 +14628,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, headerSize, cg); + auto dstArrayMemRef = generateX86MemoryReference(dstArray, counter, 0, 0, cg); generateMemRegInstruction(MOVDQUMemReg, node, dstArrayMemRef, xmmRegArrayContentCopy2, cg); generateRegImmInstruction(ADDRegImms(), node, counter, strideSize, cg); generateLabelInstruction(JMP4, node, caseConversionMainLoopLabel, cg); @@ -14631,7 +14637,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, headerSize, cg); + srcArrayMemRef = generateX86MemoryReference(srcArray, counter, 0, 0, cg); generateRegMemInstruction( manager.isCompressedString()? MOVZXReg4Mem1: MOVZXReg4Mem2, node, singleChar, srcArrayMemRef, cg); // use unsigned compare to detect invalid range @@ -14655,7 +14661,7 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener generateLabelInstruction(LABEL, node, storeToArrayLabel, cg); - dstArrayMemRef = generateX86MemoryReference(dstArray, counter, 0, headerSize, cg); + dstArrayMemRef = generateX86MemoryReference(dstArray, counter, 0, 0, cg); generateMemRegInstruction(manager.isCompressedString()? S1MemReg: S2MemReg, node, dstArrayMemRef, singleChar, cg); generateRegImmInstruction(ADDRegImms(), node, counter, manager.isCompressedString()? 1: 2, cg); generateLabelInstruction(JMP4, node, residueStartLabel, cg); @@ -14681,10 +14687,9 @@ J9::X86::TreeEvaluator::stringCaseConversionHelper(TR::Node *node, TR::CodeGener cg->stopUsingRegister(xmmRegArrayContentCopy2); - cg->decReferenceCount(node->getChild(0)); - cg->decReferenceCount(node->getChild(1)); - cg->decReferenceCount(node->getChild(2)); - cg->decReferenceCount(node->getChild(3)); + cg->decReferenceCount(node->getFirstChild()); + cg->decReferenceCount(node->getSecondChild()); + cg->decReferenceCount(node->getThirdChild()); return result; } diff --git a/runtime/compiler/x/codegen/J9TreeEvaluator.hpp b/runtime/compiler/x/codegen/J9TreeEvaluator.hpp index 2cd85de5c3f..a0a16fb6d9a 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 *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); + 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); 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 c3fe6722dea..78762a69002 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 *toUpperIntrinsic(TR::Node * node, TR::CodeGenerator * cg, bool isCompressedString); -extern TR::Register *toLowerIntrinsic(TR::Node * node, TR::CodeGenerator * cg, bool isCompressedString); +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 *inlineDoubleMax(TR::Node *node, TR::CodeGenerator *cg); extern TR::Register *inlineDoubleMin(TR::Node *node, TR::CodeGenerator *cg); @@ -4204,17 +4204,19 @@ J9::Z::CodeGenerator::inlineDirectCall( { switch (methodSymbol->getRecognizedMethod()) { - case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicUTF16: - resultReg = toUpperIntrinsic(node, cg, false); + case TR::java_lang_String_toUpperHWOptimized: + case TR::java_lang_String_toUpperHWOptimizedDecompressed: + resultReg = inlineToUpper(node, cg, false); return true; - case TR::com_ibm_jit_JITHelpers_toUpperIntrinsicLatin1: - resultReg = toUpperIntrinsic(node, cg, true); + case TR::java_lang_String_toUpperHWOptimizedCompressed: + resultReg = inlineToUpper(node, cg, true); return true; - case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicUTF16: - resultReg = toLowerIntrinsic(node, cg, false); + case TR::java_lang_String_toLowerHWOptimized: + case TR::java_lang_String_toLowerHWOptimizedDecompressed: + resultReg = inlineToLower(node, cg, false); return true; - case TR::com_ibm_jit_JITHelpers_toLowerIntrinsicLatin1: - resultReg = toLowerIntrinsic(node, cg, true); + case TR::java_lang_String_toLowerHWOptimizedCompressed: + resultReg = inlineToLower(node, cg, true); return true; default: break; diff --git a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp index 011ad8266d5..4f428e0ca33 100644 --- a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp +++ b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp @@ -211,249 +211,405 @@ doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp) return node->getRegister(); } - /** \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) + +/** + * 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) { - 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); + #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 { - 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); + stringValOffset = cg->fej9()->getInstanceFieldOffsetIncludingHeader("Ljava/lang/String;", "value", "[C", comp->getCurrentMethod()); } - // 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) + 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) { - if (isCompressedString) - { - generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0xdfdf, 0x0); - generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidRangeVector, 0xb5b5, 0x1); + cursor = generateRXInstruction (cg, TR::InstOpCode::LLGF, node, rSrcVal, generateS390MemoryReference(rSrcBase, stringValOffset, cg)); + if (shiftAmount != 0 ) generateRSInstruction (cg, TR::InstOpCode::SLLG, node, rSrcVal, rSrcVal, shiftAmount); - 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 = 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"); - 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); - } + // 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); + } + 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); + } + + // 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) + { + // 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); + + + 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); } - else if (!isToUpper && !isCompressedString) + else if(!isToUpper && !isCompressedString) { - // 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); + // 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"); + - generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x2000, 0x0); - generateVRIaInstruction(cg, TR::InstOpCode::VLEIH, node, invalidCondVector, 0x2000, 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"); } + 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(); - // Constant value of 0x20, used to convert between upper and lower - generateVRIaInstruction(cg, TR::InstOpCode::VREPI, node, charOffsetVector, static_cast(0x20), elementSizeMask); + // 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)"); - 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); + /* ===================== Step 3: Residue processing =====================*/ - cursor->setStartInternalControlFlow(); - // VLL and VSTL take an index, not a count, so subtract the count by 1 - generateRILInstruction(cg, TR::InstOpCode::SLFI, node, loadLength, 1); + // 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 - generateVRSbInstruction(cg, TR::InstOpCode::VLL, node, charBufferVector, loadLength, generateS390MemoryReference(sourceRegister, addressOffset, headerSize, cg)); + // Process residue + cursor = generateVRSbInstruction (cg, TR::InstOpCode::VLL, node, vBuf, rResidue, generateS390MemoryReference(rSrcVal, 0, cg)); iComment("====== PROCESS RESIDUE ======"); - // Check for invalid characters, go to fallback individual character conversion implementation - if (!isCompressedString) + // Check for invalid characters + if(!isCompressedString) { - generateVRRdInstruction(cg, TR::InstOpCode::VSTRC, node, selectionVector, charBufferVector, invalidRangeVector, invalidCondVector, 0x1 , elementSizeMask); - generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_CC1, node, handleInvalidChars); + 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, 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); + // 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"); - generateVRSbInstruction(cg, TR::InstOpCode::VSTL, node, modifiedCaseVector, loadLength, generateS390MemoryReference(destRegister, addressOffset, headerSize, cg), 0); + // 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); - // 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); + // 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)"); - generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, fullVectorConversion); + /* ===================== Step 4.0: Setup for core-loop =====================*/ - generateRIEInstruction(cg, TR::InstOpCode::CIJ, node, lengthRegister, sizeOfVector, success, TR::InstOpCode::COND_BL); + // 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)"); - // 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); + // 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"); - generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, loop); + // Start with index=0 into the src array + cursor = generateRRInstruction (cg, TR::InstOpCode::getXORRegOpCode(), node, rSrcValIdx, rSrcValIdx); iComment("reset srx/tgt index"); - generateVRXInstruction(cg, TR::InstOpCode::VL, node, charBufferVector, generateS390MemoryReference(sourceRegister, addressOffset, headerSize, cg)); + /* ===================== Step 4: Core loop - 16 byte at a time processing =====================*/ - if (!isCompressedString) + 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) { - generateVRRdInstruction(cg, TR::InstOpCode::VSTRC, node, selectionVector, charBufferVector, invalidRangeVector, invalidCondVector, 0x1 , elementSizeMask); - generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_CC1, node, handleInvalidChars); + 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, 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); + // 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"); - generateVRXInstruction(cg, TR::InstOpCode::VST, node, modifiedCaseVector, generateS390MemoryReference(destRegister, addressOffset, headerSize, cg), 0); + // 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); - generateRXInstruction(cg, TR::InstOpCode::getLoadAddressOpCode(), node, addressOffset, generateS390MemoryReference(addressOffset, sizeOfVector, cg)); - generateS390BranchInstruction(cg, TR::InstOpCode::BRCT, node, loopCounter, loop); + // 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"); - cursor = generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, node, success); + cursor = generateS390BranchInstruction (cg, TR::InstOpCode::BRC , TR::InstOpCode::COND_BRC, node, doneLabel); iComment("skip over invalid char handling"); - generateRIInstruction(cg, TR::InstOpCode::LHI, node, lengthRegister, 1); + /* ===================== Step 5: Invalid codepoint (and discontiguous array) handling - return null reference to Java =====================*/ - generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, node, doneLabel); + 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 = generateRRInstruction (cg, TR::InstOpCode::getXORRegOpCode(), node, rTgtBase, rTgtBase); - 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, regDeps); + /* ===================== Step 6: Done - Perform cleanup =====================*/ + + cursor = generateS390LabelInstruction (cg, TR::InstOpCode::LABEL, node, doneLabel); cursor->setEndInternalControlFlow(); + cursor->setDependencyConditions(regDeps); - cg->stopUsingRegister(addressOffset); - cg->stopUsingRegister(loadLength); + cg->stopUsingRegister(rSrcVal); + cg->stopUsingRegister(rTgtVal); + cg->stopUsingRegister(rSrcValIdx); - cg->stopUsingRegister(charBufferVector); - cg->stopUsingRegister(selectionVector); - cg->stopUsingRegister(modifiedCaseVector); - cg->stopUsingRegister(charOffsetVector); - cg->stopUsingRegister(alphaRangeVector); - cg->stopUsingRegister(alphaCondVector); - cg->stopUsingRegister(invalidRangeVector); - cg->stopUsingRegister(invalidCondVector); + cg->stopUsingRegister(rLen); + cg->stopUsingRegister(rResidue); - node->setRegister(lengthRegister); + 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->decReferenceCount(node->getChild(0)); - cg->decReferenceCount(node->getChild(1)); - cg->decReferenceCount(node->getChild(2)); - cg->decReferenceCount(node->getChild(3)); + + node->setRegister(rTgtBase); + + cg->decReferenceCount(node->getFirstChild()); + cg->decReferenceCount(node->getSecondChild()); return node->getRegister(); + #undef iComment } extern TR::Register * -toUpperIntrinsic(TR::Node *node, TR::CodeGenerator *cg, bool isCompressedString) +inlineToUpper(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 * -toLowerIntrinsic(TR::Node *node, TR::CodeGenerator *cg, bool isCompressedString) +inlineToLower(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 34ba222d721..7d03c3b4e43 100644 --- a/test/functional/Java8andUp/playlist.xml +++ b/test/functional/Java8andUp/playlist.xml @@ -1020,11 +1020,11 @@ - JCL_Test_JITHelpers_SE80 + JCL_Test_JIT_Helper_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_JITHelpers \ + org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames JCL_TEST_JIT_Helper \ -groups $(TEST_GROUP) \ -excludegroups $(DEFAULT_EXCLUDE); \ $(TEST_STATUS) @@ -1040,11 +1040,11 @@ - JCL_Test_JITHelpers + JCL_Test_JIT_Helper $(JAVA_COMMAND) $(JVM_OPTIONS) --add-exports=java.base/com.ibm.jit=ALL-UNNAMED \ - --add-opens=java.base/com.ibm.jit=ALL-UNNAMED -Xbootclasspath/a:$(Q)$(TEST_RESROOT)$(D)TestResources.jar$(Q) \ + -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_JITHelpers \ + org.testng.TestNG -d $(REPORTDIR) $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -testnames JCL_TEST_JIT_Helper \ -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 17708ac8390..d12c918e6af 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,13 +24,10 @@ 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" }) @@ -56,207 +53,4 @@ 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 7a700ded33e..25cce889c27 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,37 +1114,6 @@ 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(); @@ -1408,37 +1377,6 @@ 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 @@ -1509,6 +1447,7 @@ public void test_toUpperCase3() { } } + /** * @tests java.util.Locale#getLanguage() */ diff --git a/test/functional/Java8andUp/testng.xml b/test/functional/Java8andUp/testng.xml index 1afd61212e4..4b30ec36cf0 100644 --- a/test/functional/Java8andUp/testng.xml +++ b/test/functional/Java8andUp/testng.xml @@ -217,7 +217,7 @@ - +