| /* |
| * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #include "asm/assembler.hpp" |
| #include "asm/assembler.inline.hpp" |
| #include "opto/c2_MacroAssembler.hpp" |
| #include "opto/intrinsicnode.hpp" |
| #include "runtime/vm_version.hpp" |
| |
| #ifdef PRODUCT |
| #define BLOCK_COMMENT(str) // nothing |
| #else |
| #define BLOCK_COMMENT(str) block_comment(str) |
| #endif |
| #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") |
| |
| |
| void C2_MacroAssembler::fast_lock_lightweight(ConditionRegister flag, Register obj, Register box, |
| Register tmp1, Register tmp2, Register tmp3) { |
| compiler_fast_lock_lightweight_object(flag, obj, box, tmp1, tmp2, tmp3); |
| } |
| |
| void C2_MacroAssembler::fast_unlock_lightweight(ConditionRegister flag, Register obj, Register box, |
| Register tmp1, Register tmp2, Register tmp3) { |
| compiler_fast_unlock_lightweight_object(flag, obj, box, tmp1, tmp2, tmp3); |
| } |
| |
| void C2_MacroAssembler::load_narrow_klass_compact_c2(Register dst, Register obj, int disp) { |
| // Note: Don't clobber obj anywhere in that method! |
| |
| // The incoming address is pointing into obj-start + klass_offset_in_bytes. We need to extract |
| // obj-start, so that we can load from the object's mark-word instead. |
| ld(dst, disp - oopDesc::klass_offset_in_bytes(), obj); |
| srdi(dst, dst, markWord::klass_shift); |
| } |
| |
| // Intrinsics for CompactStrings |
| |
| // Compress char[] to byte[] by compressing 16 bytes at once. |
| void C2_MacroAssembler::string_compress_16(Register src, Register dst, Register cnt, |
| Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, |
| Label& Lfailure, bool ascii) { |
| |
| const Register tmp0 = R0; |
| const int byte_mask = ascii ? 0x7F : 0xFF; |
| assert_different_registers(src, dst, cnt, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5); |
| Label Lloop, Lslow; |
| |
| // Check if cnt >= 8 (= 16 bytes) |
| lis(tmp1, byte_mask); // tmp1 = 0x00FF00FF00FF00FF (non ascii case) |
| srwi_(tmp2, cnt, 3); |
| beq(CR0, Lslow); |
| ori(tmp1, tmp1, byte_mask); |
| rldimi(tmp1, tmp1, 32, 0); |
| mtctr(tmp2); |
| |
| // 2x unrolled loop |
| bind(Lloop); |
| ld(tmp2, 0, src); // _0_1_2_3 (Big Endian) |
| ld(tmp4, 8, src); // _4_5_6_7 |
| |
| orr(tmp0, tmp2, tmp4); |
| rldicl(tmp3, tmp2, 6*8, 64-24); // _____1_2 |
| rldimi(tmp2, tmp2, 2*8, 2*8); // _0_2_3_3 |
| rldicl(tmp5, tmp4, 6*8, 64-24); // _____5_6 |
| rldimi(tmp4, tmp4, 2*8, 2*8); // _4_6_7_7 |
| |
| andc_(tmp0, tmp0, tmp1); |
| bne(CR0, Lfailure); // Not latin1/ascii. |
| addi(src, src, 16); |
| |
| rlwimi(tmp3, tmp2, 0*8, 24, 31);// _____1_3 |
| srdi(tmp2, tmp2, 3*8); // ____0_2_ |
| rlwimi(tmp5, tmp4, 0*8, 24, 31);// _____5_7 |
| srdi(tmp4, tmp4, 3*8); // ____4_6_ |
| |
| orr(tmp2, tmp2, tmp3); // ____0123 |
| orr(tmp4, tmp4, tmp5); // ____4567 |
| |
| stw(tmp2, 0, dst); |
| stw(tmp4, 4, dst); |
| addi(dst, dst, 8); |
| bdnz(Lloop); |
| |
| bind(Lslow); // Fallback to slow version |
| } |
| |
| // Compress char[] to byte[]. cnt must be positive int. |
| void C2_MacroAssembler::string_compress(Register src, Register dst, Register cnt, Register tmp, |
| Label& Lfailure, bool ascii) { |
| const int byte_mask = ascii ? 0x7F : 0xFF; |
| Label Lloop; |
| mtctr(cnt); |
| |
| bind(Lloop); |
| lhz(tmp, 0, src); |
| cmplwi(CR0, tmp, byte_mask); |
| bgt(CR0, Lfailure); // Not latin1/ascii. |
| addi(src, src, 2); |
| stb(tmp, 0, dst); |
| addi(dst, dst, 1); |
| bdnz(Lloop); |
| } |
| |
| void C2_MacroAssembler::encode_iso_array(Register src, Register dst, Register len, |
| Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, |
| Register result, bool ascii) { |
| Label Lslow, Lfailure1, Lfailure2, Ldone; |
| |
| string_compress_16(src, dst, len, tmp1, tmp2, tmp3, tmp4, tmp5, Lfailure1, ascii); |
| rldicl_(result, len, 0, 64-3); // Remaining characters. |
| beq(CR0, Ldone); |
| bind(Lslow); |
| string_compress(src, dst, result, tmp2, Lfailure2, ascii); |
| li(result, 0); |
| b(Ldone); |
| |
| bind(Lfailure1); |
| mr(result, len); |
| mfctr(tmp1); |
| rldimi_(result, tmp1, 3, 0); // Remaining characters. |
| beq(CR0, Ldone); |
| b(Lslow); |
| |
| bind(Lfailure2); |
| mfctr(result); // Remaining characters. |
| |
| bind(Ldone); |
| subf(result, result, len); |
| } |
| |
| // Inflate byte[] to char[] by inflating 16 bytes at once. |
| void C2_MacroAssembler::string_inflate_16(Register src, Register dst, Register cnt, |
| Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5) { |
| const Register tmp0 = R0; |
| assert_different_registers(src, dst, cnt, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5); |
| Label Lloop, Lslow; |
| |
| // Check if cnt >= 8 |
| srwi_(tmp2, cnt, 3); |
| beq(CR0, Lslow); |
| lis(tmp1, 0xFF); // tmp1 = 0x00FF00FF |
| ori(tmp1, tmp1, 0xFF); |
| mtctr(tmp2); |
| |
| // 2x unrolled loop |
| bind(Lloop); |
| lwz(tmp2, 0, src); // ____0123 (Big Endian) |
| lwz(tmp4, 4, src); // ____4567 |
| addi(src, src, 8); |
| |
| rldicl(tmp3, tmp2, 7*8, 64-8); // _______2 |
| rlwimi(tmp2, tmp2, 3*8, 16, 23);// ____0113 |
| rldicl(tmp5, tmp4, 7*8, 64-8); // _______6 |
| rlwimi(tmp4, tmp4, 3*8, 16, 23);// ____4557 |
| |
| andc(tmp0, tmp2, tmp1); // ____0_1_ |
| rlwimi(tmp2, tmp3, 2*8, 0, 23); // _____2_3 |
| andc(tmp3, tmp4, tmp1); // ____4_5_ |
| rlwimi(tmp4, tmp5, 2*8, 0, 23); // _____6_7 |
| |
| rldimi(tmp2, tmp0, 3*8, 0*8); // _0_1_2_3 |
| rldimi(tmp4, tmp3, 3*8, 0*8); // _4_5_6_7 |
| |
| std(tmp2, 0, dst); |
| std(tmp4, 8, dst); |
| addi(dst, dst, 16); |
| bdnz(Lloop); |
| |
| bind(Lslow); // Fallback to slow version |
| } |
| |
| // Inflate byte[] to char[]. cnt must be positive int. |
| void C2_MacroAssembler::string_inflate(Register src, Register dst, Register cnt, Register tmp) { |
| Label Lloop; |
| mtctr(cnt); |
| |
| bind(Lloop); |
| lbz(tmp, 0, src); |
| addi(src, src, 1); |
| sth(tmp, 0, dst); |
| addi(dst, dst, 2); |
| bdnz(Lloop); |
| } |
| |
| void C2_MacroAssembler::string_compare(Register str1, Register str2, |
| Register cnt1, Register cnt2, |
| Register tmp1, Register result, int ae) { |
| const Register tmp0 = R0, |
| diff = tmp1; |
| |
| assert_different_registers(str1, str2, cnt1, cnt2, tmp0, tmp1, result); |
| Label Ldone, Lslow, Lloop, Lreturn_diff; |
| |
| // Note: Making use of the fact that compareTo(a, b) == -compareTo(b, a) |
| // we interchange str1 and str2 in the UL case and negate the result. |
| // Like this, str1 is always latin1 encoded, except for the UU case. |
| // In addition, we need 0 (or sign which is 0) extend. |
| |
| if (ae == StrIntrinsicNode::UU) { |
| srwi(cnt1, cnt1, 1); |
| } else { |
| clrldi(cnt1, cnt1, 32); |
| } |
| |
| if (ae != StrIntrinsicNode::LL) { |
| srwi(cnt2, cnt2, 1); |
| } else { |
| clrldi(cnt2, cnt2, 32); |
| } |
| |
| // See if the lengths are different, and calculate min in cnt1. |
| // Save diff in case we need it for a tie-breaker. |
| subf_(diff, cnt2, cnt1); // diff = cnt1 - cnt2 |
| // if (diff > 0) { cnt1 = cnt2; } |
| isel(cnt1, CR0, Assembler::greater, /*invert*/ false, cnt2); |
| |
| // Rename registers |
| Register chr1 = result; |
| Register chr2 = tmp0; |
| |
| // Compare multiple characters in fast loop (only implemented for same encoding). |
| int stride1 = 8, stride2 = 8; |
| if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) { |
| int log2_chars_per_iter = (ae == StrIntrinsicNode::LL) ? 3 : 2; |
| Label Lfastloop, Lskipfast; |
| |
| srwi_(tmp0, cnt1, log2_chars_per_iter); |
| beq(CR0, Lskipfast); |
| rldicl(cnt2, cnt1, 0, 64 - log2_chars_per_iter); // Remaining characters. |
| li(cnt1, 1 << log2_chars_per_iter); // Initialize for failure case: Rescan characters from current iteration. |
| mtctr(tmp0); |
| |
| bind(Lfastloop); |
| ld(chr1, 0, str1); |
| ld(chr2, 0, str2); |
| cmpd(CR0, chr1, chr2); |
| bne(CR0, Lslow); |
| addi(str1, str1, stride1); |
| addi(str2, str2, stride2); |
| bdnz(Lfastloop); |
| mr(cnt1, cnt2); // Remaining characters. |
| bind(Lskipfast); |
| } |
| |
| // Loop which searches the first difference character by character. |
| cmpwi(CR0, cnt1, 0); |
| beq(CR0, Lreturn_diff); |
| bind(Lslow); |
| mtctr(cnt1); |
| |
| switch (ae) { |
| case StrIntrinsicNode::LL: stride1 = 1; stride2 = 1; break; |
| case StrIntrinsicNode::UL: // fallthru (see comment above) |
| case StrIntrinsicNode::LU: stride1 = 1; stride2 = 2; break; |
| case StrIntrinsicNode::UU: stride1 = 2; stride2 = 2; break; |
| default: ShouldNotReachHere(); break; |
| } |
| |
| bind(Lloop); |
| if (stride1 == 1) { lbz(chr1, 0, str1); } else { lhz(chr1, 0, str1); } |
| if (stride2 == 1) { lbz(chr2, 0, str2); } else { lhz(chr2, 0, str2); } |
| subf_(result, chr2, chr1); // result = chr1 - chr2 |
| bne(CR0, Ldone); |
| addi(str1, str1, stride1); |
| addi(str2, str2, stride2); |
| bdnz(Lloop); |
| |
| // If strings are equal up to min length, return the length difference. |
| bind(Lreturn_diff); |
| mr(result, diff); |
| |
| // Otherwise, return the difference between the first mismatched chars. |
| bind(Ldone); |
| if (ae == StrIntrinsicNode::UL) { |
| neg(result, result); // Negate result (see note above). |
| } |
| } |
| |
| void C2_MacroAssembler::array_equals(bool is_array_equ, Register ary1, Register ary2, |
| Register limit, Register tmp1, Register result, bool is_byte) { |
| const Register tmp0 = R0; |
| assert_different_registers(ary1, ary2, limit, tmp0, tmp1, result); |
| Label Ldone, Lskiploop, Lloop, Lfastloop, Lskipfast; |
| bool limit_needs_shift = false; |
| |
| if (is_array_equ) { |
| const int length_offset = arrayOopDesc::length_offset_in_bytes(); |
| const int base_offset = arrayOopDesc::base_offset_in_bytes(is_byte ? T_BYTE : T_CHAR); |
| |
| // Return true if the same array. |
| cmpd(CR0, ary1, ary2); |
| beq(CR0, Lskiploop); |
| |
| // Return false if one of them is null. |
| cmpdi(CR0, ary1, 0); |
| cmpdi(CR1, ary2, 0); |
| li(result, 0); |
| cror(CR0, Assembler::equal, CR1, Assembler::equal); |
| beq(CR0, Ldone); |
| |
| // Load the lengths of arrays. |
| lwz(limit, length_offset, ary1); |
| lwz(tmp0, length_offset, ary2); |
| |
| // Return false if the two arrays are not equal length. |
| cmpw(CR0, limit, tmp0); |
| bne(CR0, Ldone); |
| |
| // Load array addresses. |
| addi(ary1, ary1, base_offset); |
| addi(ary2, ary2, base_offset); |
| } else { |
| limit_needs_shift = !is_byte; |
| li(result, 0); // Assume not equal. |
| } |
| |
| // Rename registers |
| Register chr1 = tmp0; |
| Register chr2 = tmp1; |
| |
| // Compare 8 bytes per iteration in fast loop. |
| const int log2_chars_per_iter = is_byte ? 3 : 2; |
| |
| srwi_(tmp0, limit, log2_chars_per_iter + (limit_needs_shift ? 1 : 0)); |
| beq(CR0, Lskipfast); |
| mtctr(tmp0); |
| |
| bind(Lfastloop); |
| ld(chr1, 0, ary1); |
| ld(chr2, 0, ary2); |
| addi(ary1, ary1, 8); |
| addi(ary2, ary2, 8); |
| cmpd(CR0, chr1, chr2); |
| bne(CR0, Ldone); |
| bdnz(Lfastloop); |
| |
| bind(Lskipfast); |
| rldicl_(limit, limit, limit_needs_shift ? 64 - 1 : 0, 64 - log2_chars_per_iter); // Remaining characters. |
| beq(CR0, Lskiploop); |
| mtctr(limit); |
| |
| // Character by character. |
| bind(Lloop); |
| if (is_byte) { |
| lbz(chr1, 0, ary1); |
| lbz(chr2, 0, ary2); |
| addi(ary1, ary1, 1); |
| addi(ary2, ary2, 1); |
| } else { |
| lhz(chr1, 0, ary1); |
| lhz(chr2, 0, ary2); |
| addi(ary1, ary1, 2); |
| addi(ary2, ary2, 2); |
| } |
| cmpw(CR0, chr1, chr2); |
| bne(CR0, Ldone); |
| bdnz(Lloop); |
| |
| bind(Lskiploop); |
| li(result, 1); // All characters are equal. |
| bind(Ldone); |
| } |
| |
| void C2_MacroAssembler::string_indexof(Register result, Register haystack, Register haycnt, |
| Register needle, ciTypeArray* needle_values, Register needlecnt, int needlecntval, |
| Register tmp1, Register tmp2, Register tmp3, Register tmp4, int ae) { |
| |
| // Ensure 0<needlecnt<=haycnt in ideal graph as prerequisite! |
| Label L_TooShort, L_Found, L_NotFound, L_End; |
| Register last_addr = haycnt, // Kill haycnt at the beginning. |
| addr = tmp1, |
| n_start = tmp2, |
| ch1 = tmp3, |
| ch2 = R0; |
| |
| assert(ae != StrIntrinsicNode::LU, "Invalid encoding"); |
| const int h_csize = (ae == StrIntrinsicNode::LL) ? 1 : 2; |
| const int n_csize = (ae == StrIntrinsicNode::UU) ? 2 : 1; |
| |
| // ************************************************************************************************** |
| // Prepare for main loop: optimized for needle count >=2, bail out otherwise. |
| // ************************************************************************************************** |
| |
| // Compute last haystack addr to use if no match gets found. |
| clrldi(haycnt, haycnt, 32); // Ensure positive int is valid as 64 bit value. |
| addi(addr, haystack, -h_csize); // Accesses use pre-increment. |
| if (needlecntval == 0) { // variable needlecnt |
| cmpwi(CR6, needlecnt, 2); |
| clrldi(needlecnt, needlecnt, 32); // Ensure positive int is valid as 64 bit value. |
| blt(CR6, L_TooShort); // Variable needlecnt: handle short needle separately. |
| } |
| |
| if (n_csize == 2) { lwz(n_start, 0, needle); } else { lhz(n_start, 0, needle); } // Load first 2 characters of needle. |
| |
| if (needlecntval == 0) { // variable needlecnt |
| subf(ch1, needlecnt, haycnt); // Last character index to compare is haycnt-needlecnt. |
| addi(needlecnt, needlecnt, -2); // Rest of needle. |
| } else { // constant needlecnt |
| guarantee(needlecntval != 1, "IndexOf with single-character needle must be handled separately"); |
| assert((needlecntval & 0x7fff) == needlecntval, "wrong immediate"); |
| addi(ch1, haycnt, -needlecntval); // Last character index to compare is haycnt-needlecnt. |
| if (needlecntval > 3) { li(needlecnt, needlecntval - 2); } // Rest of needle. |
| } |
| |
| if (h_csize == 2) { slwi(ch1, ch1, 1); } // Scale to number of bytes. |
| |
| if (ae ==StrIntrinsicNode::UL) { |
| srwi(tmp4, n_start, 1*8); // ___0 |
| rlwimi(n_start, tmp4, 2*8, 0, 23); // _0_1 |
| } |
| |
| add(last_addr, haystack, ch1); // Point to last address to compare (haystack+2*(haycnt-needlecnt)). |
| |
| // Main Loop (now we have at least 2 characters). |
| Label L_OuterLoop, L_InnerLoop, L_FinalCheck, L_Comp1, L_Comp2; |
| bind(L_OuterLoop); // Search for 1st 2 characters. |
| Register addr_diff = tmp4; |
| subf(addr_diff, addr, last_addr); // Difference between already checked address and last address to check. |
| addi(addr, addr, h_csize); // This is the new address we want to use for comparing. |
| srdi_(ch2, addr_diff, h_csize); |
| beq(CR0, L_FinalCheck); // 2 characters left? |
| mtctr(ch2); // num of characters / 2 |
| bind(L_InnerLoop); // Main work horse (2x unrolled search loop) |
| if (h_csize == 2) { // Load 2 characters of haystack (ignore alignment). |
| lwz(ch1, 0, addr); |
| lwz(ch2, 2, addr); |
| } else { |
| lhz(ch1, 0, addr); |
| lhz(ch2, 1, addr); |
| } |
| cmpw(CR0, ch1, n_start); // Compare 2 characters (1 would be sufficient but try to reduce branches to CompLoop). |
| cmpw(CR1, ch2, n_start); |
| beq(CR0, L_Comp1); // Did we find the needle start? |
| beq(CR1, L_Comp2); |
| addi(addr, addr, 2 * h_csize); |
| bdnz(L_InnerLoop); |
| bind(L_FinalCheck); |
| andi_(addr_diff, addr_diff, h_csize); // Remaining characters not covered by InnerLoop: (num of characters) & 1. |
| beq(CR0, L_NotFound); |
| if (h_csize == 2) { lwz(ch1, 0, addr); } else { lhz(ch1, 0, addr); } // One position left at which we have to compare. |
| cmpw(CR1, ch1, n_start); |
| beq(CR1, L_Comp1); |
| bind(L_NotFound); |
| li(result, -1); // not found |
| b(L_End); |
| |
| // ************************************************************************************************** |
| // Special Case: unfortunately, the variable needle case can be called with needlecnt<2 |
| // ************************************************************************************************** |
| if (needlecntval == 0) { // We have to handle these cases separately. |
| Label L_OneCharLoop; |
| bind(L_TooShort); |
| mtctr(haycnt); |
| if (n_csize == 2) { lhz(n_start, 0, needle); } else { lbz(n_start, 0, needle); } // First character of needle |
| bind(L_OneCharLoop); |
| if (h_csize == 2) { lhzu(ch1, 2, addr); } else { lbzu(ch1, 1, addr); } |
| cmpw(CR1, ch1, n_start); |
| beq(CR1, L_Found); // Did we find the one character needle? |
| bdnz(L_OneCharLoop); |
| li(result, -1); // Not found. |
| b(L_End); |
| } |
| |
| // ************************************************************************************************** |
| // Regular Case Part II: compare rest of needle (first 2 characters have been compared already) |
| // ************************************************************************************************** |
| |
| // Compare the rest |
| bind(L_Comp2); |
| addi(addr, addr, h_csize); // First comparison has failed, 2nd one hit. |
| bind(L_Comp1); // Addr points to possible needle start. |
| if (needlecntval != 2) { // Const needlecnt==2? |
| if (needlecntval != 3) { |
| if (needlecntval == 0) { beq(CR6, L_Found); } // Variable needlecnt==2? |
| Register n_ind = tmp4, |
| h_ind = n_ind; |
| li(n_ind, 2 * n_csize); // First 2 characters are already compared, use index 2. |
| mtctr(needlecnt); // Decremented by 2, still > 0. |
| Label L_CompLoop; |
| bind(L_CompLoop); |
| if (ae ==StrIntrinsicNode::UL) { |
| h_ind = ch1; |
| sldi(h_ind, n_ind, 1); |
| } |
| if (n_csize == 2) { lhzx(ch2, needle, n_ind); } else { lbzx(ch2, needle, n_ind); } |
| if (h_csize == 2) { lhzx(ch1, addr, h_ind); } else { lbzx(ch1, addr, h_ind); } |
| cmpw(CR1, ch1, ch2); |
| bne(CR1, L_OuterLoop); |
| addi(n_ind, n_ind, n_csize); |
| bdnz(L_CompLoop); |
| } else { // No loop required if there's only one needle character left. |
| if (n_csize == 2) { lhz(ch2, 2 * 2, needle); } else { lbz(ch2, 2 * 1, needle); } |
| if (h_csize == 2) { lhz(ch1, 2 * 2, addr); } else { lbz(ch1, 2 * 1, addr); } |
| cmpw(CR1, ch1, ch2); |
| bne(CR1, L_OuterLoop); |
| } |
| } |
| // Return index ... |
| bind(L_Found); |
| subf(result, haystack, addr); // relative to haystack, ... |
| if (h_csize == 2) { srdi(result, result, 1); } // in characters. |
| bind(L_End); |
| } // string_indexof |
| |
| void C2_MacroAssembler::string_indexof_char(Register result, Register haystack, Register haycnt, |
| Register needle, jchar needleChar, Register tmp1, Register tmp2, bool is_byte) { |
| assert_different_registers(haystack, haycnt, needle, tmp1, tmp2); |
| |
| Label L_InnerLoop, L_FinalCheck, L_Found1, L_Found2, L_NotFound, L_End; |
| Register addr = tmp1, |
| ch1 = tmp2, |
| ch2 = R0; |
| |
| const int h_csize = is_byte ? 1 : 2; |
| |
| //4: |
| srwi_(tmp2, haycnt, 1); // Shift right by exact_log2(UNROLL_FACTOR). |
| mr(addr, haystack); |
| beq(CR0, L_FinalCheck); |
| mtctr(tmp2); // Move to count register. |
| //8: |
| bind(L_InnerLoop); // Main work horse (2x unrolled search loop). |
| if (!is_byte) { |
| lhz(ch1, 0, addr); |
| lhz(ch2, 2, addr); |
| } else { |
| lbz(ch1, 0, addr); |
| lbz(ch2, 1, addr); |
| } |
| (needle != R0) ? cmpw(CR0, ch1, needle) : cmplwi(CR0, ch1, (unsigned int)needleChar); |
| (needle != R0) ? cmpw(CR1, ch2, needle) : cmplwi(CR1, ch2, (unsigned int)needleChar); |
| beq(CR0, L_Found1); // Did we find the needle? |
| beq(CR1, L_Found2); |
| addi(addr, addr, 2 * h_csize); |
| bdnz(L_InnerLoop); |
| //16: |
| bind(L_FinalCheck); |
| andi_(R0, haycnt, 1); |
| beq(CR0, L_NotFound); |
| if (!is_byte) { lhz(ch1, 0, addr); } else { lbz(ch1, 0, addr); } // One position left at which we have to compare. |
| (needle != R0) ? cmpw(CR1, ch1, needle) : cmplwi(CR1, ch1, (unsigned int)needleChar); |
| beq(CR1, L_Found1); |
| //21: |
| bind(L_NotFound); |
| li(result, -1); // Not found. |
| b(L_End); |
| |
| bind(L_Found2); |
| addi(addr, addr, h_csize); |
| //24: |
| bind(L_Found1); // Return index ... |
| subf(result, haystack, addr); // relative to haystack, ... |
| if (!is_byte) { srdi(result, result, 1); } // in characters. |
| bind(L_End); |
| } // string_indexof_char |
| |
| |
| void C2_MacroAssembler::count_positives(Register src, Register cnt, Register result, |
| Register tmp1, Register tmp2) { |
| const Register tmp0 = R0; |
| assert_different_registers(src, result, cnt, tmp0, tmp1, tmp2); |
| Label Lfastloop, Lslow, Lloop, Ldone; |
| |
| // Check if cnt >= 8 (= 16 bytes) |
| lis(tmp1, (int)(short)0x8080); // tmp1 = 0x8080808080808080 |
| srwi_(tmp2, cnt, 4); |
| mr(result, src); // Use result reg to point to the current position. |
| beq(CR0, Lslow); |
| ori(tmp1, tmp1, 0x8080); |
| rldimi(tmp1, tmp1, 32, 0); |
| mtctr(tmp2); |
| |
| // 2x unrolled loop |
| bind(Lfastloop); |
| ld(tmp2, 0, result); |
| ld(tmp0, 8, result); |
| |
| orr(tmp0, tmp2, tmp0); |
| |
| and_(tmp0, tmp0, tmp1); |
| bne(CR0, Lslow); // Found negative byte. |
| addi(result, result, 16); |
| bdnz(Lfastloop); |
| |
| bind(Lslow); // Fallback to slow version. |
| subf(tmp0, src, result); // Bytes known positive. |
| subf_(tmp0, tmp0, cnt); // Remaining Bytes. |
| beq(CR0, Ldone); |
| mtctr(tmp0); |
| bind(Lloop); |
| lbz(tmp0, 0, result); |
| andi_(tmp0, tmp0, 0x80); |
| bne(CR0, Ldone); // Found negative byte. |
| addi(result, result, 1); |
| bdnz(Lloop); |
| |
| bind(Ldone); |
| subf(result, src, result); // Result is offset from src. |
| } |
| |
| void C2_MacroAssembler::reduceI(int opcode, Register dst, Register iSrc, VectorRegister vSrc, |
| VectorRegister vTmp1, VectorRegister vTmp2) { |
| |
| auto fn_vec_op = [this](int opcode, const VectorRegister &dst, const VectorRegister &a, const VectorRegister &b) { |
| switch(opcode) { |
| case Op_AddReductionVI: vadduwm(dst, a, b); break; |
| case Op_MulReductionVI: vmuluwm(dst, a , b); break; |
| case Op_AndReductionV: vand(dst, a, b); break; |
| case Op_OrReductionV: vor(dst, a, b); break; |
| case Op_XorReductionV: vxor(dst, a, b); break; |
| case Op_MinReductionV: vminsw(dst, a, b); break; |
| case Op_MaxReductionV: vmaxsw(dst, a, b); break; |
| default: assert(false, "wrong opcode"); |
| } |
| }; |
| |
| auto fn_scalar_op = [this](int opcode, const Register &dst, const Register &a, const Register &b) { |
| switch (opcode) { |
| case Op_AddReductionVI: add(dst, a, b); break; |
| case Op_MulReductionVI: mullw(dst, a, b); break; |
| case Op_AndReductionV: andr(dst, a, b); break; |
| case Op_OrReductionV: orr(dst, a, b); break; |
| case Op_XorReductionV: xorr(dst, a, b); break; |
| case Op_MinReductionV: |
| cmpw(CR0, a, b); |
| isel(dst, CR0, Assembler::less, /*invert*/false, a, b); |
| break; |
| case Op_MaxReductionV: |
| cmpw(CR0, a, b); |
| isel(dst, CR0, Assembler::greater, /*invert*/false, a, b); |
| break; |
| default: assert(false, "wrong opcode"); |
| } |
| }; |
| |
| // vSrc = [i0,i1,i2,i3] |
| vsldoi(vTmp1, vSrc, vSrc, 8); // vTmp1 <- [i2,i3,i0,i1] |
| fn_vec_op(opcode, vTmp2, vSrc, vTmp1); // vTmp2 <- [op(i0,i2), op(i1,i3), op(i2,i0), op(i3,i1)] |
| vsldoi(vTmp1, vTmp2, vTmp2, 4); // vTmp1 <- [op(i1,i3), op(i2,i0), op(i3,i1), op(i0,i2)] |
| fn_vec_op(opcode, vTmp1, vTmp1, vTmp2); // vTmp1 <- [op(i0,i1,i2,i3), op(i0,i1,i2,i3), op(i0,i1,i2,i3), op(i0,i1,i2,i3)] |
| mfvsrwz(R0, vTmp1.to_vsr()); // R0 <- op(i0,i1,i2,i3) |
| fn_scalar_op(opcode, dst, iSrc, R0); // dst <- op(iSrc, R0) |
| } |
| |