/*
* Copyright (c) 2019, 2021, 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.vector.VectorSupport;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.function.IntUnaryOperator;
import static jdk.incubator.vector.VectorOperators.*;
@SuppressWarnings("cast")
abstract class AbstractVector<E> extends Vector<E> {
The order of vector bytes when stored in natural,
array elements of the same lane type.
This is the also the behavior of the
VectorSupport load/store instructions.
If these instructions gain the capability to do
byte swapping on the fly, add a bit to those
instructions, but let this polarity be the
"neutral" or "default" setting of the bit.
/**
* The order of vector bytes when stored in natural,
* array elements of the same lane type.
* This is the also the behavior of the
* VectorSupport load/store instructions.
* If these instructions gain the capability to do
* byte swapping on the fly, add a bit to those
* instructions, but let this polarity be the
* "neutral" or "default" setting of the bit.
*/
/*package-private*/
static final ByteOrder NATIVE_ENDIAN = ByteOrder.nativeOrder();
The order of vector bytes as stored in the register
file. This becomes visible with the asRaw[Type]Vector
operations, which convert between the internal byte-wise
representation and the typed lane-wise representation.
It is very possible for a platform to have big-endian
memory layout and little-endian register layout,
so this is a different setting from NATIVE_ENDIAN.
In fact, both Intel and ARM use LE conventions here.
Future work may be needed for resolutely BE platforms.
/**
* The order of vector bytes as stored in the register
* file. This becomes visible with the asRaw[Type]Vector
* operations, which convert between the internal byte-wise
* representation and the typed lane-wise representation.
* It is very possible for a platform to have big-endian
* memory layout and little-endian register layout,
* so this is a different setting from NATIVE_ENDIAN.
* In fact, both Intel and ARM use LE conventions here.
* Future work may be needed for resolutely BE platforms.
*/
/*package-private*/
static final ByteOrder REGISTER_ENDIAN = ByteOrder.LITTLE_ENDIAN;
/*package-private*/
AbstractVector(Object bits) {
super(bits);
}
// Extractors
/*package-private*/
abstract AbstractSpecies<E> vspecies();
@Override
@ForceInline
public final VectorSpecies<E> species() {
return vspecies();
}
// Something to make types match up better:
@Override
@ForceInline
public final
<F> Vector<F> check(VectorSpecies<F> species) {
return check0(species);
}
@ForceInline
@SuppressWarnings("unchecked")
/*package-private*/ final
<F> AbstractVector<F> check0(VectorSpecies<F> species) {
if (!sameSpecies(species)) {
throw AbstractSpecies.checkFailed(this, species);
}
return (AbstractVector<F>) this;
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public final
<F> Vector<F> check(Class<F> elementType) {
return check0(elementType);
}
@ForceInline
@SuppressWarnings("unchecked")
/*package-private*/ final
<F> AbstractVector<F> check0(Class<F> elementType) {
if (this.elementType() != elementType) {
throw AbstractSpecies.checkFailed(this, elementType);
}
return (AbstractVector<F>) this;
}
@ForceInline
@SuppressWarnings("unchecked")
/*package-private*/ final
<F> AbstractVector<F> check(Vector<F> other) {
if (!sameSpecies(other)) {
throw AbstractSpecies.checkFailed(this, other);
}
return (AbstractVector<F>) this;
}
@ForceInline
private boolean sameSpecies(Vector<?> other) {
// It's simpler and faster to do a class check.
boolean same = (this.getClass() == other.getClass());
// Make sure it works, too!
assert(same == (this.species() == other.species())) : same;
return same;
}
@ForceInline
private boolean sameSpecies(VectorSpecies<?> species) {
// It's simpler and faster to do a class check,
// even if you have to load a dummy vector.
AbstractVector<?> other = ((AbstractSpecies<?>)species).dummyVector();
boolean same = (this.getClass() == other.getClass());
// Make sure it works, too!
assert(same == (this.species() == species)) : same;
return same;
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public final VectorMask<E> maskAll(boolean bit) {
return species().maskAll(bit);
}
// Make myself into a vector of the same shape
// and same information content but different lane type
/*package-private*/
abstract AbstractVector<?> asVectorRaw(LaneType laneType);
// Make myself into a byte vector of the same shape
/*package-private*/
abstract ByteVector asByteVectorRaw();
/*package-private*/
@ForceInline
final AbstractVector<?> asVectorRawTemplate(LaneType laneType) {
// NOTE: This assumes that convert0('X')
// respects REGISTER_ENDIAN order.
return convert0('X', vspecies().withLanes(laneType));
}
/*package-private*/
@ForceInline
ByteVector asByteVectorRawTemplate() {
return (ByteVector) asVectorRawTemplate(LaneType.BYTE);
}
abstract AbstractMask<E> maskFromArray(boolean[] bits);
abstract AbstractShuffle<E> iotaShuffle();
abstract AbstractShuffle<E> iotaShuffle(int start, int step, boolean wrap);
/*do not alias this byte array*/
abstract AbstractShuffle<E> shuffleFromBytes(byte[] reorder);
abstract AbstractShuffle<E> shuffleFromArray(int[] indexes, int i);
abstract AbstractShuffle<E> shuffleFromOp(IntUnaryOperator fn);
/*package-private*/
abstract AbstractVector<E> fromByteArray0(byte[] a, int offset);
/*package-private*/
abstract AbstractVector<E> maybeSwap(ByteOrder bo);
/*package-private*/
@ForceInline
VectorShuffle<Byte> swapBytesShuffle() {
return vspecies().swapBytesShuffle();
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public ShortVector reinterpretAsShorts() {
return (ShortVector) asVectorRaw(LaneType.SHORT);
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public IntVector reinterpretAsInts() {
return (IntVector) asVectorRaw(LaneType.INT);
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public LongVector reinterpretAsLongs() {
return (LongVector) asVectorRaw(LaneType.LONG);
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public FloatVector reinterpretAsFloats() {
return (FloatVector) asVectorRaw(LaneType.FLOAT);
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public DoubleVector reinterpretAsDoubles() {
return (DoubleVector) asVectorRaw(LaneType.DOUBLE);
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public final <F>
Vector<F> convert(Conversion<E,F> conv, int part) {
// Shape invariance is simple to implement.
// It's part of the API because shape invariance
// is the default mode of operation, and shape
// shifting operations must advertise themselves.
ConversionImpl<E,F> c = (ConversionImpl<E,F>) conv;
@SuppressWarnings("unchecked")
VectorSpecies<F> rsp = (VectorSpecies<F>)
vspecies().withLanes(c.range());
return convertShape(conv, rsp, part);
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public final <F>
Vector<F> castShape(VectorSpecies<F> toSpecies, int part) {
// This is an odd mix of shape conversion plus
// lanewise conversions. It seems to be useful
// sometimes as a shorthand, though maybe we
// can drop it.
AbstractSpecies<E> vsp = vspecies();
AbstractSpecies<F> rsp = (AbstractSpecies<F>) toSpecies;
@SuppressWarnings("unchecked")
ConversionImpl<E,F> c = (ConversionImpl<E,F>)
ConversionImpl.ofCast(vsp.laneType, rsp.laneType);
return convertShape(c, rsp, part);
}
{@inheritDoc}
/**
* {@inheritDoc} <!--workaround-->
*/
@Override
@ForceInline
public abstract <F>
Vector<F> convertShape(Conversion<E,F> conv, VectorSpecies<F> rsp, int part);
This is the template for Vector::reinterpretShape, to be
specialized by each distinct vector class.
/**
* This is the template for Vector::reinterpretShape, to be
* specialized by each distinct vector class.
*/
/*package-private*/
@ForceInline
final <F>
AbstractVector<F> reinterpretShapeTemplate(VectorSpecies<F> toSpecies, int part) {
AbstractSpecies<F> rsp = (AbstractSpecies<F>) toSpecies;
AbstractSpecies<E> vsp = vspecies();
if (part == 0) {
// Works the same for in-place, expand, or contract.
return convert0('X', rsp);
} else {
int origin = shapeChangeOrigin(vsp, rsp, false, part);
//System.out.println("*** origin = "+origin+", part = "+part+", reinterpret");
if (part > 0) { // Expansion: slice first then cast.
return slice(origin).convert0('X', rsp);
} else { // Contraction: cast first then unslice.
return rsp.zero().slice(rsp.laneCount() - origin,
convert0('X', rsp));
}
}
}
@Override
public abstract AbstractVector<E> slice(int origin, Vector<E> v1);
@Override
public abstract AbstractVector<E> slice(int origin);
This is the template for Vector::convertShape, to be
specialized by each distinct vector class.
/**
* This is the template for Vector::convertShape, to be
* specialized by each distinct vector class.
*/
/*package-private*/
@ForceInline
final <F>
AbstractVector<F> convertShapeTemplate(Conversion<E,F> conv, VectorSpecies<F> toSpecies, int part) {
ConversionImpl<E,F> c = (ConversionImpl<E,F>) conv;
AbstractSpecies<F> rsp = (AbstractSpecies<F>) toSpecies;
AbstractSpecies<E> vsp = vspecies();
char kind = c.kind();
switch (kind) {
case 'C': // Regular cast conversion, known to the JIT.
break;
case 'I': // Identity conversion => reinterpret.
assert(c.sizeChangeLog2() == 0);
kind = 'X';
break;
case 'Z': // Lane-wise expansion with zero padding.
assert(c.sizeChangeLog2() > 0);
assert(c.range().elementKind == 'I');
break;
case 'R': // Lane-wise reinterpret conversion.
if (c.sizeChangeLog2() != 0) {
kind = 'Z'; // some goofy stuff here
break;
}
kind = 'X'; // No size change => reinterpret whole vector
break;
default:
throw new AssertionError(c);
}
vsp.check(c.domain()); // apply dynamic check to conv
rsp.check(c.range()); // apply dynamic check to conv
if (part == 0) {
// Works the same for in-place, expand, or contract.
return convert0(kind, rsp);
} else {
int origin = shapeChangeOrigin(vsp, rsp, true, part);
//System.out.println("*** origin = "+origin+", part = "+part+", lanewise");
if (part > 0) { // Expansion: slice first then cast.
return slice(origin).convert0(kind, rsp);
} else { // Contraction: cast first then unslice.
return rsp.zero().slice(rsp.laneCount() - origin,
convert0(kind, rsp));
}
}
}
Check a part number and return it multiplied by the appropriate
block factor to yield the origin of the operand block, as a
lane number. For expansions the origin is reckoned in the
domain vector, since the domain vector has too much information
and must be sliced. For contractions the origin is reckoned in
the range vector, since the range vector has too many lanes and
the result must be unsliced at the same position as the inverse
expansion. If the conversion is lanewise, then lane sizes may
be changing as well. This affects the logical size of the
result, and so the domain size is multiplied or divided by the
lane size change.
/**
* Check a part number and return it multiplied by the appropriate
* block factor to yield the origin of the operand block, as a
* lane number. For expansions the origin is reckoned in the
* domain vector, since the domain vector has too much information
* and must be sliced. For contractions the origin is reckoned in
* the range vector, since the range vector has too many lanes and
* the result must be unsliced at the same position as the inverse
* expansion. If the conversion is lanewise, then lane sizes may
* be changing as well. This affects the logical size of the
* result, and so the domain size is multiplied or divided by the
* lane size change.
*/
/*package-private*/
@ForceInline
static
int shapeChangeOrigin(AbstractSpecies<?> dsp,
AbstractSpecies<?> rsp,
boolean lanewise,
int part) {
int domSizeLog2 = dsp.vectorShape.vectorBitSizeLog2;
int phySizeLog2 = rsp.vectorShape.vectorBitSizeLog2;
int laneChangeLog2 = 0;
if (lanewise) {
laneChangeLog2 = (rsp.laneType.elementSizeLog2 -
dsp.laneType.elementSizeLog2);
}
int resSizeLog2 = domSizeLog2 + laneChangeLog2;
// resSizeLog2 = 0 => 1-lane vector shrinking to 1-byte lane-size
// resSizeLog2 < 0 => small vector shrinking by more than a lane-size
assert(resSizeLog2 >= 0);
// Expansion ratio: expansionLog2 = resSizeLog2 - phySizeLog2;
if (!partInRange(resSizeLog2, phySizeLog2, part)) {
// fall through...
} else if (resSizeLog2 > phySizeLog2) {
// Expansion by M means we must slice a block from the domain.
// What is that block size? It is 1/M of the domain.
// Let's compute the log2 of that block size, as 's'.
//s = (dsp.laneCountLog2() - expansionLog2);
//s = ((domSizeLog2 - dsp.laneType.elementSizeLog2) - expansionLog2);
//s = (domSizeLog2 - expansionLog2 - dsp.laneType.elementSizeLog2);
int s = phySizeLog2 - laneChangeLog2 - dsp.laneType.elementSizeLog2;
// Scale the part number by the input block size, in input lanes.
if ((s & 31) == s) // sanity check
return part << s;
} else {
// Contraction by M means we must drop a block into the range.
// What is that block size? It is 1/M of the range.
// Let's compute the log2 of that block size, as 's'.
//s = (rsp.laneCountLog2() + expansionLog2);
//s = ((phySizeLog2 - rsp.laneType.elementSizeLog2) + expansionLog2);
//s = (phySizeLog2 + expansionLog2 - rsp.laneType.elementSizeLog2);
int s = resSizeLog2 - rsp.laneType.elementSizeLog2;
// Scale the part number by the output block size, in output lanes.
if ((s & 31) == s) // sanity check
return -part << s;
}
throw wrongPart(dsp, rsp, lanewise, part);
}
@ForceInline
private static boolean partInRange(int resSizeLog2, int phySizeLog2, int part) {
// Let's try a branch-free version of this.
int diff = (resSizeLog2 - phySizeLog2);
int sign = (diff >> -1);
//d = Math.abs(diff);
//d = (sign == 0 ? diff : sign == -1 ? 1 + ~diff);
int d = (diff ^ sign) - sign;
assert(d == Math.abs(diff) && d <= 16); // let's not go crazy here
//p = part * sign;
int p = (part ^ sign) - sign;
// z = sign == 0 ? 0<=part<(1<<d), == (part & (-1 << d)) == 0
// z = sign == -1 ? 0<=-part<(1<<d), == (-part & (-1 << d)) == 0
boolean z = (p & (-1 << d)) == 0;
assert(z == partInRangeSlow(resSizeLog2, phySizeLog2, part)) : z;
return z;
}
private static boolean partInRangeSlow(int resSizeLog2, int phySizeLog2, int part) {
if (resSizeLog2 > phySizeLog2) { // expansion
int limit = 1 << (resSizeLog2 - phySizeLog2);
return part >= 0 && part < limit;
} else if (resSizeLog2 < phySizeLog2) { // contraction
int limit = 1 << (phySizeLog2 - resSizeLog2);
return part > -limit && part <= 0;
} else {
return (part == 0);
}
}
private static
ArrayIndexOutOfBoundsException
wrongPart(AbstractSpecies<?> dsp,
AbstractSpecies<?> rsp,
boolean lanewise,
int part) {
String laneChange = "";
String converting = "converting";
int dsize = dsp.elementSize(), rsize = rsp.elementSize();
if (!lanewise) {
converting = "reinterpreting";
} else if (dsize < rsize) {
laneChange = String.format(" (lanes are expanding by %d)",
rsize / dsize);
} else if (dsize > rsize) {
laneChange = String.format(" (lanes are contracting by %d)",
dsize / rsize);
}
String msg = String.format("bad part number %d %s %s -> %s%s",
part, converting, dsp, rsp, laneChange);
return new ArrayIndexOutOfBoundsException(msg);
}
/*package-private*/
ArithmeticException divZeroException() {
throw new ArithmeticException("zero vector lane in dividend "+this);
}
Helper function for all sorts of byte-wise reinterpretation casts.
This function kicks in after intrinsic failure.
/**
* Helper function for all sorts of byte-wise reinterpretation casts.
* This function kicks in after intrinsic failure.
*/
/*package-private*/
@ForceInline
final <F>
AbstractVector<F> defaultReinterpret(AbstractSpecies<F> rsp) {
int blen = Math.max(this.bitSize(), rsp.vectorBitSize()) / Byte.SIZE;
ByteOrder bo = ByteOrder.nativeOrder();
ByteBuffer bb = ByteBuffer.allocate(blen);
this.intoByteBuffer(bb, 0, bo);
VectorMask<F> m = rsp.maskAll(true);
// enum-switches don't optimize properly JDK-8161245
switch (rsp.laneType.switchKey) {
case LaneType.SK_BYTE:
return ByteVector.fromByteBuffer(rsp.check(byte.class), bb, 0, bo, m.check(byte.class)).check0(rsp);
case LaneType.SK_SHORT:
return ShortVector.fromByteBuffer(rsp.check(short.class), bb, 0, bo, m.check(short.class)).check0(rsp);
case LaneType.SK_INT:
return IntVector.fromByteBuffer(rsp.check(int.class), bb, 0, bo, m.check(int.class)).check0(rsp);
case LaneType.SK_LONG:
return LongVector.fromByteBuffer(rsp.check(long.class), bb, 0, bo, m.check(long.class)).check0(rsp);
case LaneType.SK_FLOAT:
return FloatVector.fromByteBuffer(rsp.check(float.class), bb, 0, bo, m.check(float.class)).check0(rsp);
case LaneType.SK_DOUBLE:
return DoubleVector.fromByteBuffer(rsp.check(double.class), bb, 0, bo, m.check(double.class)).check0(rsp);
default:
throw new AssertionError(rsp.toString());
}
}
Helper function for all sorts of lane-wise conversions.
This function kicks in after intrinsic failure.
/**
* Helper function for all sorts of lane-wise conversions.
* This function kicks in after intrinsic failure.
*/
/*package-private*/
@ForceInline
final <F>
AbstractVector<F> defaultCast(AbstractSpecies<F> dsp) {
int rlength = dsp.laneCount;
if (vspecies().laneType.elementKind == 'F') {
// Buffer input values in a double array.
double[] lanes = toDoubleArray();
int limit = Math.min(lanes.length, rlength);
// enum-switches don't optimize properly JDK-8161245
switch (dsp.laneType.switchKey) {
case LaneType.SK_BYTE: {
byte[] a = new byte[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (byte) lanes[i];
}
return ByteVector.fromArray(dsp.check(byte.class), a, 0).check0(dsp);
}
case LaneType.SK_SHORT: {
short[] a = new short[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (short) lanes[i];
}
return ShortVector.fromArray(dsp.check(short.class), a, 0).check0(dsp);
}
case LaneType.SK_INT: {
int[] a = new int[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (int) lanes[i];
}
return IntVector.fromArray(dsp.check(int.class), a, 0).check0(dsp);
}
case LaneType.SK_LONG: {
long[] a = new long[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (long) lanes[i];
}
return LongVector.fromArray(dsp.check(long.class), a, 0).check0(dsp);
}
case LaneType.SK_FLOAT: {
float[] a = new float[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (float) lanes[i];
}
return FloatVector.fromArray(dsp.check(float.class), a, 0).check0(dsp);
}
case LaneType.SK_DOUBLE: {
double[] a = new double[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (double) lanes[i];
}
return DoubleVector.fromArray(dsp.check(double.class), a, 0).check0(dsp);
}
default: break;
}
} else {
// Buffer input values in a long array.
long[] lanes = toLongArray();
int limit = Math.min(lanes.length, rlength);
// enum-switches don't optimize properly JDK-8161245
switch (dsp.laneType.switchKey) {
case LaneType.SK_BYTE: {
byte[] a = new byte[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (byte) lanes[i];
}
return ByteVector.fromArray(dsp.check(byte.class), a, 0).check0(dsp);
}
case LaneType.SK_SHORT: {
short[] a = new short[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (short) lanes[i];
}
return ShortVector.fromArray(dsp.check(short.class), a, 0).check0(dsp);
}
case LaneType.SK_INT: {
int[] a = new int[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (int) lanes[i];
}
return IntVector.fromArray(dsp.check(int.class), a, 0).check0(dsp);
}
case LaneType.SK_LONG: {
long[] a = new long[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (long) lanes[i];
}
return LongVector.fromArray(dsp.check(long.class), a, 0).check0(dsp);
}
case LaneType.SK_FLOAT: {
float[] a = new float[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (float) lanes[i];
}
return FloatVector.fromArray(dsp.check(float.class), a, 0).check0(dsp);
}
case LaneType.SK_DOUBLE: {
double[] a = new double[rlength];
for (int i = 0; i < limit; i++) {
a[i] = (double) lanes[i];
}
return DoubleVector.fromArray(dsp.check(double.class), a, 0).check0(dsp);
}
default: break;
}
}
throw new AssertionError();
}
// Constant-folded access to conversion intrinsics:
Dispatch on conversion kind and target species.
The code of this is arranged to fold up if the
vector class is constant and the target species
is also constant. This is often the case.
Residual non-folded code may also perform acceptably
in some cases due to type profiling, especially
of rvtype. If only one shape is being used,
the profiling of rvtype should help speculatively
fold the code even when the target species is
not a constant.
/**
* Dispatch on conversion kind and target species.
* The code of this is arranged to fold up if the
* vector class is constant and the target species
* is also constant. This is often the case.
* Residual non-folded code may also perform acceptably
* in some cases due to type profiling, especially
* of rvtype. If only one shape is being used,
* the profiling of rvtype should help speculatively
* fold the code even when the target species is
* not a constant.
*/
/*package-private*/
@ForceInline
final <F>
AbstractVector<F> convert0(char kind, AbstractSpecies<F> rsp) {
// Derive some JIT-time constants:
Class<?> etype; // fill in after switch (constant)
int vlength; // fill in after switch (mark type profile?)
Class<?> rvtype; // fill in after switch (mark type profile)
Class<?> rtype;
int rlength;
switch (kind) {
case 'Z': // lane-wise size change, maybe with sign clip
// Maybe this should be an intrinsic also.
AbstractSpecies<?> rspi = rsp.asIntegral();
AbstractVector<?> bitv = resizeLanes0(this, rspi);
return (rspi == rsp ? bitv.check0(rsp) : bitv.convert0('X', rsp));
case 'C': // lane-wise cast (but not identity)
rtype = rsp.elementType();
rlength = rsp.laneCount();
etype = this.elementType(); // (profile)
vlength = this.length(); // (profile)
rvtype = rsp.dummyVector().getClass(); // (profile)
return VectorSupport.convert(VectorSupport.VECTOR_OP_CAST,
this.getClass(), etype, vlength,
rvtype, rtype, rlength,
this, rsp,
AbstractVector::defaultCast);
case 'X': // reinterpret cast, not lane-wise if lane sizes differ
rtype = rsp.elementType();
rlength = rsp.laneCount();
etype = this.elementType(); // (profile)
vlength = this.length(); // (profile)
rvtype = rsp.dummyVector().getClass(); // (profile)
return VectorSupport.convert(VectorSupport.VECTOR_OP_REINTERPRET,
this.getClass(), etype, vlength,
rvtype, rtype, rlength,
this, rsp,
AbstractVector::defaultReinterpret);
}
throw new AssertionError();
}
@ForceInline
private static <F>
AbstractVector<F>
resizeLanes0(AbstractVector<?> v, AbstractSpecies<F> rspi) {
AbstractSpecies<?> dsp = v.vspecies();
int sizeChange = rspi.elementSize() - dsp.elementSize();
AbstractSpecies<?> dspi = dsp.asIntegral();
if (dspi != dsp) v = v.convert0('R', dspi);
if (sizeChange <= 0) { // clip in place
return v.convert0('C', rspi);
}
// extend in place, but remove unwanted sign extension
long mask = -1L >>> sizeChange;
return (AbstractVector<F>)
v.convert0('C', rspi)
.lanewise(AND, rspi.broadcast(mask));
}
// Byte buffer wrappers.
static ByteBuffer wrapper(ByteBuffer bb, ByteOrder bo) {
return bb.duplicate().order(bo);
}
static ByteBuffer wrapper(byte[] a, ByteOrder bo) {
return ByteBuffer.wrap(a).order(bo);
}
static {
// Recode uses of VectorSupport.reinterpret if this assertion fails:
assert(REGISTER_ENDIAN == ByteOrder.LITTLE_ENDIAN);
}
}