1 /*
  2  * Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package jdk.incubator.vector;
 26 
 27 import java.util.Objects;
 28 
 29 import jdk.internal.vm.annotation.ForceInline;
 30 
 31 import static jdk.incubator.vector.VectorOperators.*;
 32 
 33 abstract class AbstractMask<E> extends VectorMask<E> {
 34     AbstractMask(boolean[] bits) {
 35         super(bits);
 36     }
 37 
 38     /*package-private*/
 39     abstract boolean[] getBits();
 40 
 41     // Unary operator
 42 
 43     interface MUnOp {
 44         boolean apply(int i, boolean a);
 45     }
 46 
 47     abstract AbstractMask<E> uOp(MUnOp f);
 48 
 49     // Binary operator
 50 
 51     interface MBinOp {
 52         boolean apply(int i, boolean a, boolean b);
 53     }
 54 
 55     abstract AbstractMask<E> bOp(VectorMask<E> o, MBinOp f);
 56 
 57     /*package-private*/
 58     abstract AbstractSpecies<E> vspecies();
 59 
 60     @Override
 61     @ForceInline
 62     public final VectorSpecies<E> vectorSpecies() {
 63         return vspecies();
 64     }
 65 
 66     @Override
 67     public boolean laneIsSet(int i) {
 68         int length = length();
 69         Objects.checkIndex(i, length);
 70         if (length <= Long.SIZE) {
 71             return ((toLong() >>> i) & 1L) == 1;
 72         } else {
 73             return getBits()[i];
 74         }
 75     }
 76 
 77     @Override
 78     public void intoArray(boolean[] bits, int i) {
 79         System.arraycopy(getBits(), 0, bits, i, length());
 80     }
 81 
 82     @Override
 83     public boolean[] toArray() {
 84         return getBits().clone();
 85     }
 86 
 87     @Override
 88     @ForceInline
 89     @SuppressWarnings("unchecked")
 90     public
 91     <F> VectorMask<F> check(Class<F> elementType) {
 92         if (vectorSpecies().elementType() != elementType) {
 93             throw AbstractSpecies.checkFailed(this, elementType);
 94         }
 95         return (VectorMask<F>) this;
 96     }
 97 
 98     @Override
 99     @ForceInline
100     @SuppressWarnings("unchecked")
101     public
102     <F> VectorMask<F> check(VectorSpecies<F> species) {
103         if (species != vectorSpecies()) {
104             throw AbstractSpecies.checkFailed(this, species);
105         }
106         return (VectorMask<F>) this;
107     }
108 
109     @Override
110     @ForceInline
111     @SuppressWarnings("unchecked")
112     <F> VectorMask<F> check(Class<? extends VectorMask<F>> maskClass, Vector<F> vector) {
113         if (!sameSpecies(maskClass, vector)) {
114             throw AbstractSpecies.checkFailed(this, vector);
115         }
116         return (VectorMask<F>) this;
117     }
118 
119     @ForceInline
120     private <F> boolean sameSpecies(Class<? extends VectorMask<F>> maskClass, Vector<F> vector) {
121         boolean same = getClass() == maskClass;
122         assert (same == (vectorSpecies() == vector.species())) : same;
123         return same;
124     }
125 
126     @Override
127     public VectorMask<E> andNot(VectorMask<E> m) {
128         return and(m.not());
129     }
130 
131     /*package-private*/
132     static boolean anyTrueHelper(boolean[] bits) {
133         // FIXME: Maybe use toLong() != 0 here.
134         for (boolean i : bits) {
135             if (i) return true;
136         }
137         return false;
138     }
139 
140     /*package-private*/
141     static boolean allTrueHelper(boolean[] bits) {
142         // FIXME: Maybe use not().toLong() == 0 here.
143         for (boolean i : bits) {
144             if (!i) return false;
145         }
146         return true;
147     }
148 
149     /*package-private*/
150     static int trueCountHelper(boolean[] bits) {
151         int c = 0;
152         for (boolean i : bits) {
153             if (i) c++;
154         }
155         return c;
156     }
157 
158     /*package-private*/
159     static int firstTrueHelper(boolean[] bits) {
160         for (int i = 0; i < bits.length; i++) {
161             if (bits[i])  return i;
162         }
163         return bits.length;
164     }
165 
166     /*package-private*/
167     static int lastTrueHelper(boolean[] bits) {
168         for (int i = bits.length-1; i >= 0; i--) {
169             if (bits[i])  return i;
170         }
171         return -1;
172     }
173 
174     /*package-private*/
175     static long toLongHelper(boolean[] bits) {
176         long res = 0;
177         long set = 1;
178         for (int i = 0; i < bits.length; i++) {
179             res = bits[i] ? res | set : res;
180             set = set << 1;
181         }
182         return res;
183     }
184 
185     @Override
186     @ForceInline
187     public VectorMask<E> indexInRange(int offset, int limit) {
188         int vlength = length();
189         Vector<E> iota = vectorSpecies().zero().addIndex(1);
190         VectorMask<E> badMask = checkIndex0(offset, limit, iota, vlength);
191         return this.andNot(badMask);
192     }
193 
194     /*package-private*/
195     @ForceInline
196     AbstractVector<E>
197     toVectorTemplate() {
198         AbstractSpecies<E> vsp = vspecies();
199         Vector<E> zero = vsp.broadcast(0);
200         Vector<E> mone = vsp.broadcast(-1);
201         // -1 will result in the most significant bit being set in
202         // addition to some or all other lane bits.
203         // For integral types, *all* lane bits will be set.
204         // The bits for -1.0 are like {0b10111*0000*}.
205         // FIXME: Use a conversion intrinsic for this operation.
206         // https://bugs.openjdk.java.net/browse/JDK-8225740
207         return (AbstractVector<E>) zero.blend(mone, this);
208     }
209 
210     /**
211      * Test if a masked memory access at a given offset into an array
212      * of the given length will stay within the array.
213      * The per-lane offsets are iota*esize.
214      */
215     /*package-private*/
216     @ForceInline
217     void checkIndexByLane(int offset, int alength,
218                           Vector<E> iota,
219                           int esize) {
220         if (VectorIntrinsics.VECTOR_ACCESS_OOB_CHECK == 0) {
221             return;
222         }
223         // Although the specification is simple, the implementation is
224         // tricky, because the value iota*esize might possibly
225         // overflow.  So we calculate our test values as scalars,
226         // clipping to the range [-1..VLENGTH], and test them against
227         // the unscaled iota vector, whose values are in [0..VLENGTH-1].
228         int vlength = length();
229         VectorMask<E> badMask;
230         if (esize == 1) {
231             badMask = checkIndex0(offset, alength, iota, vlength);
232         } else if (offset >= 0) {
233             // Masked access to multi-byte lanes in byte array.
234             // It could be aligned anywhere.
235             int elemCount = Math.min(vlength, (alength - offset) / esize);
236             badMask = checkIndex0(0, elemCount, iota, vlength);
237         } else {
238             int clipOffset = Math.max(offset, -(vlength * esize));
239             badMask = checkIndex0(clipOffset, alength,
240                                   iota.lanewise(VectorOperators.MUL, esize),
241                                   vlength * esize);
242         }
243         badMask = badMask.and(this);
244         if (badMask.anyTrue()) {
245             int badLane = badMask.firstTrue();
246             throw ((AbstractMask<E>)badMask)
247                    .checkIndexFailed(offset, badLane, alength, esize);
248         }
249     }
250 
251     private
252     @ForceInline
253     VectorMask<E> checkIndex0(int offset, int alength,
254                               Vector<E> iota, int vlength) {
255         // An active lane is bad if its number is greater than
256         // alength-offset, since when added to offset it will step off
257         // of the end of the array.  To avoid overflow when
258         // converting, clip the comparison value to [0..vlength]
259         // inclusive.
260         int indexLimit = Math.max(0, Math.min(alength - offset, vlength));
261         VectorMask<E> badMask =
262             iota.compare(GE, iota.broadcast(indexLimit));
263         if (offset < 0) {
264             // An active lane is bad if its number is less than
265             // -offset, because when added to offset it will then
266             // address an array element at a negative index.  To avoid
267             // overflow when converting, clip the comparison value at
268             // vlength.  This specific expression works correctly even
269             // when offset is Integer.MIN_VALUE.
270             int firstGoodIndex = -Math.max(offset, -vlength);
271             VectorMask<E> badMask2 =
272                 iota.compare(LT, iota.broadcast(firstGoodIndex));
273             if (indexLimit >= vlength) {
274                 badMask = badMask2;  // 1st badMask is all true
275             } else {
276                 badMask = badMask.or(badMask2);
277             }
278         }
279         return badMask;
280     }
281 
282     private IndexOutOfBoundsException checkIndexFailed(int offset, int lane,
283                                                        int alength, int esize) {
284         String msg = String.format("Masked range check failed: "+
285                                    "vector mask %s out of bounds at "+
286                                    "index %d+%d in array of length %d",
287                                    this, offset, lane * esize, alength);
288         if (esize != 1) {
289             msg += String.format(" (each lane spans %d array elements)", esize);
290         }
291         throw new IndexOutOfBoundsException(msg);
292     }
293 
294 }