8144693: Intrinsify StringCoding.hasNegatives() on SPARC
authorthartmann
Mon, 21 Mar 2016 08:42:20 +0100
changeset 36808 bae14ddeff3b
parent 36807 539a2817f0b4
child 36809 6f9362b27d4f
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>
hotspot/src/cpu/sparc/vm/macroAssembler_sparc.cpp
hotspot/src/cpu/sparc/vm/macroAssembler_sparc.hpp
hotspot/src/cpu/sparc/vm/sparc.ad
hotspot/test/compiler/intrinsics/string/TestHasNegatives.java
--- 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");
+    }
+}