8144693: Intrinsify StringCoding.hasNegatives() on SPARC
Summary: Implemented C2 instrinsic for StringCode.hasNegatives() on SPARC.
Reviewed-by: kvn, jrose, thartmann
Contributed-by: Guy Delamarter <guy.delamarter@oracle.com>
--- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp Thu Mar 17 09:50:00 2016 +0300
+++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp Mon Mar 21 08:42:20 2016 +0100
@@ -4666,8 +4666,109 @@
bind(Ldone);
}
+void MacroAssembler::has_negatives(Register inp, Register size, Register result, Register t2, Register t3, Register t4, Register t5) {
+
+ // test for negative bytes in input string of a given size
+ // result 1 if found, 0 otherwise.
+
+ Label Lcore, Ltail, Lreturn, Lcore_rpt;
+
+ assert_different_registers(inp, size, t2, t3, t4, t5, result);
+
+ Register i = result; // result used as integer index i until very end
+ Register lmask = t2; // t2 is aliased to lmask
+
+ // INITIALIZATION
+ // ===========================================================
+ // initialize highbits mask -> lmask = 0x8080808080808080 (8B/64b)
+ // compute unaligned offset -> i
+ // compute core end index -> t5
+ Assembler::sethi(0x80808000, t2); //! sethi macro fails to emit optimal
+ add(t2, 0x80, t2);
+ sllx(t2, 32, t3);
+ or3(t3, t2, lmask); // 0x8080808080808080 -> lmask
+ sra(size,0,size);
+ andcc(inp, 0x7, i); // unaligned offset -> i
+ br(Assembler::zero, true, Assembler::pn, Lcore); // starts 8B aligned?
+ delayed()->add(size, -8, t5); // (annuled) core end index -> t5
+
+ // ===========================================================
+
+ // UNALIGNED HEAD
+ // ===========================================================
+ // * unaligned head handling: grab aligned 8B containing unaligned inp(ut)
+ // * obliterate (ignore) bytes outside string by shifting off reg ends
+ // * compare with bitmask, short circuit return true if one or more high
+ // bits set.
+ cmp(size, 0);
+ br(Assembler::zero, true, Assembler::pn, Lreturn); // short-circuit?
+ delayed()->mov(0,result); // annuled so i not clobbered for following
+ neg(i, t4);
+ add(i, size, t5);
+ ldx(inp, t4, t3); // raw aligned 8B containing unaligned head -> t3
+ mov(8, t4);
+ sub(t4, t5, t4);
+ sra(t4, 31, t5);
+ andn(t4, t5, t5);
+ add(i, t5, t4);
+ sll(t5, 3, t5);
+ sll(t4, 3, t4); // # bits to shift right, left -> t5,t4
+ srlx(t3, t5, t3);
+ sllx(t3, t4, t3); // bytes outside string in 8B header obliterated -> t3
+ andcc(lmask, t3, G0);
+ brx(Assembler::notZero, true, Assembler::pn, Lreturn); // short circuit?
+ delayed()->mov(1,result); // annuled so i not clobbered for following
+ add(size, -8, t5); // core end index -> t5
+ mov(8, t4);
+ sub(t4, i, i); // # bytes examined in unalgn head (<8) -> i
+ // ===========================================================
+
+ // ALIGNED CORE
+ // ===========================================================
+ // * iterate index i over aligned 8B sections of core, comparing with
+ // bitmask, short circuit return true if one or more high bits set
+ // t5 contains core end index/loop limit which is the index
+ // of the MSB of last (unaligned) 8B fully contained in the string.
+ // inp contains address of first byte in string/array
+ // lmask contains 8B high bit mask for comparison
+ // i contains next index to be processed (adr. inp+i is on 8B boundary)
+ bind(Lcore);
+ cmp_and_br_short(i, t5, Assembler::greater, Assembler::pn, Ltail);
+ bind(Lcore_rpt);
+ ldx(inp, i, t3);
+ andcc(t3, lmask, G0);
+ brx(Assembler::notZero, true, Assembler::pn, Lreturn);
+ delayed()->mov(1, result); // annuled so i not clobbered for following
+ add(i, 8, i);
+ cmp_and_br_short(i, t5, Assembler::lessEqual, Assembler::pn, Lcore_rpt);
+ // ===========================================================
+
+ // ALIGNED TAIL (<8B)
+ // ===========================================================
+ // handle aligned tail of 7B or less as complete 8B, obliterating end of
+ // string bytes by shifting them off end, compare what's left with bitmask
+ // inp contains address of first byte in string/array
+ // lmask contains 8B high bit mask for comparison
+ // i contains next index to be processed (adr. inp+i is on 8B boundary)
+ bind(Ltail);
+ subcc(size, i, t4); // # of remaining bytes in string -> t4
+ // return 0 if no more remaining bytes
+ br(Assembler::lessEqual, true, Assembler::pn, Lreturn);
+ delayed()->mov(0, result); // annuled so i not clobbered for following
+ ldx(inp, i, t3); // load final 8B (aligned) containing tail -> t3
+ mov(8, t5);
+ sub(t5, t4, t4);
+ mov(0, result); // ** i clobbered at this point
+ sll(t4, 3, t4); // bits beyond end of string -> t4
+ srlx(t3, t4, t3); // bytes beyond end now obliterated -> t3
+ andcc(lmask, t3, G0);
+ movcc(Assembler::notZero, false, xcc, 1, result);
+ bind(Lreturn);
+}
+
#endif
+
// Use BIS for zeroing (count is in bytes).
void MacroAssembler::bis_zeroing(Register to, Register count, Register temp, Label& Ldone) {
assert(UseBlockZeroing && VM_Version::has_block_zeroing(), "only works with BIS zeroing");
--- a/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp Thu Mar 17 09:50:00 2016 +0300
+++ b/hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp Mon Mar 21 08:42:20 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1392,6 +1392,11 @@
void array_equals(bool is_array_equ, Register ary1, Register ary2,
Register limit, Register tmp, Register result, bool is_byte);
+ // test for negative bytes in input string of a given size, result 0 if none
+ void has_negatives(Register inp, Register size, Register result,
+ Register t2, Register t3, Register t4,
+ Register t5);
+
#endif
// Use BIS for zeroing
--- a/hotspot/src/cpu/sparc/vm/sparc.ad Thu Mar 17 09:50:00 2016 +0300
+++ b/hotspot/src/cpu/sparc/vm/sparc.ad Mon Mar 21 08:42:20 2016 +0100
@@ -10168,6 +10168,22 @@
ins_pipe(long_memory_op);
%}
+instruct has_negatives(o0RegP pAryR, g3RegI iSizeR, notemp_iRegI resultR,
+ iRegL tmp1L, iRegL tmp2L, iRegL tmp3L, iRegL tmp4L,
+ flagsReg ccr)
+%{
+ match(Set resultR (HasNegatives pAryR iSizeR));
+ effect(TEMP resultR, TEMP tmp1L, TEMP tmp2L, TEMP tmp3L, TEMP tmp4L, USE pAryR, USE iSizeR, KILL ccr);
+ format %{ "has negatives byte[] $pAryR,$iSizeR -> $resultR // KILL $tmp1L,$tmp2L,$tmp3L,$tmp4L" %}
+ ins_encode %{
+ __ has_negatives($pAryR$$Register, $iSizeR$$Register,
+ $resultR$$Register,
+ $tmp1L$$Register, $tmp2L$$Register,
+ $tmp3L$$Register, $tmp4L$$Register);
+ %}
+ ins_pipe(long_memory_op);
+%}
+
// char[] to byte[] compression
instruct string_compress(o0RegP src, o1RegP dst, g3RegI len, notemp_iRegI result, iRegL tmp, flagsReg ccr) %{
predicate(UseVIS < 3);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/intrinsics/string/TestHasNegatives.java Mon Mar 21 08:42:20 2016 +0100
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8054307
+ * @summary Validates StringCoding.hasNegatives intrinsic with a small range of tests.
+ * @run main/bootclasspath java.lang.TestHasNegatives
+ */
+package java.lang;
+
+import java.lang.StringCoding;
+
+/*
+ * @summary Validates StringCoding.hasNegatives intrinsic with a small
+ * range of tests. Must be run with modified bootclasspath
+ * to allow existence in java.lang package.
+ */
+public class TestHasNegatives {
+
+ private static byte[] tBa = new byte[4096 + 16];
+
+ /**
+ * Completely initialize the test array, preparing it for tests of the
+ * StringCoding.hasNegatives method with a given array segment offset,
+ * length, and number of negative bytes.
+ */
+ public static void initialize(int off, int len, int neg) {
+ assert (len + off <= tBa.length);
+ // insert "canary" (negative) values before offset
+ for (int i = 0; i < off; ++i) {
+ tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80);
+ }
+ // fill the array segment
+ for (int i = off; i < len + off; ++i) {
+ tBa[i] = (byte) (((i - off + 15) & 0x7F));
+ }
+ if (neg != 0) {
+ // modify a number (neg) disparate array bytes inside
+ // segment to be negative.
+ int div = (neg > 1) ? (len - 1) / (neg - 1) : 0;
+ int idx;
+ for (int i = 0; i < neg; ++i) {
+ idx = off + (len - 1) - div * i;
+ tBa[idx] = (byte) (0x80 | tBa[idx]);
+ }
+ }
+ // insert "canary" negative values after array segment
+ for (int i = len + off; i < tBa.length; ++i) {
+ tBa[i] = (byte) (((i + 15) & 0x7F) | 0x80);
+ }
+ }
+
+ /** Sizes of array segments to test. */
+ private static int sizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 17, 19, 23, 37, 61, 131,
+ 4099 };
+
+ /**
+ * Test different array segment sizes, offsets, and number of negative
+ * bytes.
+ */
+ public static void test_hasNegatives() throws Exception {
+ int len, off;
+ int ng;
+ boolean r;
+
+ for (ng = 0; ng < 57; ++ng) { // number of negatives in array segment
+ for (off = 0; off < 8; ++off) { // starting offset of array segment
+ for (int i = 0; i < sizes.length; ++i) { // array segment size
+ // choice
+ len = sizes[i];
+ if (len + off > tBa.length)
+ continue;
+ initialize(off, len, ng);
+ r = StringCoding.hasNegatives(tBa, off, len);
+ if (r ^ ((ng == 0) ? false : true)) {
+ throw new Exception("Failed test hasNegatives " + "offset: " + off + " "
+ + "length: " + len + " " + "return: " + r + " " + "negatives: "
+ + ng);
+ }
+ }
+ }
+ }
+ }
+
+ public void run() throws Exception {
+ // iterate to eventually get intrinsic inlined
+ for (int j = 0; j < 1000; ++j) {
+ test_hasNegatives();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ (new TestHasNegatives()).run();
+ System.out.println("hasNegatives validated");
+ }
+}