| /* | |
| * 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; | |
| } | |