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     @ForceInline
 68     public boolean laneIsSet(int i) {
 69         int length = length();
 70         Objects.checkIndex(i, length);
 71         if (length <= Long.SIZE) {
 72             return ((toLong() >>> i) & 1L) == 1;
 73         } else {
 74             return getBits()[i];
 75         }
 76     }
 77 
 78     @Override
 79     public void intoArray(boolean[] bits, int i) {
 80         System.arraycopy(getBits(), 0, bits, i, length());
 81     }
 82 
 83     @Override
 84     public boolean[] toArray() {
 85         return getBits().clone();
 86     }
 87 
 88     @Override
 89     @ForceInline
 90     @SuppressWarnings("unchecked")
 91     public
 92     <F> VectorMask<F> check(Class<F> elementType) {
 93         if (vectorSpecies().elementType() != elementType) {
 94             throw AbstractSpecies.checkFailed(this, elementType);
 95         }
 96         return (VectorMask<F>) this;
 97     }
 98 
 99     @Override
100     @ForceInline
101     @SuppressWarnings("unchecked")
102     public
103     <F> VectorMask<F> check(VectorSpecies<F> species) {
104         if (species != vectorSpecies()) {
105             throw AbstractSpecies.checkFailed(this, species);
106         }
107         return (VectorMask<F>) this;
108     }
109 
110     @Override
111     @ForceInline
112     @SuppressWarnings("unchecked")
113     <F> VectorMask<F> check(Class<? extends VectorMask<F>> maskClass, Vector<F> vector) {
114         if (!sameSpecies(maskClass, vector)) {
115             throw AbstractSpecies.checkFailed(this, vector);
116         }
117         return (VectorMask<F>) this;
118     }
119 
120     @ForceInline
121     private <F> boolean sameSpecies(Class<? extends VectorMask<F>> maskClass, Vector<F> vector) {
122         boolean same = getClass() == maskClass;
123         assert (same == (vectorSpecies() == vector.species())) : same;
124         return same;
125     }
126 
127     @Override
128     public VectorMask<E> andNot(VectorMask<E> m) {
129         return and(m.not());
130     }
131 
132     /*package-private*/
133     static boolean anyTrueHelper(boolean[] bits) {
134         // FIXME: Maybe use toLong() != 0 here.
135         for (boolean i : bits) {
136             if (i) return true;
137         }
138         return false;
139     }
140 
141     /*package-private*/
142     static boolean allTrueHelper(boolean[] bits) {
143         // FIXME: Maybe use not().toLong() == 0 here.
144         for (boolean i : bits) {
145             if (!i) return false;
146         }
147         return true;
148     }
149 
150     /*package-private*/
151     static int trueCountHelper(boolean[] bits) {
152         int c = 0;
153         for (boolean i : bits) {
154             if (i) c++;
155         }
156         return c;
157     }
158 
159     /*package-private*/
160     static int firstTrueHelper(boolean[] bits) {
161         for (int i = 0; i < bits.length; i++) {
162             if (bits[i])  return i;
163         }
164         return bits.length;
165     }
166 
167     /*package-private*/
168     static int lastTrueHelper(boolean[] bits) {
169         for (int i = bits.length-1; i >= 0; i--) {
170             if (bits[i])  return i;
171         }
172         return -1;
173     }
174 
175     /*package-private*/
176     static long toLongHelper(boolean[] bits) {
177         long res = 0;
178         long set = 1;
179         for (int i = 0; i < bits.length; i++) {
180             res = bits[i] ? res | set : res;
181             set = set << 1;
182         }
183         return res;
184     }
185 
186     @Override
187     @ForceInline
188     public VectorMask<E> indexInRange(int offset, int limit) {
189         int vlength = length();
190         Vector<E> iota = vectorSpecies().zero().addIndex(1);
191         VectorMask<E> badMask = checkIndex0(offset, limit, iota, vlength);
192         return this.andNot(badMask);
193     }
194 
195     /*package-private*/
196     @ForceInline
197     AbstractVector<E>
198     toVectorTemplate() {
199         AbstractSpecies<E> vsp = vspecies();
200         Vector<E> zero = vsp.broadcast(0);
201         Vector<E> mone = vsp.broadcast(-1);
202         // -1 will result in the most significant bit being set in
203         // addition to some or all other lane bits.
204         // For integral types, *all* lane bits will be set.
205         // The bits for -1.0 are like {0b10111*0000*}.
206         // FIXME: Use a conversion intrinsic for this operation.
207         // https://bugs.openjdk.java.net/browse/JDK-8225740
208         return (AbstractVector<E>) zero.blend(mone, this);
209     }
210 
211     /**
212      * Test if a masked memory access at a given offset into an array
213      * of the given length will stay within the array.
214      * The per-lane offsets are iota*esize.
215      */
216     /*package-private*/
217     @ForceInline
218     void checkIndexByLane(int offset, int alength,
219                           Vector<E> iota,
220                           int esize) {
221         if (VectorIntrinsics.VECTOR_ACCESS_OOB_CHECK == 0) {
222             return;
223         }
224         // Although the specification is simple, the implementation is
225         // tricky, because the value iota*esize might possibly
226         // overflow.  So we calculate our test values as scalars,
227         // clipping to the range [-1..VLENGTH], and test them against
228         // the unscaled iota vector, whose values are in [0..VLENGTH-1].
229         int vlength = length();
230         VectorMask<E> badMask;
231         if (esize == 1) {
232             badMask = checkIndex0(offset, alength, iota, vlength);
233         } else if (offset >= 0) {
234             // Masked access to multi-byte lanes in byte array.
235             // It could be aligned anywhere.
236             int elemCount = Math.min(vlength, (alength - offset) / esize);
237             badMask = checkIndex0(0, elemCount, iota, vlength);
238         } else {
239             int clipOffset = Math.max(offset, -(vlength * esize));
240             badMask = checkIndex0(clipOffset, alength,
241                                   iota.lanewise(VectorOperators.MUL, esize),
242                                   vlength * esize);
243         }
244         badMask = badMask.and(this);
245         if (badMask.anyTrue()) {
246             int badLane = badMask.firstTrue();
247             throw ((AbstractMask<E>)badMask)
248                    .checkIndexFailed(offset, badLane, alength, esize);
249         }
250     }
251 
252     private
253     @ForceInline
254     VectorMask<E> checkIndex0(int offset, int alength,
255                               Vector<E> iota, int vlength) {
256         // An active lane is bad if its number is greater than
257         // alength-offset, since when added to offset it will step off
258         // of the end of the array.  To avoid overflow when
259         // converting, clip the comparison value to [0..vlength]
260         // inclusive.
261         int indexLimit = Math.max(0, Math.min(alength - offset, vlength));
262         VectorMask<E> badMask =
263             iota.compare(GE, iota.broadcast(indexLimit));
264         if (offset < 0) {
265             // An active lane is bad if its number is less than
266             // -offset, because when added to offset it will then
267             // address an array element at a negative index.  To avoid
268             // overflow when converting, clip the comparison value at
269             // vlength.  This specific expression works correctly even
270             // when offset is Integer.MIN_VALUE.
271             int firstGoodIndex = -Math.max(offset, -vlength);
272             VectorMask<E> badMask2 =
273                 iota.compare(LT, iota.broadcast(firstGoodIndex));
274             if (indexLimit >= vlength) {
275                 badMask = badMask2;  // 1st badMask is all true
276             } else {
277                 badMask = badMask.or(badMask2);
278             }
279         }
280         return badMask;
281     }
282 
283     private IndexOutOfBoundsException checkIndexFailed(int offset, int lane,
284                                                        int alength, int esize) {
285         String msg = String.format("Masked range check failed: "+
286                                    "vector mask %s out of bounds at "+
287                                    "index %d+%d in array of length %d",
288                                    this, offset, lane * esize, alength);
289         if (esize != 1) {
290             msg += String.format(" (each lane spans %d array elements)", esize);
291         }
292         throw new IndexOutOfBoundsException(msg);
293     }
294 
295 }