| /* |
| * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2017, 2024 SAP SE. 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/stubRoutines.hpp" |
| |
| #define BLOCK_COMMENT(str) block_comment(str) |
| #define BIND(label) bind(label); BLOCK_COMMENT(#label ":") |
| |
| void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register temp1, Register temp2) { |
| compiler_fast_lock_lightweight_object(obj, box, temp1, temp2); |
| } |
| |
| |
| void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Register temp1, Register temp2) { |
| compiler_fast_unlock_lightweight_object(obj, box, temp1, temp2); |
| } |
| |
| void C2_MacroAssembler::load_narrow_klass_compact_c2(Register dst, Address src) { |
| // 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. |
| z_lg(dst, src.plus_disp(-oopDesc::klass_offset_in_bytes())); |
| z_srlg(dst, dst, markWord::klass_shift); // TODO: could be z_sra |
| } |
| |
| //------------------------------------------------------ |
| // Special String Intrinsics. Implementation |
| //------------------------------------------------------ |
| |
| // Intrinsics for CompactStrings |
| |
| // Compress char[] to byte[]. |
| // Restores: src, dst |
| // Uses: cnt |
| // Kills: tmp, Z_R0, Z_R1. |
| // Early clobber: result. |
| // Note: |
| // cnt is signed int. Do not rely on high word! |
| // counts # characters, not bytes. |
| // |
| // The result indicates success or failure of the operation. |
| // General compress operation (cut off high order byte which must be all zeroes). |
| // = len - all characters have been successfully compressed. |
| // = 0 - compress failed. At least one character was found with a non-zero high order byte. |
| // This is the failure return value which exactly corresponds to the Java implementation. |
| // 0 <= result < len - compress failed. That many characters were compressed successfully |
| // before the first non-compressable character was found. This is the |
| // current, but not fully compatible, implementation. See below. |
| // Encode to ISO or 7-bit ASCII array. |
| // = len - all characters have been encoded successfully. |
| // < len - encode failed. That many characters were encoded successfully. |
| // When used as an index into the character array, the return value addresses the |
| // first not encodeable character. |
| // |
| // If precise is true, the processing stops exactly at the point where a failure is detected. |
| // More characters than indicated by the return value may have been read from the src array. |
| // Exactly the number of characters indicated by the return value have been written to dst. |
| // If precise is false, a few characters more than indicated by the return value may have been |
| // written to the dst array. In any failure case, The result value indexes the first invalid character. |
| unsigned int C2_MacroAssembler::string_compress(Register result, Register Rsrc, Register Rdst, Register Rcnt, |
| Register tmp, bool precise, bool toASCII, VectorRegister Vtmp1, VectorRegister Vtmp2, |
| VectorRegister Vmask, VectorRegister Vzero, VectorRegister Vsrc_first, VectorRegister v21, |
| VectorRegister v22, VectorRegister Vsrc_last) { |
| assert_different_registers(Z_R0, Z_R1, result, Rsrc, Rdst, Rcnt, tmp); |
| |
| unsigned short char_mask = 0xff00; // all selected bits must be '0' for a char to be valid |
| unsigned int mask_ix_l = 0; // leftmost one bit pos in mask |
| unsigned int mask_ix_r = 7; // rightmost one bit pos in mask |
| if (precise) { |
| if (toASCII) { |
| BLOCK_COMMENT("encode_ascii_array {"); |
| char_mask = 0xff80; |
| mask_ix_r = 8; // rightmost one bit pos in mask. ASCII only uses codes 0..127 |
| } else { |
| BLOCK_COMMENT("encode_iso_array {"); |
| } |
| } else { |
| BLOCK_COMMENT("string_compress {"); |
| assert(!toASCII, "Can't compress strings to 7-bit ASCII"); |
| } |
| int block_start = offset(); |
| |
| Register Rix = tmp; |
| Register Rmask = result; // holds incompatibility check mask until result value is stored. |
| Label ScalarShortcut, AllDone; |
| |
| z_iilf(Rmask, (unsigned int)char_mask<<16 | (unsigned int)char_mask); |
| z_iihf(Rmask, (unsigned int)char_mask<<16 | (unsigned int)char_mask); |
| |
| #if 0 // Sacrifice shortcuts for code compactness |
| { |
| //---< shortcuts for short strings (very frequent) >--- |
| // Strings with 4 and 8 characters were found to occur very frequently. |
| // Therefore, we handle them right away with minimal overhead. |
| Label skipShortcut, skip4Shortcut, skip8Shortcut; |
| Register Rout = Z_R0; |
| z_chi(Rcnt, 4); |
| z_brne(skip4Shortcut); // 4 characters are very frequent |
| z_lg(Z_R0, 0, Rsrc); // Treat exactly 4 characters specially. |
| if (VM_Version::has_DistinctOpnds()) { |
| Rout = Z_R0; |
| z_ngrk(Rix, Z_R0, Rmask); |
| } else { |
| Rout = Rix; |
| z_lgr(Rix, Z_R0); |
| z_ngr(Z_R0, Rmask); |
| } |
| z_brnz(skipShortcut); |
| z_stcmh(Rout, 5, 0, Rdst); |
| z_stcm(Rout, 5, 2, Rdst); |
| z_lgfr(result, Rcnt); |
| z_bru(AllDone); |
| bind(skip4Shortcut); |
| |
| z_chi(Rcnt, 8); |
| z_brne(skip8Shortcut); // There's more to do... |
| z_lmg(Z_R0, Z_R1, 0, Rsrc); // Treat exactly 8 characters specially. |
| if (VM_Version::has_DistinctOpnds()) { |
| Rout = Z_R0; |
| z_ogrk(Rix, Z_R0, Z_R1); |
| z_ngr(Rix, Rmask); |
| } else { |
| Rout = Rix; |
| z_lgr(Rix, Z_R0); |
| z_ogr(Z_R0, Z_R1); |
| z_ngr(Z_R0, Rmask); |
| } |
| z_brnz(skipShortcut); |
| z_stcmh(Rout, 5, 0, Rdst); |
| z_stcm(Rout, 5, 2, Rdst); |
| z_stcmh(Z_R1, 5, 4, Rdst); |
| z_stcm(Z_R1, 5, 6, Rdst); |
| z_lgfr(result, Rcnt); |
| z_bru(AllDone); |
| |
| bind(skip8Shortcut); |
| clear_reg(Z_R0, true, false); // #characters already processed (none). Precond for scalar loop. |
| z_brl(ScalarShortcut); // Just a few characters |
| |
| bind(skipShortcut); |
| } |
| #endif |
| clear_reg(Z_R0); // make sure register is properly initialized. |
| |
| if (VM_Version::has_VectorFacility()) { |
| const int min_vcnt = 32; // Minimum #characters required to use vector instructions. |
| // Otherwise just do nothing in vector mode. |
| // Must correspond to # vector registers used by implementation, |
| // and must be a power of 2. |
| const int log_min_vcnt = exact_log2(min_vcnt); |
| Label VectorLoop, VectorDone, VectorBreak; |
| |
| assert((Vsrc_last->encoding() - Vsrc_first->encoding() + 1) == min_vcnt/8, "logic error"); |
| assert(VM_Version::has_DistinctOpnds(), "Assumption when has_VectorFacility()"); |
| z_srak(Rix, Rcnt, log_min_vcnt); // # vector loop iterations |
| z_brz(VectorDone); // not enough data for vector loop |
| |
| z_vzero(Vzero); // all zeroes |
| z_vgmh(Vmask, mask_ix_l, mask_ix_r); // generate 0xff00/0xff80 mask for all 2-byte elements |
| z_sllg(Z_R0, Rix, log_min_vcnt); // remember #chars that will be processed by vector loop |
| |
| bind(VectorLoop); |
| z_vlm(Vsrc_first, Vsrc_last, 0, Rsrc); |
| add2reg(Rsrc, min_vcnt*2); |
| |
| //---< check for incompatible character >--- |
| z_vo(Vtmp1, Vsrc_first, v21); |
| z_vo(Vtmp2, v22, Vsrc_last); |
| z_vo(Vtmp1, Vtmp1, Vtmp2); |
| z_vn(Vtmp1, Vtmp1, Vmask); |
| z_vceqhs(Vtmp1, Vtmp1, Vzero); // all bits selected by mask must be zero for successful compress. |
| z_bvnt(VectorBreak); // break vector loop if not all vector elements compare eq -> incompatible character found. |
| // re-process data from current iteration in break handler. |
| |
| //---< pack & store characters >--- |
| z_vpkh(Vtmp1, Vsrc_first, v21); // pack (src1, src2) -> tmp1 |
| z_vpkh(Vtmp2, v22, Vsrc_last); // pack (src3, src4) -> tmp2 |
| z_vstm(Vtmp1, Vtmp2, 0, Rdst); // store packed string |
| add2reg(Rdst, min_vcnt); |
| |
| z_brct(Rix, VectorLoop); |
| |
| z_bru(VectorDone); |
| |
| bind(VectorBreak); |
| add2reg(Rsrc, -min_vcnt*2); // Fix Rsrc. Rsrc was already updated, but Rdst and Rix are not. |
| z_sll(Rix, log_min_vcnt); // # chars processed so far in VectorLoop, excl. current iteration. |
| z_sr(Z_R0, Rix); // correct # chars processed in total. |
| |
| bind(VectorDone); |
| } |
| |
| { |
| const int min_cnt = 8; // Minimum #characters required to use unrolled loop. |
| // Otherwise just do nothing in unrolled loop. |
| // Must correspond to # registers used by implementation, |
| // and must be a power of 2. |
| const int log_min_cnt = exact_log2(min_cnt); |
| Label UnrolledLoop, UnrolledDone, UnrolledBreak; |
| |
| if (VM_Version::has_DistinctOpnds()) { |
| z_srk(Rix, Rcnt, Z_R0); // remaining # chars to compress in unrolled loop |
| } else { |
| z_lr(Rix, Rcnt); |
| z_sr(Rix, Z_R0); |
| } |
| z_sra(Rix, log_min_cnt); // unrolled loop count |
| z_brz(UnrolledDone); |
| |
| bind(UnrolledLoop); |
| z_lmg(Z_R0, Z_R1, 0, Rsrc); |
| if (precise) { |
| z_ogr(Z_R1, Z_R0); // check all 8 chars for incompatibility |
| z_ngr(Z_R1, Rmask); |
| z_brnz(UnrolledBreak); |
| |
| z_lg(Z_R1, 8, Rsrc); // reload destroyed register |
| z_stcmh(Z_R0, 5, 0, Rdst); |
| z_stcm(Z_R0, 5, 2, Rdst); |
| } else { |
| z_stcmh(Z_R0, 5, 0, Rdst); |
| z_stcm(Z_R0, 5, 2, Rdst); |
| |
| z_ogr(Z_R0, Z_R1); |
| z_ngr(Z_R0, Rmask); |
| z_brnz(UnrolledBreak); |
| } |
| z_stcmh(Z_R1, 5, 4, Rdst); |
| z_stcm(Z_R1, 5, 6, Rdst); |
| |
| add2reg(Rsrc, min_cnt*2); |
| add2reg(Rdst, min_cnt); |
| z_brct(Rix, UnrolledLoop); |
| |
| z_lgfr(Z_R0, Rcnt); // # chars processed in total after unrolled loop. |
| z_nilf(Z_R0, ~(min_cnt-1)); |
| z_tmll(Rcnt, min_cnt-1); |
| z_brnaz(ScalarShortcut); // if all bits zero, there is nothing left to do for scalar loop. |
| // Rix == 0 in all cases. |
| z_sllg(Z_R1, Rcnt, 1); // # src bytes already processed. Only lower 32 bits are valid! |
| // Z_R1 contents must be treated as unsigned operand! For huge strings, |
| // (Rcnt >= 2**30), the value may spill into the sign bit by sllg. |
| z_lgfr(result, Rcnt); // all characters processed. |
| z_slgfr(Rdst, Rcnt); // restore ptr |
| z_slgfr(Rsrc, Z_R1); // restore ptr, double the element count for Rsrc restore |
| z_bru(AllDone); |
| |
| bind(UnrolledBreak); |
| z_lgfr(Z_R0, Rcnt); // # chars processed in total after unrolled loop |
| z_nilf(Z_R0, ~(min_cnt-1)); |
| z_sll(Rix, log_min_cnt); // # chars not yet processed in UnrolledLoop (due to break), broken iteration not included. |
| z_sr(Z_R0, Rix); // fix # chars processed OK so far. |
| if (!precise) { |
| // Because we don't need to be precise, we just return the # of characters which have been written. |
| // The first illegal character is in the index range [result-min_cnt/2, result+min_cnt/2). |
| z_lgfr(result, Z_R0); |
| z_sllg(Z_R1, Z_R0, 1); // # src bytes already processed. Only lower 32 bits are valid! |
| // Z_R1 contents must be treated as unsigned operand! For huge strings, |
| // (Rcnt >= 2**30), the value may spill into the sign bit by sllg. |
| z_aghi(result, min_cnt/2); // min_cnt/2 characters have already been written |
| // but ptrs were not updated yet. |
| z_slgfr(Rdst, Z_R0); // restore ptr |
| z_slgfr(Rsrc, Z_R1); // restore ptr, double the element count for Rsrc restore |
| z_bru(AllDone); |
| } |
| bind(UnrolledDone); |
| } |
| |
| { |
| Label ScalarLoop, ScalarDone, ScalarBreak; |
| |
| bind(ScalarShortcut); |
| z_ltgfr(result, Rcnt); |
| z_brz(AllDone); |
| |
| #if 0 // Sacrifice shortcuts for code compactness |
| { |
| //---< Special treatment for very short strings (one or two characters) >--- |
| // For these strings, we are sure that the above code was skipped. |
| // Thus, no registers were modified, register restore is not required. |
| Label ScalarDoit, Scalar2Char; |
| z_chi(Rcnt, 2); |
| z_brh(ScalarDoit); |
| z_llh(Z_R1, 0, Z_R0, Rsrc); |
| z_bre(Scalar2Char); |
| z_tmll(Z_R1, char_mask); |
| z_lghi(result, 0); // cnt == 1, first char invalid, no chars successfully processed |
| z_brnaz(AllDone); |
| z_stc(Z_R1, 0, Z_R0, Rdst); |
| z_lghi(result, 1); |
| z_bru(AllDone); |
| |
| bind(Scalar2Char); |
| z_llh(Z_R0, 2, Z_R0, Rsrc); |
| z_tmll(Z_R1, char_mask); |
| z_lghi(result, 0); // cnt == 2, first char invalid, no chars successfully processed |
| z_brnaz(AllDone); |
| z_stc(Z_R1, 0, Z_R0, Rdst); |
| z_tmll(Z_R0, char_mask); |
| z_lghi(result, 1); // cnt == 2, second char invalid, one char successfully processed |
| z_brnaz(AllDone); |
| z_stc(Z_R0, 1, Z_R0, Rdst); |
| z_lghi(result, 2); |
| z_bru(AllDone); |
| |
| bind(ScalarDoit); |
| } |
| #endif |
| |
| if (VM_Version::has_DistinctOpnds()) { |
| z_srk(Rix, Rcnt, Z_R0); // remaining # chars to compress in scalar loop |
| } else { |
| z_lr(Rix, Rcnt); |
| z_sr(Rix, Z_R0); |
| } |
| z_lgfr(result, Rcnt); // # processed characters (if all encodes ok). |
| z_brz(ScalarDone); // anything left to do? (uses CC from Rix calculation) |
| |
| bind(ScalarLoop); |
| z_llh(Z_R1, 0, Z_R0, Rsrc); |
| z_tmll(Z_R1, char_mask); |
| z_brnaz(ScalarBreak); |
| z_stc(Z_R1, 0, Z_R0, Rdst); |
| add2reg(Rsrc, 2); |
| add2reg(Rdst, 1); |
| z_brct(Rix, ScalarLoop); |
| |
| z_bru(ScalarDone); |
| |
| bind(ScalarBreak); |
| z_sr(result, Rix); |
| |
| bind(ScalarDone); |
| z_sgfr(Rdst, result); // restore ptr |
| z_sgfr(Rsrc, result); // restore ptr, double the element count for Rsrc restore |
| z_sgfr(Rsrc, result); |
| } |
| bind(AllDone); |
| |
| if (precise) { |
| if (toASCII) { |
| BLOCK_COMMENT("} encode_ascii_array"); |
| } else { |
| BLOCK_COMMENT("} encode_iso_array"); |
| } |
| } else { |
| BLOCK_COMMENT("} string_compress"); |
| } |
| return offset() - block_start; |
| } |
| |
| // Inflate byte[] to char[]. |
| unsigned int C2_MacroAssembler::string_inflate_trot(Register src, Register dst, Register cnt, Register tmp) { |
| int block_start = offset(); |
| |
| BLOCK_COMMENT("string_inflate {"); |
| |
| Register stop_char = Z_R0; |
| Register table = Z_R1; |
| Register src_addr = tmp; |
| |
| assert_different_registers(Z_R0, Z_R1, tmp, src, dst, cnt); |
| assert(dst->encoding()%2 == 0, "must be even reg"); |
| assert(cnt->encoding()%2 == 1, "must be odd reg"); |
| assert(cnt->encoding() - dst->encoding() == 1, "must be even/odd pair"); |
| |
| StubRoutines::zarch::generate_load_trot_table_addr(this, table); // kills Z_R0 (if ASSERT) |
| clear_reg(stop_char); // Stop character. Not used here, but initialized to have a defined value. |
| lgr_if_needed(src_addr, src); |
| z_llgfr(cnt, cnt); // # src characters, must be a positive simm32. |
| |
| translate_ot(dst, src_addr, /* mask = */ 0x0001); |
| |
| BLOCK_COMMENT("} string_inflate"); |
| |
| return offset() - block_start; |
| } |
| |
| // Inflate byte[] to char[]. |
| // Restores: src, dst |
| // Uses: cnt |
| // Kills: tmp, Z_R0, Z_R1. |
| // Note: |
| // cnt is signed int. Do not rely on high word! |
| // counts # characters, not bytes. |
| unsigned int C2_MacroAssembler::string_inflate(Register src, Register dst, Register cnt, Register tmp, |
| VectorRegister v20, VectorRegister v21, VectorRegister v22, |
| VectorRegister v23, VectorRegister v24, VectorRegister v25) { |
| assert_different_registers(Z_R0, Z_R1, src, dst, cnt, tmp); |
| |
| BLOCK_COMMENT("string_inflate {"); |
| int block_start = offset(); |
| |
| Register Rcnt = cnt; // # characters (src: bytes, dst: char (2-byte)), remaining after current loop. |
| Register Rix = tmp; // loop index |
| Register Rsrc = src; // addr(src array) |
| Register Rdst = dst; // addr(dst array) |
| Label ScalarShortcut, AllDone; |
| |
| #if 0 // Sacrifice shortcuts for code compactness |
| { |
| //---< shortcuts for short strings (very frequent) >--- |
| Label skipShortcut, skip4Shortcut; |
| z_ltr(Rcnt, Rcnt); // absolutely nothing to do for strings of len == 0. |
| z_brz(AllDone); |
| clear_reg(Z_R0); // make sure registers are properly initialized. |
| clear_reg(Z_R1); |
| z_chi(Rcnt, 4); |
| z_brne(skip4Shortcut); // 4 characters are very frequent |
| z_icm(Z_R0, 5, 0, Rsrc); // Treat exactly 4 characters specially. |
| z_icm(Z_R1, 5, 2, Rsrc); |
| z_stm(Z_R0, Z_R1, 0, Rdst); |
| z_bru(AllDone); |
| bind(skip4Shortcut); |
| |
| z_chi(Rcnt, 8); |
| z_brh(skipShortcut); // There's a lot to do... |
| z_lgfr(Z_R0, Rcnt); // remaining #characters (<= 8). Precond for scalar loop. |
| // This does not destroy the "register cleared" state of Z_R0. |
| z_brl(ScalarShortcut); // Just a few characters |
| z_icmh(Z_R0, 5, 0, Rsrc); // Treat exactly 8 characters specially. |
| z_icmh(Z_R1, 5, 4, Rsrc); |
| z_icm(Z_R0, 5, 2, Rsrc); |
| z_icm(Z_R1, 5, 6, Rsrc); |
| z_stmg(Z_R0, Z_R1, 0, Rdst); |
| z_bru(AllDone); |
| bind(skipShortcut); |
| } |
| #endif |
| clear_reg(Z_R0); // make sure register is properly initialized. |
| |
| if (VM_Version::has_VectorFacility()) { |
| const int min_vcnt = 32; // Minimum #characters required to use vector instructions. |
| // Otherwise just do nothing in vector mode. |
| // Must be multiple of vector register length (16 bytes = 128 bits). |
| const int log_min_vcnt = exact_log2(min_vcnt); |
| Label VectorLoop, VectorDone; |
| |
| assert(VM_Version::has_DistinctOpnds(), "Assumption when has_VectorFacility()"); |
| z_srak(Rix, Rcnt, log_min_vcnt); // calculate # vector loop iterations |
| z_brz(VectorDone); // skip if none |
| |
| z_sllg(Z_R0, Rix, log_min_vcnt); // remember #chars that will be processed by vector loop |
| |
| bind(VectorLoop); |
| z_vlm(v20, v21, 0, Rsrc); // get next 32 characters (single-byte) |
| add2reg(Rsrc, min_vcnt); |
| |
| z_vuplhb(v22, v20); // V2 <- (expand) V0(high) |
| z_vupllb(v23, v20); // V3 <- (expand) V0(low) |
| z_vuplhb(v24, v21); // V4 <- (expand) V1(high) |
| z_vupllb(v25, v21); // V5 <- (expand) V1(low) |
| z_vstm(v22, v25, 0, Rdst); // store next 32 bytes |
| add2reg(Rdst, min_vcnt*2); |
| |
| z_brct(Rix, VectorLoop); |
| |
| bind(VectorDone); |
| } |
| |
| const int min_cnt = 8; // Minimum #characters required to use unrolled scalar loop. |
| // Otherwise just do nothing in unrolled scalar mode. |
| // Must be multiple of 8. |
| { |
| const int log_min_cnt = exact_log2(min_cnt); |
| Label UnrolledLoop, UnrolledDone; |
| |
| |
| if (VM_Version::has_DistinctOpnds()) { |
| z_srk(Rix, Rcnt, Z_R0); // remaining # chars to process in unrolled loop |
| } else { |
| z_lr(Rix, Rcnt); |
| z_sr(Rix, Z_R0); |
| } |
| z_sra(Rix, log_min_cnt); // unrolled loop count |
| z_brz(UnrolledDone); |
| |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| |
| bind(UnrolledLoop); |
| z_icmh(Z_R0, 5, 0, Rsrc); |
| z_icmh(Z_R1, 5, 4, Rsrc); |
| z_icm(Z_R0, 5, 2, Rsrc); |
| z_icm(Z_R1, 5, 6, Rsrc); |
| add2reg(Rsrc, min_cnt); |
| |
| z_stmg(Z_R0, Z_R1, 0, Rdst); |
| |
| add2reg(Rdst, min_cnt*2); |
| z_brct(Rix, UnrolledLoop); |
| |
| bind(UnrolledDone); |
| z_lgfr(Z_R0, Rcnt); // # chars left over after unrolled loop. |
| z_nilf(Z_R0, min_cnt-1); |
| z_brnz(ScalarShortcut); // if zero, there is nothing left to do for scalar loop. |
| // Rix == 0 in all cases. |
| z_sgfr(Z_R0, Rcnt); // negative # characters the ptrs have been advanced previously. |
| z_agr(Rdst, Z_R0); // restore ptr, double the element count for Rdst restore. |
| z_agr(Rdst, Z_R0); |
| z_agr(Rsrc, Z_R0); // restore ptr. |
| z_bru(AllDone); |
| } |
| |
| { |
| bind(ScalarShortcut); |
| // Z_R0 must contain remaining # characters as 64-bit signed int here. |
| // register contents is preserved over scalar processing (for register fixup). |
| |
| #if 0 // Sacrifice shortcuts for code compactness |
| { |
| Label ScalarDefault; |
| z_chi(Rcnt, 2); |
| z_brh(ScalarDefault); |
| z_llc(Z_R0, 0, Z_R0, Rsrc); // 6 bytes |
| z_sth(Z_R0, 0, Z_R0, Rdst); // 4 bytes |
| z_brl(AllDone); |
| z_llc(Z_R0, 1, Z_R0, Rsrc); // 6 bytes |
| z_sth(Z_R0, 2, Z_R0, Rdst); // 4 bytes |
| z_bru(AllDone); |
| bind(ScalarDefault); |
| } |
| #endif |
| |
| Label CodeTable; |
| // Some comments on Rix calculation: |
| // - Rcnt is small, therefore no bits shifted out of low word (sll(g) instructions). |
| // - high word of both Rix and Rcnt may contain garbage |
| // - the final lngfr takes care of that garbage, extending the sign to high word |
| z_sllg(Rix, Z_R0, 2); // calculate 10*Rix = (4*Rix + Rix)*2 |
| z_ar(Rix, Z_R0); |
| z_larl(Z_R1, CodeTable); |
| z_sll(Rix, 1); |
| z_lngfr(Rix, Rix); // ix range: [0..7], after inversion & mult: [-(7*12)..(0*12)]. |
| z_bc(Assembler::bcondAlways, 0, Rix, Z_R1); |
| |
| z_llc(Z_R1, 6, Z_R0, Rsrc); // 6 bytes |
| z_sth(Z_R1, 12, Z_R0, Rdst); // 4 bytes |
| |
| z_llc(Z_R1, 5, Z_R0, Rsrc); |
| z_sth(Z_R1, 10, Z_R0, Rdst); |
| |
| z_llc(Z_R1, 4, Z_R0, Rsrc); |
| z_sth(Z_R1, 8, Z_R0, Rdst); |
| |
| z_llc(Z_R1, 3, Z_R0, Rsrc); |
| z_sth(Z_R1, 6, Z_R0, Rdst); |
| |
| z_llc(Z_R1, 2, Z_R0, Rsrc); |
| z_sth(Z_R1, 4, Z_R0, Rdst); |
| |
| z_llc(Z_R1, 1, Z_R0, Rsrc); |
| z_sth(Z_R1, 2, Z_R0, Rdst); |
| |
| z_llc(Z_R1, 0, Z_R0, Rsrc); |
| z_sth(Z_R1, 0, Z_R0, Rdst); |
| bind(CodeTable); |
| |
| z_chi(Rcnt, 8); // no fixup for small strings. Rdst, Rsrc were not modified. |
| z_brl(AllDone); |
| |
| z_sgfr(Z_R0, Rcnt); // # characters the ptrs have been advanced previously. |
| z_agr(Rdst, Z_R0); // restore ptr, double the element count for Rdst restore. |
| z_agr(Rdst, Z_R0); |
| z_agr(Rsrc, Z_R0); // restore ptr. |
| } |
| bind(AllDone); |
| |
| BLOCK_COMMENT("} string_inflate"); |
| return offset() - block_start; |
| } |
| |
| // Inflate byte[] to char[], length known at compile time. |
| // Restores: src, dst |
| // Kills: tmp, Z_R0, Z_R1. |
| // Note: |
| // len is signed int. Counts # characters, not bytes. |
| unsigned int C2_MacroAssembler::string_inflate_const(Register src, Register dst, Register tmp, int len , |
| VectorRegister v20, VectorRegister v21, VectorRegister v22, |
| VectorRegister v23, VectorRegister v24, VectorRegister v25) { |
| assert_different_registers(Z_R0, Z_R1, src, dst, tmp); |
| |
| BLOCK_COMMENT("string_inflate_const {"); |
| int block_start = offset(); |
| |
| Register Rix = tmp; // loop index |
| Register Rsrc = src; // addr(src array) |
| Register Rdst = dst; // addr(dst array) |
| Label ScalarShortcut, AllDone; |
| int nprocessed = 0; |
| int src_off = 0; // compensate for saved (optimized away) ptr advancement. |
| int dst_off = 0; // compensate for saved (optimized away) ptr advancement. |
| bool restore_inputs = false; |
| bool workreg_clear = false; |
| |
| if ((len >= 32) && VM_Version::has_VectorFacility()) { |
| const int min_vcnt = 32; // Minimum #characters required to use vector instructions. |
| // Otherwise just do nothing in vector mode. |
| // Must be multiple of vector register length (16 bytes = 128 bits). |
| const int log_min_vcnt = exact_log2(min_vcnt); |
| const int iterations = (len - nprocessed) >> log_min_vcnt; |
| nprocessed += iterations << log_min_vcnt; |
| Label VectorLoop; |
| |
| if (iterations == 1) { |
| z_vlm(v20, v21, 0+src_off, Rsrc); // get next 32 characters (single-byte) |
| z_vuplhb(v22, v20); // V2 <- (expand) V0(high) |
| z_vupllb(v23, v20); // V3 <- (expand) V0(low) |
| z_vuplhb(v24, v21); // V4 <- (expand) V1(high) |
| z_vupllb(v25, v21); // V5 <- (expand) V1(low) |
| z_vstm(v22, v25, 0+dst_off, Rdst); // store next 32 bytes |
| |
| src_off += min_vcnt; |
| dst_off += min_vcnt*2; |
| } else { |
| restore_inputs = true; |
| |
| z_lgfi(Rix, len>>log_min_vcnt); |
| bind(VectorLoop); |
| z_vlm(v20, v21, 0, Rsrc); // get next 32 characters (single-byte) |
| add2reg(Rsrc, min_vcnt); |
| |
| z_vuplhb(v22, v20); // V2 <- (expand) V0(high) |
| z_vupllb(v23, v20); // V3 <- (expand) V0(low) |
| z_vuplhb(v24, v21); // V4 <- (expand) V1(high) |
| z_vupllb(v25, v21); // V5 <- (expand) V1(low) |
| z_vstm(v22, v25, 0, Rdst); // store next 32 bytes |
| add2reg(Rdst, min_vcnt*2); |
| |
| z_brct(Rix, VectorLoop); |
| } |
| } |
| |
| if (((len-nprocessed) >= 16) && VM_Version::has_VectorFacility()) { |
| const int min_vcnt = 16; // Minimum #characters required to use vector instructions. |
| // Otherwise just do nothing in vector mode. |
| // Must be multiple of vector register length (16 bytes = 128 bits). |
| const int log_min_vcnt = exact_log2(min_vcnt); |
| const int iterations = (len - nprocessed) >> log_min_vcnt; |
| nprocessed += iterations << log_min_vcnt; |
| assert(iterations == 1, "must be!"); |
| |
| z_vl(v20, 0+src_off, Z_R0, Rsrc); // get next 16 characters (single-byte) |
| z_vuplhb(v22, v20); // V2 <- (expand) V0(high) |
| z_vupllb(v23, v20); // V3 <- (expand) V0(low) |
| z_vstm(v22, v23, 0+dst_off, Rdst); // store next 32 bytes |
| |
| src_off += min_vcnt; |
| dst_off += min_vcnt*2; |
| } |
| |
| if ((len-nprocessed) > 8) { |
| const int min_cnt = 8; // Minimum #characters required to use unrolled scalar loop. |
| // Otherwise just do nothing in unrolled scalar mode. |
| // Must be multiple of 8. |
| const int log_min_cnt = exact_log2(min_cnt); |
| const int iterations = (len - nprocessed) >> log_min_cnt; |
| nprocessed += iterations << log_min_cnt; |
| |
| //---< avoid loop overhead/ptr increment for small # iterations >--- |
| if (iterations <= 2) { |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| workreg_clear = true; |
| |
| z_icmh(Z_R0, 5, 0+src_off, Rsrc); |
| z_icmh(Z_R1, 5, 4+src_off, Rsrc); |
| z_icm(Z_R0, 5, 2+src_off, Rsrc); |
| z_icm(Z_R1, 5, 6+src_off, Rsrc); |
| z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst); |
| |
| src_off += min_cnt; |
| dst_off += min_cnt*2; |
| } |
| |
| if (iterations == 2) { |
| z_icmh(Z_R0, 5, 0+src_off, Rsrc); |
| z_icmh(Z_R1, 5, 4+src_off, Rsrc); |
| z_icm(Z_R0, 5, 2+src_off, Rsrc); |
| z_icm(Z_R1, 5, 6+src_off, Rsrc); |
| z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst); |
| |
| src_off += min_cnt; |
| dst_off += min_cnt*2; |
| } |
| |
| if (iterations > 2) { |
| Label UnrolledLoop; |
| restore_inputs = true; |
| |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| workreg_clear = true; |
| |
| z_lgfi(Rix, iterations); |
| bind(UnrolledLoop); |
| z_icmh(Z_R0, 5, 0, Rsrc); |
| z_icmh(Z_R1, 5, 4, Rsrc); |
| z_icm(Z_R0, 5, 2, Rsrc); |
| z_icm(Z_R1, 5, 6, Rsrc); |
| add2reg(Rsrc, min_cnt); |
| |
| z_stmg(Z_R0, Z_R1, 0, Rdst); |
| add2reg(Rdst, min_cnt*2); |
| |
| z_brct(Rix, UnrolledLoop); |
| } |
| } |
| |
| if ((len-nprocessed) > 0) { |
| switch (len-nprocessed) { |
| case 8: |
| if (!workreg_clear) { |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| } |
| z_icmh(Z_R0, 5, 0+src_off, Rsrc); |
| z_icmh(Z_R1, 5, 4+src_off, Rsrc); |
| z_icm(Z_R0, 5, 2+src_off, Rsrc); |
| z_icm(Z_R1, 5, 6+src_off, Rsrc); |
| z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst); |
| break; |
| case 7: |
| if (!workreg_clear) { |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| } |
| clear_reg(Rix); |
| z_icm(Z_R0, 5, 0+src_off, Rsrc); |
| z_icm(Z_R1, 5, 2+src_off, Rsrc); |
| z_icm(Rix, 5, 4+src_off, Rsrc); |
| z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); |
| z_llc(Z_R0, 6+src_off, Z_R0, Rsrc); |
| z_st(Rix, 8+dst_off, Z_R0, Rdst); |
| z_sth(Z_R0, 12+dst_off, Z_R0, Rdst); |
| break; |
| case 6: |
| if (!workreg_clear) { |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| } |
| clear_reg(Rix); |
| z_icm(Z_R0, 5, 0+src_off, Rsrc); |
| z_icm(Z_R1, 5, 2+src_off, Rsrc); |
| z_icm(Rix, 5, 4+src_off, Rsrc); |
| z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); |
| z_st(Rix, 8+dst_off, Z_R0, Rdst); |
| break; |
| case 5: |
| if (!workreg_clear) { |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| } |
| z_icm(Z_R0, 5, 0+src_off, Rsrc); |
| z_icm(Z_R1, 5, 2+src_off, Rsrc); |
| z_llc(Rix, 4+src_off, Z_R0, Rsrc); |
| z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); |
| z_sth(Rix, 8+dst_off, Z_R0, Rdst); |
| break; |
| case 4: |
| if (!workreg_clear) { |
| clear_reg(Z_R0); |
| clear_reg(Z_R1); |
| } |
| z_icm(Z_R0, 5, 0+src_off, Rsrc); |
| z_icm(Z_R1, 5, 2+src_off, Rsrc); |
| z_stm(Z_R0, Z_R1, 0+dst_off, Rdst); |
| break; |
| case 3: |
| if (!workreg_clear) { |
| clear_reg(Z_R0); |
| } |
| z_llc(Z_R1, 2+src_off, Z_R0, Rsrc); |
| z_icm(Z_R0, 5, 0+src_off, Rsrc); |
| z_sth(Z_R1, 4+dst_off, Z_R0, Rdst); |
| z_st(Z_R0, 0+dst_off, Rdst); |
| break; |
| case 2: |
| z_llc(Z_R0, 0+src_off, Z_R0, Rsrc); |
| z_llc(Z_R1, 1+src_off, Z_R0, Rsrc); |
| z_sth(Z_R0, 0+dst_off, Z_R0, Rdst); |
| z_sth(Z_R1, 2+dst_off, Z_R0, Rdst); |
| break; |
| case 1: |
| z_llc(Z_R0, 0+src_off, Z_R0, Rsrc); |
| z_sth(Z_R0, 0+dst_off, Z_R0, Rdst); |
| break; |
| default: |
| guarantee(false, "Impossible"); |
| break; |
| } |
| src_off += len-nprocessed; |
| dst_off += (len-nprocessed)*2; |
| nprocessed = len; |
| } |
| |
| //---< restore modified input registers >--- |
| if ((nprocessed > 0) && restore_inputs) { |
| z_agfi(Rsrc, -(nprocessed-src_off)); |
| if (nprocessed < 1000000000) { // avoid int overflow |
| z_agfi(Rdst, -(nprocessed*2-dst_off)); |
| } else { |
| z_agfi(Rdst, -(nprocessed-dst_off)); |
| z_agfi(Rdst, -nprocessed); |
| } |
| } |
| |
| BLOCK_COMMENT("} string_inflate_const"); |
| return offset() - block_start; |
| } |
| |
| // Returns the number of non-negative bytes (aka US-ASCII characters) found |
| // before the first negative byte is encountered. |
| unsigned int C2_MacroAssembler::count_positives(Register result, Register src, Register cnt, Register tmp) { |
| const unsigned int block_start = offset(); |
| const unsigned int byte_mask = 0x80; |
| const unsigned int twobyte_mask = byte_mask<<8 | byte_mask; |
| const unsigned int unroll_factor = 16; |
| const unsigned int log_unroll_factor = exact_log2(unroll_factor); |
| Register pos = src; // current position in src array, restored at end |
| Register ctr = result; // loop counter, result value |
| Register mask = tmp; // holds the sign detection mask |
| Label unrolledLoop, unrolledDone, byteLoop, allDone; |
| |
| assert_different_registers(result, src, cnt, tmp); |
| |
| BLOCK_COMMENT("count_positives {"); |
| |
| lgr_if_needed(pos, src); // current position in src array |
| z_srak(ctr, cnt, log_unroll_factor); // # iterations of unrolled loop |
| z_brnh(unrolledDone); // array too short for unrolled loop |
| |
| z_iilf(mask, twobyte_mask<<16 | twobyte_mask); |
| z_iihf(mask, twobyte_mask<<16 | twobyte_mask); |
| |
| bind(unrolledLoop); |
| z_lmg(Z_R0, Z_R1, 0, pos); |
| z_ogr(Z_R0, Z_R1); |
| z_ngr(Z_R0, mask); |
| z_brne(unrolledDone); // There is a negative byte somewhere. |
| // ctr and pos are not updated yet -> |
| // delegate finding correct pos to byteLoop. |
| add2reg(pos, unroll_factor); |
| z_brct(ctr, unrolledLoop); |
| |
| // Once we arrive here, we have to examine at most (unroll_factor - 1) bytes more. |
| // We then either have reached the end of the array or we hit a negative byte. |
| bind(unrolledDone); |
| z_sll(ctr, log_unroll_factor); // calculate # bytes not processed by unrolled loop |
| // > 0 only if a negative byte was found |
| z_lr(Z_R0, cnt); // calculate remainder bytes |
| z_nilf(Z_R0, unroll_factor - 1); |
| z_ar(ctr, Z_R0); // remaining bytes |
| z_brnh(allDone); // shortcut if nothing left to do |
| |
| bind(byteLoop); |
| z_cli(0, pos, byte_mask); // unsigned comparison! byte@pos must be smaller that byte_mask |
| z_brnl(allDone); // negative byte found. |
| |
| add2reg(pos, 1); |
| z_brct(ctr, byteLoop); |
| |
| bind(allDone); |
| |
| z_srk(ctr, cnt, ctr); // # bytes actually processed (= cnt or index of first negative byte) |
| z_sgfr(pos, ctr); // restore src |
| z_lgfr(result, ctr); // unnecessary. Only there to be sure the high word has a defined state. |
| |
| BLOCK_COMMENT("} count_positives"); |
| |
| return offset() - block_start; |
| } |
| |
| // kill: cnt1, cnt2, odd_reg, even_reg; early clobber: result |
| unsigned int C2_MacroAssembler::string_compare(Register str1, Register str2, |
| Register cnt1, Register cnt2, |
| Register odd_reg, Register even_reg, Register result, int ae) { |
| int block_start = offset(); |
| |
| assert_different_registers(str1, cnt1, cnt2, odd_reg, even_reg, result); |
| assert_different_registers(str2, cnt1, cnt2, odd_reg, even_reg, result); |
| |
| // If strings are equal up to min length, return the length difference. |
| const Register diff = result, // Pre-set result with length difference. |
| min = cnt1, // min number of bytes |
| tmp = cnt2; |
| |
| // 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 when using 64 bit register. |
| const bool used_as_LU = (ae == StrIntrinsicNode::LU || ae == StrIntrinsicNode::UL); |
| |
| BLOCK_COMMENT("string_compare {"); |
| |
| if (used_as_LU) { |
| z_srl(cnt2, 1); |
| } |
| |
| // See if the lengths are different, and calculate min in cnt1. |
| // Save diff in case we need it for a tie-breaker. |
| |
| // diff = cnt1 - cnt2 |
| if (VM_Version::has_DistinctOpnds()) { |
| z_srk(diff, cnt1, cnt2); |
| } else { |
| z_lr(diff, cnt1); |
| z_sr(diff, cnt2); |
| } |
| if (str1 != str2) { |
| if (VM_Version::has_LoadStoreConditional()) { |
| z_locr(min, cnt2, Assembler::bcondHigh); |
| } else { |
| Label Lskip; |
| z_brl(Lskip); // min ok if cnt1 < cnt2 |
| z_lr(min, cnt2); // min = cnt2 |
| bind(Lskip); |
| } |
| } |
| |
| if (ae == StrIntrinsicNode::UU) { |
| z_sra(diff, 1); |
| } |
| if (str1 != str2) { |
| Label Ldone; |
| if (used_as_LU) { |
| // Loop which searches the first difference character by character. |
| Label Lloop; |
| const Register ind1 = Z_R1, |
| ind2 = min; |
| int stride1 = 1, stride2 = 2; // See comment above. |
| |
| // ind1: index, even_reg: index increment, odd_reg: index limit |
| z_llilf(ind1, (unsigned int)(-stride1)); |
| z_lhi(even_reg, stride1); |
| add2reg(odd_reg, -stride1, min); |
| clear_reg(ind2); // kills min |
| |
| bind(Lloop); |
| z_brxh(ind1, even_reg, Ldone); |
| z_llc(tmp, Address(str1, ind1)); |
| z_llh(Z_R0, Address(str2, ind2)); |
| z_ahi(ind2, stride2); |
| z_sr(tmp, Z_R0); |
| z_bre(Lloop); |
| |
| z_lr(result, tmp); |
| |
| } else { |
| // Use clcle in fast loop (only for same encoding). |
| z_lgr(Z_R0, str1); |
| z_lgr(even_reg, str2); |
| z_llgfr(Z_R1, min); |
| z_llgfr(odd_reg, min); |
| |
| if (ae == StrIntrinsicNode::LL) { |
| compare_long_ext(Z_R0, even_reg, 0); |
| } else { |
| compare_long_uni(Z_R0, even_reg, 0); |
| } |
| z_bre(Ldone); |
| z_lgr(Z_R1, Z_R0); |
| if (ae == StrIntrinsicNode::LL) { |
| z_llc(Z_R0, Address(even_reg)); |
| z_llc(result, Address(Z_R1)); |
| } else { |
| z_llh(Z_R0, Address(even_reg)); |
| z_llh(result, Address(Z_R1)); |
| } |
| z_sr(result, Z_R0); |
| } |
| |
| // Otherwise, return the difference between the first mismatched chars. |
| bind(Ldone); |
| } |
| |
| if (ae == StrIntrinsicNode::UL) { |
| z_lcr(result, result); // Negate result (see note above). |
| } |
| |
| BLOCK_COMMENT("} string_compare"); |
| |
| return offset() - block_start; |
| } |
| |
| unsigned int C2_MacroAssembler::array_equals(bool is_array_equ, Register ary1, Register ary2, Register limit, |
| Register odd_reg, Register even_reg, Register result, bool is_byte) { |
| int block_start = offset(); |
| |
| BLOCK_COMMENT("array_equals {"); |
| |
| assert_different_registers(ary1, limit, odd_reg, even_reg); |
| assert_different_registers(ary2, limit, odd_reg, even_reg); |
| |
| Label Ldone, Ldone_true, Ldone_false, Lclcle, CLC_template; |
| int base_offset = 0; |
| |
| if (ary1 != ary2) { |
| if (is_array_equ) { |
| base_offset = arrayOopDesc::base_offset_in_bytes(is_byte ? T_BYTE : T_CHAR); |
| |
| // Return true if the same array. |
| compareU64_and_branch(ary1, ary2, Assembler::bcondEqual, Ldone_true); |
| |
| // Return false if one of them is null. |
| compareU64_and_branch(ary1, (intptr_t)0, Assembler::bcondEqual, Ldone_false); |
| compareU64_and_branch(ary2, (intptr_t)0, Assembler::bcondEqual, Ldone_false); |
| |
| // Load the lengths of arrays. |
| z_llgf(odd_reg, Address(ary1, arrayOopDesc::length_offset_in_bytes())); |
| |
| // Return false if the two arrays are not equal length. |
| z_c(odd_reg, Address(ary2, arrayOopDesc::length_offset_in_bytes())); |
| z_brne(Ldone_false); |
| |
| // string len in bytes (right operand) |
| if (!is_byte) { |
| z_chi(odd_reg, 128); |
| z_sll(odd_reg, 1); // preserves flags |
| z_brh(Lclcle); |
| } else { |
| compareU32_and_branch(odd_reg, (intptr_t)256, Assembler::bcondHigh, Lclcle); |
| } |
| } else { |
| z_llgfr(odd_reg, limit); // Need to zero-extend prior to using the value. |
| compareU32_and_branch(limit, (intptr_t)256, Assembler::bcondHigh, Lclcle); |
| } |
| |
| |
| // Use clc instruction for up to 256 bytes. |
| { |
| Register str1_reg = ary1, |
| str2_reg = ary2; |
| if (is_array_equ) { |
| str1_reg = Z_R1; |
| str2_reg = even_reg; |
| add2reg(str1_reg, base_offset, ary1); // string addr (left operand) |
| add2reg(str2_reg, base_offset, ary2); // string addr (right operand) |
| } |
| z_ahi(odd_reg, -1); // Clc uses decremented limit. Also compare result to 0. |
| z_brl(Ldone_true); |
| // Note: We could jump to the template if equal. |
| |
| assert(VM_Version::has_ExecuteExtensions(), "unsupported hardware"); |
| z_exrl(odd_reg, CLC_template); |
| z_bre(Ldone_true); |
| // fall through |
| |
| bind(Ldone_false); |
| clear_reg(result); |
| z_bru(Ldone); |
| |
| bind(CLC_template); |
| z_clc(0, 0, str1_reg, 0, str2_reg); |
| } |
| |
| // Use clcle instruction. |
| { |
| bind(Lclcle); |
| add2reg(even_reg, base_offset, ary2); // string addr (right operand) |
| add2reg(Z_R0, base_offset, ary1); // string addr (left operand) |
| |
| z_lgr(Z_R1, odd_reg); // string len in bytes (left operand) |
| if (is_byte) { |
| compare_long_ext(Z_R0, even_reg, 0); |
| } else { |
| compare_long_uni(Z_R0, even_reg, 0); |
| } |
| z_lghi(result, 0); // Preserve flags. |
| z_brne(Ldone); |
| } |
| } |
| // fall through |
| |
| bind(Ldone_true); |
| z_lghi(result, 1); // All characters are equal. |
| bind(Ldone); |
| |
| BLOCK_COMMENT("} array_equals"); |
| |
| return offset() - block_start; |
| } |
| |
| // kill: haycnt, needlecnt, odd_reg, even_reg; early clobber: result |
| unsigned int C2_MacroAssembler::string_indexof(Register result, Register haystack, Register haycnt, |
| Register needle, Register needlecnt, int needlecntval, |
| Register odd_reg, Register even_reg, int ae) { |
| int block_start = offset(); |
| |
| // Ensure 0<needlecnt<=haycnt in ideal graph as prerequisite! |
| assert(ae != StrIntrinsicNode::LU, "Invalid encoding"); |
| const int h_csize = (ae == StrIntrinsicNode::LL) ? 1 : 2; |
| const int n_csize = (ae == StrIntrinsicNode::UU) ? 2 : 1; |
| Label L_needle1, L_Found, L_NotFound; |
| |
| BLOCK_COMMENT("string_indexof {"); |
| |
| if (needle == haystack) { |
| z_lhi(result, 0); |
| } else { |
| |
| // Load first character of needle (R0 used by search_string instructions). |
| if (n_csize == 2) { z_llgh(Z_R0, Address(needle)); } else { z_llgc(Z_R0, Address(needle)); } |
| |
| // Compute last haystack addr to use if no match gets found. |
| if (needlecnt != noreg) { // variable needlecnt |
| z_ahi(needlecnt, -1); // Remaining characters after first one. |
| z_sr(haycnt, needlecnt); // Compute index succeeding last element to compare. |
| if (n_csize == 2) { z_sll(needlecnt, 1); } // In bytes. |
| } else { // constant needlecnt |
| assert((needlecntval & 0x7fff) == needlecntval, "must be positive simm16 immediate"); |
| // Compute index succeeding last element to compare. |
| if (needlecntval != 1) { z_ahi(haycnt, 1 - needlecntval); } |
| } |
| |
| z_llgfr(haycnt, haycnt); // Clear high half. |
| z_lgr(result, haystack); // Final result will be computed from needle start pointer. |
| if (h_csize == 2) { z_sll(haycnt, 1); } // Scale to number of bytes. |
| z_agr(haycnt, haystack); // Point to address succeeding last element (haystack+scale*(haycnt-needlecnt+1)). |
| |
| if (h_csize != n_csize) { |
| assert(ae == StrIntrinsicNode::UL, "Invalid encoding"); |
| |
| if (needlecnt != noreg || needlecntval != 1) { |
| if (needlecnt != noreg) { |
| compare32_and_branch(needlecnt, (intptr_t)0, Assembler::bcondEqual, L_needle1); |
| } |
| |
| // Main Loop: UL version (now we have at least 2 characters). |
| Label L_OuterLoop, L_InnerLoop, L_Skip; |
| bind(L_OuterLoop); // Search for 1st 2 characters. |
| z_lgr(Z_R1, haycnt); |
| MacroAssembler::search_string_uni(Z_R1, result); |
| z_brc(Assembler::bcondNotFound, L_NotFound); |
| z_lgr(result, Z_R1); |
| |
| z_lghi(Z_R1, n_csize); |
| z_lghi(even_reg, h_csize); |
| bind(L_InnerLoop); |
| z_llgc(odd_reg, Address(needle, Z_R1)); |
| z_ch(odd_reg, Address(result, even_reg)); |
| z_brne(L_Skip); |
| if (needlecnt != noreg) { z_cr(Z_R1, needlecnt); } else { z_chi(Z_R1, needlecntval - 1); } |
| z_brnl(L_Found); |
| z_aghi(Z_R1, n_csize); |
| z_aghi(even_reg, h_csize); |
| z_bru(L_InnerLoop); |
| |
| bind(L_Skip); |
| z_aghi(result, h_csize); // This is the new address we want to use for comparing. |
| z_bru(L_OuterLoop); |
| } |
| |
| } else { |
| const intptr_t needle_bytes = (n_csize == 2) ? ((needlecntval - 1) << 1) : (needlecntval - 1); |
| Label L_clcle; |
| |
| if (needlecnt != noreg || (needlecntval != 1 && needle_bytes <= 256)) { |
| if (needlecnt != noreg) { |
| compare32_and_branch(needlecnt, 256, Assembler::bcondHigh, L_clcle); |
| z_ahi(needlecnt, -1); // remaining bytes -1 (for CLC) |
| z_brl(L_needle1); |
| } |
| |
| // Main Loop: clc version (now we have at least 2 characters). |
| Label L_OuterLoop, CLC_template; |
| bind(L_OuterLoop); // Search for 1st 2 characters. |
| z_lgr(Z_R1, haycnt); |
| if (h_csize == 1) { |
| MacroAssembler::search_string(Z_R1, result); |
| } else { |
| MacroAssembler::search_string_uni(Z_R1, result); |
| } |
| z_brc(Assembler::bcondNotFound, L_NotFound); |
| z_lgr(result, Z_R1); |
| |
| if (needlecnt != noreg) { |
| assert(VM_Version::has_ExecuteExtensions(), "unsupported hardware"); |
| z_exrl(needlecnt, CLC_template); |
| } else { |
| z_clc(h_csize, needle_bytes -1, Z_R1, n_csize, needle); |
| } |
| z_bre(L_Found); |
| z_aghi(result, h_csize); // This is the new address we want to use for comparing. |
| z_bru(L_OuterLoop); |
| |
| if (needlecnt != noreg) { |
| bind(CLC_template); |
| z_clc(h_csize, 0, Z_R1, n_csize, needle); |
| } |
| } |
| |
| if (needlecnt != noreg || needle_bytes > 256) { |
| bind(L_clcle); |
| |
| // Main Loop: clcle version (now we have at least 256 bytes). |
| Label L_OuterLoop, CLC_template; |
| bind(L_OuterLoop); // Search for 1st 2 characters. |
| z_lgr(Z_R1, haycnt); |
| if (h_csize == 1) { |
| MacroAssembler::search_string(Z_R1, result); |
| } else { |
| MacroAssembler::search_string_uni(Z_R1, result); |
| } |
| z_brc(Assembler::bcondNotFound, L_NotFound); |
| |
| add2reg(Z_R0, n_csize, needle); |
| add2reg(even_reg, h_csize, Z_R1); |
| z_lgr(result, Z_R1); |
| if (needlecnt != noreg) { |
| z_llgfr(Z_R1, needlecnt); // needle len in bytes (left operand) |
| z_llgfr(odd_reg, needlecnt); |
| } else { |
| load_const_optimized(Z_R1, needle_bytes); |
| if (Immediate::is_simm16(needle_bytes)) { z_lghi(odd_reg, needle_bytes); } else { z_lgr(odd_reg, Z_R1); } |
| } |
| if (h_csize == 1) { |
| compare_long_ext(Z_R0, even_reg, 0); |
| } else { |
| compare_long_uni(Z_R0, even_reg, 0); |
| } |
| z_bre(L_Found); |
| |
| if (n_csize == 2) { z_llgh(Z_R0, Address(needle)); } else { z_llgc(Z_R0, Address(needle)); } // Reload. |
| z_aghi(result, h_csize); // This is the new address we want to use for comparing. |
| z_bru(L_OuterLoop); |
| } |
| } |
| |
| if (needlecnt != noreg || needlecntval == 1) { |
| bind(L_needle1); |
| |
| // Single needle character version. |
| if (h_csize == 1) { |
| MacroAssembler::search_string(haycnt, result); |
| } else { |
| MacroAssembler::search_string_uni(haycnt, result); |
| } |
| z_lgr(result, haycnt); |
| z_brc(Assembler::bcondFound, L_Found); |
| } |
| |
| bind(L_NotFound); |
| add2reg(result, -1, haystack); // Return -1. |
| |
| bind(L_Found); // Return index (or -1 in fallthrough case). |
| z_sgr(result, haystack); |
| if (h_csize == 2) { z_srag(result, result, exact_log2(sizeof(jchar))); } |
| } |
| BLOCK_COMMENT("} string_indexof"); |
| |
| return offset() - block_start; |
| } |
| |
| // early clobber: result |
| unsigned int C2_MacroAssembler::string_indexof_char(Register result, Register haystack, Register haycnt, |
| Register needle, jchar needleChar, Register odd_reg, Register even_reg, bool is_byte) { |
| int block_start = offset(); |
| |
| BLOCK_COMMENT("string_indexof_char {"); |
| |
| if (needle == haystack) { |
| z_lhi(result, 0); |
| } else { |
| |
| Label Ldone; |
| |
| z_llgfr(odd_reg, haycnt); // Preset loop ctr/searchrange end. |
| if (needle == noreg) { |
| load_const_optimized(Z_R0, (unsigned long)needleChar); |
| } else { |
| if (is_byte) { |
| z_llgcr(Z_R0, needle); // First (and only) needle char. |
| } else { |
| z_llghr(Z_R0, needle); // First (and only) needle char. |
| } |
| } |
| |
| if (!is_byte) { |
| z_agr(odd_reg, odd_reg); // Calc #bytes to be processed with SRSTU. |
| } |
| |
| z_lgr(even_reg, haystack); // haystack addr |
| z_agr(odd_reg, haystack); // First char after range end. |
| z_lghi(result, -1); |
| |
| if (is_byte) { |
| MacroAssembler::search_string(odd_reg, even_reg); |
| } else { |
| MacroAssembler::search_string_uni(odd_reg, even_reg); |
| } |
| z_brc(Assembler::bcondNotFound, Ldone); |
| if (is_byte) { |
| if (VM_Version::has_DistinctOpnds()) { |
| z_sgrk(result, odd_reg, haystack); |
| } else { |
| z_sgr(odd_reg, haystack); |
| z_lgr(result, odd_reg); |
| } |
| } else { |
| z_slgr(odd_reg, haystack); |
| z_srlg(result, odd_reg, exact_log2(sizeof(jchar))); |
| } |
| |
| bind(Ldone); |
| } |
| BLOCK_COMMENT("} string_indexof_char"); |
| |
| return offset() - block_start; |
| } |
| |