/* * Copyright (c) 2017, 2020, 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. */ package jdk.incubator.vector; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.Stable; import java.nio.ByteOrder; import java.lang.reflect.Array; import java.util.Arrays; import java.util.function.Function; import java.util.function.IntUnaryOperator; abstract class AbstractSpecies extends jdk.internal.vm.vector.VectorSupport.VectorSpecies implements VectorSpecies { @Stable final VectorShape vectorShape; @Stable final LaneType laneType; @Stable final int laneCount; @Stable final int laneCountLog2P1; @Stable final Class> vectorType; @Stable final Class> maskType; @Stable final Function> vectorFactory; @Stable final VectorShape indexShape; @Stable final int maxScale, minScale; @Stable final int vectorBitSize, vectorByteSize; AbstractSpecies(VectorShape vectorShape, LaneType laneType, Class> vectorType, Class> maskType, Function> vectorFactory) { this.vectorShape = vectorShape; this.laneType = laneType; this.vectorType = vectorType; this.maskType = maskType; this.vectorFactory = vectorFactory; // derived values: int bitSize = vectorShape.vectorBitSize(); int byteSize = bitSize / Byte.SIZE; assert(byteSize * 8 == bitSize); this.vectorBitSize = bitSize; this.vectorByteSize = byteSize; int elementSize = laneType.elementSize; this.laneCount = bitSize / elementSize; assert(laneCount > 0); // could be 1 for mono-vector (double in v64) this.laneCountLog2P1 = Integer.numberOfTrailingZeros(laneCount) + 1; // Note: The shape might be the max-shape, // if there is no vector this large. int indexBitSize = Integer.SIZE * laneCount; this.indexShape = VectorShape.forIndexBitSize(indexBitSize, elementSize); // What are the largest and smallest scale factors that, // when multiplied times the elements in [0..VLENGTH], // inclusive, do not overflow the ETYPE? int precision = laneType.elementPrecision; if (precision >= Integer.SIZE) { // No overflow possible from int*int. this.maxScale = Integer.MAX_VALUE; this.minScale = Integer.MIN_VALUE; } else { boolean isfp = (laneType.elementKind == 'F'); long x = laneCount; long maxScale = ((1L << precision)-(isfp?0:1)) / x; long minScale = (-1L << precision) / x; this.maxScale = (int) maxScale; this.minScale = (int) minScale; } } @Stable //lazy JIT constant AbstractSpecies indexSpecies; @Stable //lazy JIT constant AbstractShuffle swapBytesShuffle; @Stable //lazy JIT constant AbstractVector dummyVector; @Override @ForceInline public final int length() { return laneCount; } // Inside the implementation we use the more descriptive // term laneCount: /*package-private*/ @ForceInline final int laneCount() { return laneCount; } /*package-private*/ @ForceInline final int laneCountLog2() { return laneCountLog2P1 - 1; // subtract one from stable value } @Override @ForceInline @SuppressWarnings("unchecked") //NOT FINAL: SPECIALIZED public Class elementType() { return (Class) laneType.elementType; } // FIXME: appeal to general method (see https://bugs.openjdk.java.net/browse/JDK-6176992) // replace usages of this method and remove @ForceInline @SuppressWarnings("unchecked") //NOT FINAL: SPECIALIZED Class genericElementType() { return (Class) laneType.genericElementType; } @Override @ForceInline //NOT FINAL: SPECIALIZED public Class> vectorType() { return vectorType; } @Override @ForceInline public final Class> maskType() { return maskType; } @Override @ForceInline public final int elementSize() { return laneType.elementSize; } /*package-private*/ @ForceInline final int elementByteSize() { return laneType.elementSize / Byte.SIZE; } @Override @ForceInline public final VectorShape vectorShape() { return vectorShape; } @ForceInline /*package-private*/ final VectorShape indexShape() { return indexShape; } @Override @ForceInline public final int vectorBitSize() { return vectorBitSize; } @Override @ForceInline public final int vectorByteSize() { return vectorByteSize; } @Override @ForceInline public final int loopBound(int length) { return VectorIntrinsics.roundDown(length, laneCount); } @Override @ForceInline public final VectorMask indexInRange(int offset, int limit) { return maskAll(true).indexInRange(offset, limit); } @Override @ForceInline public final VectorSpecies withLanes(Class newType) { return withLanes(LaneType.of(newType)).check(newType); } @ForceInline /*package-private*/ final AbstractSpecies withLanes(LaneType newType) { if (newType == laneType) return this; return findSpecies(newType, vectorShape); } @ForceInline /*package-private*/ AbstractSpecies asIntegral() { return withLanes(laneType.asIntegral()); } @ForceInline /*package-private*/ AbstractSpecies asFloating() { return withLanes(laneType.asFloating()); } @Override @ForceInline @SuppressWarnings("unchecked") public final VectorSpecies withShape(VectorShape newShape) { if (newShape == vectorShape) return this; return (VectorSpecies) findSpecies(laneType, newShape); } @ForceInline /*package-private*/ AbstractSpecies indexSpecies() { // This JITs to a constant value: AbstractSpecies sp = indexSpecies; if (sp != null) return sp; return indexSpecies = findSpecies(LaneType.INT, indexShape).check0(int.class); } @ForceInline /*package-private*/ @SuppressWarnings("unchecked") AbstractSpecies byteSpecies() { // This JITs to a constant value: return (AbstractSpecies) withLanes(LaneType.BYTE); } @ForceInline /*package-private*/ AbstractShuffle swapBytesShuffle() { // This JITs to a constant value: AbstractShuffle sh = swapBytesShuffle; if (sh != null) return sh; return swapBytesShuffle = makeSwapBytesShuffle(); } private AbstractShuffle makeSwapBytesShuffle() { int vbytes = vectorByteSize(); int lbytes = elementByteSize(); int[] sourceIndexes = new int[vbytes]; for (int i = 0; i < vbytes; i++) { sourceIndexes[i] = i ^ (lbytes-1); } return (AbstractShuffle) VectorShuffle.fromValues(byteSpecies(), sourceIndexes); } /*package-private*/ abstract Vector fromIntValues(int[] values); /** * Do not use a dummy except to call methods on it when you don't * care about the lane values. The main benefit of it is to * populate the type profile, which then allows the JIT to derive * constant values for dummy.species(), the current species, and * then for all of its attributes: ETYPE, VLENGTH, VSHAPE, etc. */ @ForceInline /*package-private*/ AbstractVector dummyVector() { // This JITs to a constant value: AbstractVector dummy = dummyVector; if (dummy != null) return dummy; // The rest of this computation is probably not JIT-ted. return makeDummyVector(); } private AbstractVector makeDummyVector() { Object za = Array.newInstance(elementType(), laneCount); return dummyVector = vectorFactory.apply(za); // This is the only use of vectorFactory. // All other factory requests are routed // through the dummy vector. } /** * Build a mask by directly calling its constructor. * It is an error if the array is aliased elsewhere. */ @ForceInline /*package-private*/ AbstractMask maskFactory(boolean[] bits) { return dummyVector().maskFromArray(bits); } public final @Override @ForceInline VectorShuffle shuffleFromArray(int[] sourceIndexes, int offset) { return dummyVector().shuffleFromArray(sourceIndexes, offset); } public final @Override @ForceInline VectorShuffle shuffleFromValues(int... sourceIndexes) { return dummyVector().shuffleFromArray(sourceIndexes, 0); } public final @Override @ForceInline VectorShuffle shuffleFromOp(IntUnaryOperator fn) { return dummyVector().shuffleFromOp(fn); } public final @Override @ForceInline VectorShuffle iotaShuffle(int start, int step, boolean wrap) { AbstractShuffle res; if (start == 0 && step == 1) return dummyVector().iotaShuffle(); else return dummyVector().iotaShuffle(start, step, wrap); } @ForceInline @Override public final Vector fromByteArray(byte[] a, int offset, ByteOrder bo) { return dummyVector() .fromByteArray0(a, offset) .maybeSwap(bo); } @Override public VectorMask loadMask(boolean[] bits, int offset) { return VectorMask.fromArray(this, bits, offset); } // Define zero and iota when we know the ETYPE and VSHAPE. public abstract AbstractVector zero(); /*package-private*/ abstract AbstractVector iota(); // Constructing vectors from raw bits. /*package-private*/ abstract long longToElementBits(long e); /*package-private*/ abstract AbstractVector broadcastBits(long bits); /*package-private*/ final IllegalArgumentException badElementBits(long iv, Object cv) { String msg = String.format("Vector creation failed: "+ "value %s cannot be represented in ETYPE %s"+ "; result of cast is %s", iv, elementType(), cv); return new IllegalArgumentException(msg); } /*package-private*/ static final IllegalArgumentException badArrayBits(Object iv, boolean isInt, long cv) { String msg = String.format("Array creation failed: "+ "lane value %s cannot be represented in %s"+ "; result of cast is %s", iv, (isInt ? "int" : "long"), cv); return new IllegalArgumentException(msg); } /*package-private*/ Object iotaArray() { // Create an iota array. It's OK if this is really slow, // because it happens only once per species. Object ia = Array.newInstance(laneType.elementType, laneCount); assert(ia.getClass() == laneType.arrayType); checkValue(laneCount-1); // worst case for (int i = 0; i < laneCount; i++) { if ((byte)i == i) Array.setByte(ia, i, (byte)i); else if ((short)i == i) Array.setShort(ia, i, (short)i); else Array.setInt(ia, i, i); assert(Array.getDouble(ia, i) == i); } return ia; } @ForceInline /*package-private*/ void checkScale(int scale) { if (scale > 0) { if (scale <= maxScale) return; } else { // scale <= 0 if (scale >= minScale) return; } throw checkScaleFailed(scale); } private IllegalArgumentException checkScaleFailed(int scale) { String msg = String.format("%s: cannot represent VLENGTH*%d", this, scale); return new IllegalArgumentException(msg); } /*package-private*/ interface RVOp { long apply(int i); // supply raw element bits } /*package-private*/ abstract AbstractVector rvOp(RVOp f); /*package-private*/ interface FOpm { boolean apply(int i); } AbstractMask opm(FOpm f) { boolean[] res = new boolean[laneCount]; for (int i = 0; i < res.length; i++) { res[i] = f.apply(i); } return dummyVector().maskFromArray(res); } @Override @ForceInline public final VectorSpecies check(Class elementType) { return check0(elementType); } @ForceInline @SuppressWarnings("unchecked") /*package-private*/ final AbstractSpecies check0(Class elementType) { if (elementType != this.elementType()) { throw AbstractSpecies.checkFailed(this, elementType); } return (AbstractSpecies) this; } @ForceInline /*package-private*/ AbstractSpecies check(LaneType laneType) { if (laneType != this.laneType) { throw AbstractSpecies.checkFailed(this, laneType); } return this; } @Override @ForceInline public int partLimit(VectorSpecies toSpecies, boolean lanewise) { AbstractSpecies rsp = (AbstractSpecies) toSpecies; int inSizeLog2 = this.vectorShape.vectorBitSizeLog2; int outSizeLog2 = rsp.vectorShape.vectorBitSizeLog2; if (lanewise) { inSizeLog2 += (rsp.laneType.elementSizeLog2 - this.laneType.elementSizeLog2); } int diff = (inSizeLog2 - outSizeLog2); // Let's try a branch-free version of this. int sign = (diff >> -1); //d = Math.abs(diff); //d = (sign == 0 ? diff : sign == -1 ? 1 + ~diff); int d = (diff ^ sign) - sign; // Compute sgn(diff) << abs(diff), but replace 1 by 0. return ((sign | 1) << d) & ~1; } /** * Helper for throwing CheckCastExceptions, * used by the various Vector*.check(*) methods. */ /*package-private*/ static ClassCastException checkFailed(Object what, Object required) { // Find a species for the thing that's failing. AbstractSpecies whatSpecies = null; String where; if (what instanceof VectorSpecies) { whatSpecies = (AbstractSpecies) what; where = whatSpecies.toString(); } else if (what instanceof Vector) { whatSpecies = (AbstractSpecies) ((Vector) what).species(); where = "a Vector<"+whatSpecies.genericElementType()+">"; } else if (what instanceof VectorMask) { whatSpecies = (AbstractSpecies) ((VectorMask) what).vectorSpecies(); where = "a VectorMask<"+whatSpecies.genericElementType()+">"; } else if (what instanceof VectorShuffle) { whatSpecies = (AbstractSpecies) ((VectorShuffle) what).vectorSpecies(); where = "a VectorShuffle<"+whatSpecies.genericElementType()+">"; } else { where = what.toString(); } Object found = null; if (whatSpecies != null) { if (required instanceof VectorSpecies) { // required is a VectorSpecies; found the wrong species found = whatSpecies; } else if (required instanceof Vector) { // same VectorSpecies required; found the wrong species found = whatSpecies; required = ((Vector)required).species(); } else if (required instanceof Class) { // required is a Class; found the wrong ETYPE Class requiredClass = (Class) required; LaneType requiredType = LaneType.forClassOrNull(requiredClass); found = whatSpecies.elementType(); if (requiredType == null) { required = required + " (not a valid lane type)"; } else if (!requiredClass.isPrimitive()) { required = required + " (should be " + requiredType + ")"; } } else if (required instanceof LaneType) { // required is a LaneType; found the wrong ETYPE required = ((LaneType) required).elementType; found = whatSpecies.elementType(); } else if (required instanceof Integer) { // required is a length; species has wrong VLENGTH required = required + " lanes"; found = whatSpecies.length(); } } if (found == null) found = "bad value"; String msg = where+": required "+required+" but found "+found; return new ClassCastException(msg); } private static final @Stable AbstractSpecies[][] CACHES = new AbstractSpecies[LaneType.SK_LIMIT][VectorShape.SK_LIMIT]; // Helper functions for finding species: /*package-private*/ @ForceInline static AbstractSpecies findSpecies(Class elementType, LaneType laneType, VectorShape shape) { assert(elementType == laneType.elementType); return findSpecies(laneType, shape).check0(elementType); } /*package-private*/ @ForceInline static AbstractSpecies findSpecies(LaneType laneType, VectorShape shape) { // The JIT can see into this cache. // Therefore it is useful to arrange for constant // arguments to this method. If the cache // is full when the JIT runs, the cache item becomes // a compile-time constant. And then all the @Stable // fields of the AbstractSpecies are also constants. AbstractSpecies s = CACHES[laneType.switchKey][shape.switchKey]; if (s != null) return s; return computeSpecies(laneType, shape); } private static AbstractSpecies computeSpecies(LaneType laneType, VectorShape shape) { AbstractSpecies s = null; // enum-switches don't optimize properly JDK-8161245 switch (laneType.switchKey) { case LaneType.SK_FLOAT: s = FloatVector.species(shape); break; case LaneType.SK_DOUBLE: s = DoubleVector.species(shape); break; case LaneType.SK_BYTE: s = ByteVector.species(shape); break; case LaneType.SK_SHORT: s = ShortVector.species(shape); break; case LaneType.SK_INT: s = IntVector.species(shape); break; case LaneType.SK_LONG: s = LongVector.species(shape); break; } if (s == null) { // NOTE: The result of this method is guaranteed to be // non-null. Later calls to ".check" also ensure this. // If this method hits a NPE, it is because a helper // method EVector.species() has returned a null value, and // that is because a SPECIES_X static constant has not yet // been initialized. And that, in turn, is because // somebody is calling this method way too early during // bootstrapping. throw new AssertionError("bootstrap problem"); } assert(s.laneType == laneType) : s + "!=" + laneType; assert(s.vectorShape == shape) : s + "!=" + shape; CACHES[laneType.switchKey][shape.switchKey] = s; return s; } @Override public final String toString() { return "Species["+laneType+", "+laneCount+", "+vectorShape+"]"; } @Override public final boolean equals(Object obj) { if (obj instanceof AbstractSpecies) { AbstractSpecies that = (AbstractSpecies) obj; return (this.laneType == that.laneType && this.laneCount == that.laneCount && this.vectorShape == that.vectorShape); } return this == obj; } /** * Returns a hash code value for the shuffle, * based on the lane source indexes and the vector species. * * @return a hash code value for this shuffle */ @Override public final int hashCode() { int[] a = { laneType.ordinal(), laneCount, vectorShape.ordinal() }; return Arrays.hashCode(a); } }