diff --git a/src/coreclr/src/jit/emit.h b/src/coreclr/src/jit/emit.h
index 1ded601fadb56..6574fef7af862 100644
--- a/src/coreclr/src/jit/emit.h
+++ b/src/coreclr/src/jit/emit.h
@@ -1222,9 +1222,11 @@ class emitter
#define PERFSCORE_THROUGHPUT_ILLEGAL -1024.0f
-#define PERFSCORE_THROUGHPUT_4X 0.25f // Fastest - Quad issue
-#define PERFSCORE_THROUGHPUT_3X (1.0f / 3.0f) // Faster - Three issue
-#define PERFSCORE_THROUGHPUT_2X 0.5f // Faster - Dual issue
+#define PERFSCORE_THROUGHPUT_6X (1.0f / 6.0f) // Hextuple issue
+#define PERFSCORE_THROUGHPUT_5X 0.20f // Pentuple issue
+#define PERFSCORE_THROUGHPUT_4X 0.25f // Quad issue
+#define PERFSCORE_THROUGHPUT_3X (1.0f / 3.0f) // Three issue
+#define PERFSCORE_THROUGHPUT_2X 0.5f // Dual issue
#define PERFSCORE_THROUGHPUT_1C 1.0f // Single Issue
diff --git a/src/coreclr/src/jit/emitarm64.cpp b/src/coreclr/src/jit/emitarm64.cpp
index 9ebf3de2f215a..93d83cac617e4 100644
--- a/src/coreclr/src/jit/emitarm64.cpp
+++ b/src/coreclr/src/jit/emitarm64.cpp
@@ -817,6 +817,22 @@ void emitter::emitInsSanityCheck(instrDesc* id)
break;
case IF_DV_3C: // DV_3C .Q.........mmmmm ......nnnnnddddd Vd Vn Vm (vector)
+ switch (id->idIns())
+ {
+ case INS_tbl:
+ case INS_tbl_2regs:
+ case INS_tbl_3regs:
+ case INS_tbl_4regs:
+ case INS_tbx:
+ case INS_tbx_2regs:
+ case INS_tbx_3regs:
+ case INS_tbx_4regs:
+ elemsize = optGetElemsize(id->idInsOpt());
+ assert(elemsize == EA_1BYTE);
+ break;
+ default:
+ break;
+ }
assert(isValidVectorDatasize(id->idOpSize()));
assert(isValidArrangement(id->idOpSize(), id->idInsOpt()));
assert(isVectorRegister(id->idReg1()));
@@ -3213,15 +3229,16 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt)
}
//------------------------------------------------------------------------
-// insGetLoadStoreRegisterListSize: Returns a size of the register list a given instruction operates on.
+// insGetRegisterListSize: Returns a size of the register list a given instruction operates on.
//
// Arguments:
-// ins - A Load/Store Vector instruction (e.g. ld1 (2 registers), ld1r, st1).
+// ins - An instruction which uses a register list
+// (e.g. ld1 (2 registers), ld1r, st1, tbl, tbx).
//
// Return value:
// A number of consecutive SIMD and floating-point registers the instruction loads to/store from.
//
-/*static*/ unsigned emitter::insGetLoadStoreRegisterListSize(instruction ins)
+/*static*/ unsigned emitter::insGetRegisterListSize(instruction ins)
{
unsigned registerListSize = 0;
@@ -3230,6 +3247,8 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt)
case INS_ld1:
case INS_ld1r:
case INS_st1:
+ case INS_tbl:
+ case INS_tbx:
registerListSize = 1;
break;
@@ -3238,6 +3257,8 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt)
case INS_ld2r:
case INS_st1_2regs:
case INS_st2:
+ case INS_tbl_2regs:
+ case INS_tbx_2regs:
registerListSize = 2;
break;
@@ -3246,6 +3267,8 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt)
case INS_ld3r:
case INS_st1_3regs:
case INS_st3:
+ case INS_tbl_3regs:
+ case INS_tbx_3regs:
registerListSize = 3;
break;
@@ -3254,6 +3277,8 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt)
case INS_ld4r:
case INS_st1_4regs:
case INS_st4:
+ case INS_tbl_4regs:
+ case INS_tbx_4regs:
registerListSize = 4;
break;
@@ -5192,7 +5217,7 @@ void emitter::emitIns_R_R_I(
if (insOptsAnyArrangement(opt))
{
- registerListSize = insGetLoadStoreRegisterListSize(ins);
+ registerListSize = insGetRegisterListSize(ins);
assert(isValidVectorDatasize(size));
assert(isValidArrangement(size, opt));
assert((size * registerListSize) == imm);
@@ -5226,7 +5251,7 @@ void emitter::emitIns_R_R_I(
assert(isValidArrangement(size, opt));
elemsize = optGetElemsize(opt);
- registerListSize = insGetLoadStoreRegisterListSize(ins);
+ registerListSize = insGetRegisterListSize(ins);
assert((elemsize * registerListSize) == imm);
// Load single structure and replicate post-indexed by an immediate
@@ -5676,6 +5701,14 @@ void emitter::emitIns_R_R_R(
case INS_eor:
case INS_orr:
case INS_orn:
+ case INS_tbl:
+ case INS_tbl_2regs:
+ case INS_tbl_3regs:
+ case INS_tbl_4regs:
+ case INS_tbx:
+ case INS_tbx_2regs:
+ case INS_tbx_3regs:
+ case INS_tbx_4regs:
if (isVectorRegister(reg1))
{
assert(isValidVectorDatasize(size));
@@ -6612,7 +6645,7 @@ void emitter::emitIns_R_R_I_I(
assert(isValidVectorElemsize(elemsize));
assert(isValidVectorIndex(EA_16BYTE, elemsize, imm1));
- registerListSize = insGetLoadStoreRegisterListSize(ins);
+ registerListSize = insGetRegisterListSize(ins);
assert((elemsize * registerListSize) == (unsigned)imm2);
assert(insOptsPostIndex(opt));
@@ -11884,7 +11917,7 @@ void emitter::emitDispIns(
case IF_LS_2D: // LS_2D .Q.............. ....ssnnnnnttttt Vt Rn
case IF_LS_2E: // LS_2E .Q.............. ....ssnnnnnttttt Vt Rn
- registerListSize = insGetLoadStoreRegisterListSize(id->idIns());
+ registerListSize = insGetRegisterListSize(id->idIns());
emitDispVectorRegList(id->idReg1(), registerListSize, id->idInsOpt(), true);
if (fmt == IF_LS_2D)
@@ -11903,7 +11936,7 @@ void emitter::emitDispIns(
case IF_LS_2F: // LS_2F .Q.............. xx.Sssnnnnnttttt Vt[] Rn
case IF_LS_2G: // LS_2G .Q.............. xx.Sssnnnnnttttt Vt[] Rn
- registerListSize = insGetLoadStoreRegisterListSize(id->idIns());
+ registerListSize = insGetRegisterListSize(id->idIns());
elemsize = id->idOpSize();
emitDispVectorElemList(id->idReg1(), registerListSize, elemsize, id->idSmallCns(), true);
@@ -11967,7 +12000,7 @@ void emitter::emitDispIns(
case IF_LS_3F: // LS_3F .Q.........mmmmm ....ssnnnnnttttt Vt Rn Rm
case IF_LS_3G: // LS_3G .Q.........mmmmm ...Sssnnnnnttttt Vt[] Rn Rm
- registerListSize = insGetLoadStoreRegisterListSize(id->idIns());
+ registerListSize = insGetRegisterListSize(id->idIns());
if (fmt == IF_LS_3F)
{
@@ -12468,9 +12501,25 @@ void emitter::emitDispIns(
case IF_DV_3C: // DV_3C .Q.........mmmmm ......nnnnnddddd Vd Vn Vm (vector)
emitDispVectorReg(id->idReg1(), id->idInsOpt(), true);
- if (ins != INS_mov)
+ switch (ins)
{
- emitDispVectorReg(id->idReg2(), id->idInsOpt(), true);
+ case INS_tbl:
+ case INS_tbl_2regs:
+ case INS_tbl_3regs:
+ case INS_tbl_4regs:
+ case INS_tbx:
+ case INS_tbx_2regs:
+ case INS_tbx_3regs:
+ case INS_tbx_4regs:
+ registerListSize = insGetRegisterListSize(ins);
+ elemsize = id->idOpSize();
+ emitDispVectorRegList(id->idReg2(), registerListSize, id->idInsOpt(), true);
+ break;
+ case INS_mov:
+ break;
+ default:
+ emitDispVectorReg(id->idReg2(), id->idInsOpt(), true);
+ break;
}
emitDispVectorReg(id->idReg3(), id->idInsOpt(), false);
break;
@@ -14145,9 +14194,48 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins
}
break;
- case IF_DV_3C: // mov,and, bic, eor, mov,mvn, orn, bsl, bit, bif (vector)
- result.insThroughput = PERFSCORE_THROUGHPUT_2X;
- result.insLatency = PERFSCORE_LATENCY_1C;
+ case IF_DV_3C: // mov,and, bic, eor, mov,mvn, orn, bsl, bit, bif,
+ // tbl, tbx (vector)
+ switch (ins)
+ {
+ case INS_tbl:
+ result.insThroughput = PERFSCORE_THROUGHPUT_2X;
+ result.insLatency = PERFSCORE_LATENCY_1C;
+ break;
+ case INS_tbl_2regs:
+ result.insThroughput = PERFSCORE_THROUGHPUT_3X;
+ result.insLatency = PERFSCORE_LATENCY_2C;
+ break;
+ case INS_tbl_3regs:
+ result.insThroughput = PERFSCORE_THROUGHPUT_4X;
+ result.insLatency = PERFSCORE_LATENCY_3C;
+ break;
+ case INS_tbl_4regs:
+ result.insThroughput = PERFSCORE_THROUGHPUT_3X;
+ result.insLatency = PERFSCORE_LATENCY_4C;
+ break;
+ case INS_tbx:
+ result.insThroughput = PERFSCORE_THROUGHPUT_3X;
+ result.insLatency = PERFSCORE_LATENCY_2C;
+ break;
+ case INS_tbx_2regs:
+ result.insThroughput = PERFSCORE_THROUGHPUT_4X;
+ result.insLatency = PERFSCORE_LATENCY_3C;
+ break;
+ case INS_tbx_3regs:
+ result.insThroughput = PERFSCORE_THROUGHPUT_5X;
+ result.insLatency = PERFSCORE_LATENCY_4C;
+ break;
+ case INS_tbx_4regs:
+ result.insThroughput = PERFSCORE_THROUGHPUT_6X;
+ result.insLatency = PERFSCORE_LATENCY_5C;
+ break;
+ default:
+ // All other instructions
+ result.insThroughput = PERFSCORE_THROUGHPUT_2X;
+ result.insLatency = PERFSCORE_LATENCY_1C;
+ break;
+ }
break;
case IF_DV_2E: // mov, dup (scalar)
diff --git a/src/coreclr/src/jit/emitarm64.h b/src/coreclr/src/jit/emitarm64.h
index 751d0fe28a5c9..f2cef877a7b20 100644
--- a/src/coreclr/src/jit/emitarm64.h
+++ b/src/coreclr/src/jit/emitarm64.h
@@ -453,9 +453,9 @@ static emitAttr optGetSrcsize(insOpts conversion);
// for an element of size 'elemsize' in a vector register of size 'datasize'
static bool isValidVectorIndex(emitAttr datasize, emitAttr elemsize, ssize_t index);
-// For a given Load/Store Vector instruction 'ins' returns a number of consecutive SIMD registers
-// the instruction loads to/store from.
-static unsigned insGetLoadStoreRegisterListSize(instruction ins);
+// For a given instruction 'ins' which contains a register lists returns a
+// number of consecutive SIMD registers the instruction loads to/store from.
+static unsigned insGetRegisterListSize(instruction ins);
/************************************************************************/
/* Public inline informational methods */
diff --git a/src/coreclr/src/jit/hwintrinsiclistarm64.h b/src/coreclr/src/jit/hwintrinsiclistarm64.h
index 2afaec50462be..da1b2e9463739 100644
--- a/src/coreclr/src/jit/hwintrinsiclistarm64.h
+++ b/src/coreclr/src/jit/hwintrinsiclistarm64.h
@@ -118,6 +118,8 @@ HARDWARE_INTRINSIC(AdvSimd, SqrtScalar,
HARDWARE_INTRINSIC(AdvSimd, Store, -1, 2, {INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1, INS_st1}, HW_Category_MemoryStore, HW_Flag_SpecialCodeGen|HW_Flag_BaseTypeFromSecondArg)
HARDWARE_INTRINSIC(AdvSimd, Subtract, -1, 2, {INS_sub, INS_sub, INS_sub, INS_sub, INS_sub, INS_sub, INS_sub, INS_sub, INS_fsub, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
HARDWARE_INTRINSIC(AdvSimd, SubtractScalar, 8, 2, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_sub, INS_sub, INS_fsub, INS_fsub}, HW_Category_SIMDScalar, HW_Flag_NoFlag)
+HARDWARE_INTRINSIC(AdvSimd, VectorTableLookup, 8, 2, {INS_tbl, INS_tbl, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
+HARDWARE_INTRINSIC(AdvSimd, VectorTableLookupExtension, 8, 3, {INS_tbx, INS_tbx, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_HasRMWSemantics)
HARDWARE_INTRINSIC(AdvSimd, Xor, -1, 2, {INS_eor, INS_eor, INS_eor, INS_eor, INS_eor, INS_eor, INS_eor, INS_eor, INS_eor, INS_eor}, HW_Category_SimpleSIMD, HW_Flag_Commutative)
// ***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
@@ -195,6 +197,8 @@ HARDWARE_INTRINSIC(AdvSimd_Arm64, TransposeEven, -
HARDWARE_INTRINSIC(AdvSimd_Arm64, TransposeOdd, -1, 2, {INS_trn2, INS_trn2, INS_trn2, INS_trn2, INS_trn2, INS_trn2, INS_trn2, INS_trn2, INS_trn2, INS_trn2}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
HARDWARE_INTRINSIC(AdvSimd_Arm64, UnzipEven, -1, 2, {INS_uzp1, INS_uzp1, INS_uzp1, INS_uzp1, INS_uzp1, INS_uzp1, INS_uzp1, INS_uzp1, INS_uzp1, INS_uzp1}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
HARDWARE_INTRINSIC(AdvSimd_Arm64, UnzipOdd, -1, 2, {INS_uzp2, INS_uzp2, INS_uzp2, INS_uzp2, INS_uzp2, INS_uzp2, INS_uzp2, INS_uzp2, INS_uzp2, INS_uzp2}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
+HARDWARE_INTRINSIC(AdvSimd_Arm64, VectorTableLookup, 16, 2, {INS_tbl, INS_tbl, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
+HARDWARE_INTRINSIC(AdvSimd_Arm64, VectorTableLookupExtension, 16, 3, {INS_tbx, INS_tbx, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_HasRMWSemantics)
HARDWARE_INTRINSIC(AdvSimd_Arm64, ZipHigh, -1, 2, {INS_zip2, INS_zip2, INS_zip2, INS_zip2, INS_zip2, INS_zip2, INS_zip2, INS_zip2, INS_zip2, INS_zip2}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
HARDWARE_INTRINSIC(AdvSimd_Arm64, ZipLow, -1, 2, {INS_zip1, INS_zip1, INS_zip1, INS_zip1, INS_zip1, INS_zip1, INS_zip1, INS_zip1, INS_zip1, INS_zip1}, HW_Category_SimpleSIMD, HW_Flag_NoFlag)
diff --git a/src/coreclr/src/jit/instrsarm64.h b/src/coreclr/src/jit/instrsarm64.h
index 1b8e99e587b3f..4569606835996 100644
--- a/src/coreclr/src/jit/instrsarm64.h
+++ b/src/coreclr/src/jit/instrsarm64.h
@@ -1832,6 +1832,31 @@ INST1(uxtl, "uxtl", 0, 0, IF_DV_2O, 0x2F00A400)
INST1(uxtl2, "uxtl2", 0, 0, IF_DV_2O, 0x6F00A400)
// uxtl2 Vd,Vn DV_2O 011011110iiiiiii 101001nnnnnddddd 6F00 A400 Vd,Vn (shift - vector)
+
+INST1(tbl, "tbl", 0, 0, IF_DV_3C, 0x0E000000)
+ // tbl Vd,{Vn},Vm DV_3C 0Q001110000mmmmm 000000nnnnnddddd 0E00 0000 Vd,Vn,Vm (vector)
+
+INST1(tbl_2regs, "tbl", 0, 0, IF_DV_3C, 0x0E002000)
+ // tbl Vd,{Vn,Vn+1},Vm DV_3C 0Q001110000mmmmm 001000nnnnnddddd 0E00 2000 Vd,Vn,Vm (vector)
+
+INST1(tbl_3regs, "tbl", 0, 0, IF_DV_3C, 0x0E004000)
+ // tbl Vd,{Vn,Vn+1,Vn+2},Vm DV_3C 0Q001110000mmmmm 010000nnnnnddddd 0E00 4000 Vd,Vn,Vm (vector)
+
+INST1(tbl_4regs, "tbl", 0, 0, IF_DV_3C, 0x0E006000)
+ // tbl Vd,{Vn,Vn+1,Vn+2,Vn+3},Vm DV_3C 0Q001110000mmmmm 011000nnnnnddddd 0E00 6000 Vd,Vn,Vm (vector)
+
+INST1(tbx, "tbx", 0, 0, IF_DV_3C, 0x0E001000)
+ // tbx Vd,{Vn},Vm DV_3C 0Q001110000mmmmm 000100nnnnnddddd 0E00 1000 Vd,Vn,Vm (vector)
+
+INST1(tbx_2regs, "tbx", 0, 0, IF_DV_3C, 0x0E003000)
+ // tbx Vd,{Vn,Vn+1},Vm DV_3C 0Q001110000mmmmm 001100nnnnnddddd 0E00 3000 Vd,Vn,Vm (vector)
+
+INST1(tbx_3regs, "tbx", 0, 0, IF_DV_3C, 0x0E005000)
+ // tbx Vd,{Vn,Vn+1,Vn+2},Vm DV_3C 0Q001110000mmmmm 010100nnnnnddddd 0E00 5000 Vd,Vn,Vm (vector)
+
+INST1(tbx_4regs, "tbx", 0, 0, IF_DV_3C, 0x0E007000)
+ // tbx Vd,{Vn,Vn+1,Vn+2,Vn+3},Vm DV_3C 0Q001110000mmmmm 011100nnnnnddddd 0E00 7000 Vd,Vn,Vm (vector)
+
// clang-format on
/*****************************************************************************/
diff --git a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_r.csproj b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_r.csproj
index 98bcab7a7c5bc..035e0640e8ce2 100644
--- a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_r.csproj
+++ b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_r.csproj
@@ -224,6 +224,10 @@
+
+
+
+
diff --git a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_ro.csproj b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_ro.csproj
index 0a58c29ec9dde..2c85066500d50 100644
--- a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_ro.csproj
+++ b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/AdvSimd.Arm64_ro.csproj
@@ -224,6 +224,10 @@
+
+
+
+
diff --git a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/Program.AdvSimd.Arm64.cs b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/Program.AdvSimd.Arm64.cs
index 405efb58d4dd9..1e55e18f12708 100644
--- a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/Program.AdvSimd.Arm64.cs
+++ b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/Program.AdvSimd.Arm64.cs
@@ -228,6 +228,10 @@ static Program()
["TransposeOdd.Vector128.UInt16"] = TransposeOdd_Vector128_UInt16,
["TransposeOdd.Vector128.UInt32"] = TransposeOdd_Vector128_UInt32,
["TransposeOdd.Vector128.UInt64"] = TransposeOdd_Vector128_UInt64,
+ ["VectorTableLookup.Vector128.Byte"] = VectorTableLookup_Vector128_Byte,
+ ["VectorTableLookup.Vector128.SByte"] = VectorTableLookup_Vector128_SByte,
+ ["VectorTableLookupExtension.Vector128.Byte"] = VectorTableLookupExtension_Vector128_Byte,
+ ["VectorTableLookupExtension.Vector128.SByte"] = VectorTableLookupExtension_Vector128_SByte,
["UnzipEven.Vector64.Byte"] = UnzipEven_Vector64_Byte,
["UnzipEven.Vector64.Int16"] = UnzipEven_Vector64_Int16,
["UnzipEven.Vector64.Int32"] = UnzipEven_Vector64_Int32,
diff --git a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookup.Vector128.Byte.cs b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookup.Vector128.Byte.cs
new file mode 100644
index 0000000000000..5b3950a6b285e
--- /dev/null
+++ b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookup.Vector128.Byte.cs
@@ -0,0 +1,537 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx *
+ * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file. *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.Arm;
+
+namespace JIT.HardwareIntrinsics.Arm
+{
+ public static partial class Program
+ {
+ private static void VectorTableLookup_Vector128_Byte()
+ {
+ var test = new SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte();
+
+ if (test.IsSupported)
+ {
+ // Validates basic functionality works, using Unsafe.Read
+ test.RunBasicScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates basic functionality works, using Load
+ test.RunBasicScenario_Load();
+ }
+
+ // Validates calling via reflection works, using Unsafe.Read
+ test.RunReflectionScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates calling via reflection works, using Load
+ test.RunReflectionScenario_Load();
+ }
+
+ // Validates passing a static member works
+ test.RunClsVarScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a static member works, using pinning and Load
+ test.RunClsVarScenario_Load();
+ }
+
+ // Validates passing a local works, using Unsafe.Read
+ test.RunLclVarScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a local works, using Load
+ test.RunLclVarScenario_Load();
+ }
+
+ // Validates passing the field of a local class works
+ test.RunClassLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local class works, using pinning and Load
+ test.RunClassLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a class works
+ test.RunClassFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a class works, using pinning and Load
+ test.RunClassFldScenario_Load();
+ }
+
+ // Validates passing the field of a local struct works
+ test.RunStructLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local struct works, using pinning and Load
+ test.RunStructLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a struct works
+ test.RunStructFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a struct works, using pinning and Load
+ test.RunStructFldScenario_Load();
+ }
+ }
+ else
+ {
+ // Validates we throw on unsupported hardware
+ test.RunUnsupportedScenario();
+ }
+
+ if (!test.Succeeded)
+ {
+ throw new Exception("One or more scenarios did not complete as expected.");
+ }
+ }
+ }
+
+ public sealed unsafe class SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte
+ {
+ private struct DataTable
+ {
+ private byte[] inArray1;
+ private byte[] inArray2;
+ private byte[] outArray;
+
+ private GCHandle inHandle1;
+ private GCHandle inHandle2;
+ private GCHandle outHandle;
+
+ private ulong alignment;
+
+ public DataTable(Byte[] inArray1, Byte[] inArray2, Byte[] outArray, int alignment)
+ {
+ int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf();
+ int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf();
+ int sizeOfoutArray = outArray.Length * Unsafe.SizeOf();
+ if ((alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2 || (alignment * 2) < sizeOfoutArray)
+ {
+ throw new ArgumentException("Invalid value of alignment");
+ }
+
+ this.inArray1 = new byte[alignment * 2];
+ this.inArray2 = new byte[alignment * 2];
+ this.outArray = new byte[alignment * 2];
+
+ this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned);
+ this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned);
+ this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned);
+
+ this.alignment = (ulong)alignment;
+
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As(ref inArray1[0]), (uint)sizeOfinArray1);
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray2Ptr), ref Unsafe.As(ref inArray2[0]), (uint)sizeOfinArray2);
+ }
+
+ public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment);
+ public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment);
+ public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment);
+
+ public void Dispose()
+ {
+ inHandle1.Free();
+ inHandle2.Free();
+ outHandle.Free();
+ }
+
+ private static unsafe void* Align(byte* buffer, ulong expectedAlignment)
+ {
+ return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1));
+ }
+ }
+
+ private struct TestStruct
+ {
+ public Vector128 _fld1;
+ public Vector128 _fld2;
+
+ public static TestStruct Create()
+ {
+ var testStruct = new TestStruct();
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref testStruct._fld1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref testStruct._fld2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+
+ return testStruct;
+ }
+
+ public void RunStructFldScenario(SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte testClass)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(_fld1, _fld2);
+
+ Unsafe.Write(testClass._dataTable.outArrayPtr, result);
+ testClass.ValidateResult(_fld1, _fld2, testClass._dataTable.outArrayPtr);
+ }
+
+ public void RunStructFldScenario_Load(SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte testClass)
+ {
+ fixed (Vector128* pFld1 = &_fld1)
+ fixed (Vector128* pFld2 = &_fld2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((Byte*)(pFld1)),
+ AdvSimd.LoadVector128((Byte*)(pFld2))
+ );
+
+ Unsafe.Write(testClass._dataTable.outArrayPtr, result);
+ testClass.ValidateResult(_fld1, _fld2, testClass._dataTable.outArrayPtr);
+ }
+ }
+ }
+
+ private static readonly int LargestVectorSize = 16;
+
+ private static readonly int Op1ElementCount = Unsafe.SizeOf>() / sizeof(Byte);
+ private static readonly int Op2ElementCount = Unsafe.SizeOf>() / sizeof(Byte);
+ private static readonly int RetElementCount = Unsafe.SizeOf>() / sizeof(Byte);
+
+ private static Byte[] _data1 = new Byte[Op1ElementCount];
+ private static Byte[] _data2 = new Byte[Op2ElementCount];
+
+ private static Vector128 _clsVar1;
+ private static Vector128 _clsVar2;
+
+ private Vector128 _fld1;
+ private Vector128 _fld2;
+
+ private DataTable _dataTable;
+
+ static SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte()
+ {
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+ }
+
+ public SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte()
+ {
+ Succeeded = true;
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ _dataTable = new DataTable(_data1, _data2, new Byte[RetElementCount], LargestVectorSize);
+ }
+
+ public bool IsSupported => AdvSimd.Arm64.IsSupported;
+
+ public bool Succeeded { get; set; }
+
+ public void RunBasicScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ Unsafe.Read>(_dataTable.inArray1Ptr),
+ Unsafe.Read>(_dataTable.inArray2Ptr)
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunBasicScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray1Ptr)),
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray2Ptr))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunReflectionScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead));
+
+ var result = typeof(AdvSimd.Arm64).GetMethod(nameof(AdvSimd.Arm64.VectorTableLookup), new Type[] { typeof(Vector128), typeof(Vector128) })
+ .Invoke(null, new object[] {
+ Unsafe.Read>(_dataTable.inArray1Ptr),
+ Unsafe.Read>(_dataTable.inArray2Ptr)
+ });
+
+ Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result));
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunReflectionScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_Load));
+
+ var result = typeof(AdvSimd.Arm64).GetMethod(nameof(AdvSimd.Arm64.VectorTableLookup), new Type[] { typeof(Vector128), typeof(Vector128) })
+ .Invoke(null, new object[] {
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray1Ptr)),
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray2Ptr))
+ });
+
+ Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result));
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunClsVarScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClsVarScenario));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ _clsVar1,
+ _clsVar2
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClsVarScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClsVarScenario_Load));
+
+ fixed (Vector128* pClsVar1 = &_clsVar1)
+ fixed (Vector128* pClsVar2 = &_clsVar2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((Byte*)(pClsVar1)),
+ AdvSimd.LoadVector128((Byte*)(pClsVar2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunLclVarScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead));
+
+ var op1 = Unsafe.Read>(_dataTable.inArray1Ptr);
+ var op2 = Unsafe.Read>(_dataTable.inArray2Ptr);
+ var result = AdvSimd.Arm64.VectorTableLookup(op1, op2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(op1, op2, _dataTable.outArrayPtr);
+ }
+
+ public void RunLclVarScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_Load));
+
+ var op1 = AdvSimd.LoadVector128((Byte*)(_dataTable.inArray1Ptr));
+ var op2 = AdvSimd.LoadVector128((Byte*)(_dataTable.inArray2Ptr));
+ var result = AdvSimd.Arm64.VectorTableLookup(op1, op2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(op1, op2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassLclFldScenario));
+
+ var test = new SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte();
+ var result = AdvSimd.Arm64.VectorTableLookup(test._fld1, test._fld2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassLclFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassLclFldScenario_Load));
+
+ var test = new SimpleBinaryOpTest__VectorTableLookup_Vector128_Byte();
+
+ fixed (Vector128* pFld1 = &test._fld1)
+ fixed (Vector128* pFld2 = &test._fld2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((Byte*)(pFld1)),
+ AdvSimd.LoadVector128((Byte*)(pFld2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunClassFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(_fld1, _fld2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario_Load));
+
+ fixed (Vector128* pFld1 = &_fld1)
+ fixed (Vector128* pFld2 = &_fld2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((Byte*)(pFld1)),
+ AdvSimd.LoadVector128((Byte*)(pFld2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunStructLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario));
+
+ var test = TestStruct.Create();
+ var result = AdvSimd.Arm64.VectorTableLookup(test._fld1, test._fld2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunStructLclFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario_Load));
+
+ var test = TestStruct.Create();
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((Byte*)(&test._fld1)),
+ AdvSimd.LoadVector128((Byte*)(&test._fld2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunStructFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario));
+
+ var test = TestStruct.Create();
+ test.RunStructFldScenario(this);
+ }
+
+ public void RunStructFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario_Load));
+
+ var test = TestStruct.Create();
+ test.RunStructFldScenario_Load(this);
+ }
+
+ public void RunUnsupportedScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario));
+
+ bool succeeded = false;
+
+ try
+ {
+ RunBasicScenario_UnsafeRead();
+ }
+ catch (PlatformNotSupportedException)
+ {
+ succeeded = true;
+ }
+
+ if (!succeeded)
+ {
+ Succeeded = false;
+ }
+ }
+
+ private void ValidateResult(Vector128 op1, Vector128 op2, void* result, [CallerMemberName] string method = "")
+ {
+ Byte[] inArray1 = new Byte[Op1ElementCount];
+ Byte[] inArray2 = new Byte[Op2ElementCount];
+ Byte[] outArray = new Byte[RetElementCount];
+
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref inArray1[0]), op1);
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref inArray2[0]), op2);
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf>());
+
+ ValidateResult(inArray1, inArray2, outArray, method);
+ }
+
+ private void ValidateResult(void* op1, void* op2, void* result, [CallerMemberName] string method = "")
+ {
+ Byte[] inArray1 = new Byte[Op1ElementCount];
+ Byte[] inArray2 = new Byte[Op2ElementCount];
+ Byte[] outArray = new Byte[RetElementCount];
+
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf>());
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray2[0]), ref Unsafe.AsRef(op2), (uint)Unsafe.SizeOf>());
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf>());
+
+ ValidateResult(inArray1, inArray2, outArray, method);
+ }
+
+ private void ValidateResult(Byte[] left, Byte[] right, Byte[] result, [CallerMemberName] string method = "")
+ {
+ bool succeeded = true;
+
+ if (Helpers.TableVectorLookup(0, right, left) != result[0])
+ {
+ succeeded = false;
+ }
+ else
+ {
+ for (var i = 1; i < RetElementCount; i++)
+ {
+ if (Helpers.TableVectorLookup(i, right, left) != result[i])
+ {
+ succeeded = false;
+ break;
+ }
+ }
+ }
+
+ if (!succeeded)
+ {
+ TestLibrary.TestFramework.LogInformation($"{nameof(AdvSimd.Arm64)}.{nameof(AdvSimd.Arm64.VectorTableLookup)}(Vector128, Vector128): {method} failed:");
+ TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})");
+ TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})");
+ TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})");
+ TestLibrary.TestFramework.LogInformation(string.Empty);
+
+ Succeeded = false;
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookup.Vector128.SByte.cs b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookup.Vector128.SByte.cs
new file mode 100644
index 0000000000000..e032ce9fa82c8
--- /dev/null
+++ b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookup.Vector128.SByte.cs
@@ -0,0 +1,537 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx *
+ * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file. *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.Arm;
+
+namespace JIT.HardwareIntrinsics.Arm
+{
+ public static partial class Program
+ {
+ private static void VectorTableLookup_Vector128_SByte()
+ {
+ var test = new SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte();
+
+ if (test.IsSupported)
+ {
+ // Validates basic functionality works, using Unsafe.Read
+ test.RunBasicScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates basic functionality works, using Load
+ test.RunBasicScenario_Load();
+ }
+
+ // Validates calling via reflection works, using Unsafe.Read
+ test.RunReflectionScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates calling via reflection works, using Load
+ test.RunReflectionScenario_Load();
+ }
+
+ // Validates passing a static member works
+ test.RunClsVarScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a static member works, using pinning and Load
+ test.RunClsVarScenario_Load();
+ }
+
+ // Validates passing a local works, using Unsafe.Read
+ test.RunLclVarScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a local works, using Load
+ test.RunLclVarScenario_Load();
+ }
+
+ // Validates passing the field of a local class works
+ test.RunClassLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local class works, using pinning and Load
+ test.RunClassLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a class works
+ test.RunClassFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a class works, using pinning and Load
+ test.RunClassFldScenario_Load();
+ }
+
+ // Validates passing the field of a local struct works
+ test.RunStructLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local struct works, using pinning and Load
+ test.RunStructLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a struct works
+ test.RunStructFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a struct works, using pinning and Load
+ test.RunStructFldScenario_Load();
+ }
+ }
+ else
+ {
+ // Validates we throw on unsupported hardware
+ test.RunUnsupportedScenario();
+ }
+
+ if (!test.Succeeded)
+ {
+ throw new Exception("One or more scenarios did not complete as expected.");
+ }
+ }
+ }
+
+ public sealed unsafe class SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte
+ {
+ private struct DataTable
+ {
+ private byte[] inArray1;
+ private byte[] inArray2;
+ private byte[] outArray;
+
+ private GCHandle inHandle1;
+ private GCHandle inHandle2;
+ private GCHandle outHandle;
+
+ private ulong alignment;
+
+ public DataTable(SByte[] inArray1, SByte[] inArray2, SByte[] outArray, int alignment)
+ {
+ int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf();
+ int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf();
+ int sizeOfoutArray = outArray.Length * Unsafe.SizeOf();
+ if ((alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2 || (alignment * 2) < sizeOfoutArray)
+ {
+ throw new ArgumentException("Invalid value of alignment");
+ }
+
+ this.inArray1 = new byte[alignment * 2];
+ this.inArray2 = new byte[alignment * 2];
+ this.outArray = new byte[alignment * 2];
+
+ this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned);
+ this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned);
+ this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned);
+
+ this.alignment = (ulong)alignment;
+
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As(ref inArray1[0]), (uint)sizeOfinArray1);
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray2Ptr), ref Unsafe.As(ref inArray2[0]), (uint)sizeOfinArray2);
+ }
+
+ public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment);
+ public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment);
+ public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment);
+
+ public void Dispose()
+ {
+ inHandle1.Free();
+ inHandle2.Free();
+ outHandle.Free();
+ }
+
+ private static unsafe void* Align(byte* buffer, ulong expectedAlignment)
+ {
+ return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1));
+ }
+ }
+
+ private struct TestStruct
+ {
+ public Vector128 _fld1;
+ public Vector128 _fld2;
+
+ public static TestStruct Create()
+ {
+ var testStruct = new TestStruct();
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetSByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref testStruct._fld1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (SByte)(TestLibrary.Generator.GetSByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref testStruct._fld2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+
+ return testStruct;
+ }
+
+ public void RunStructFldScenario(SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte testClass)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(_fld1, _fld2);
+
+ Unsafe.Write(testClass._dataTable.outArrayPtr, result);
+ testClass.ValidateResult(_fld1, _fld2, testClass._dataTable.outArrayPtr);
+ }
+
+ public void RunStructFldScenario_Load(SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte testClass)
+ {
+ fixed (Vector128* pFld1 = &_fld1)
+ fixed (Vector128* pFld2 = &_fld2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((SByte*)(pFld1)),
+ AdvSimd.LoadVector128((SByte*)(pFld2))
+ );
+
+ Unsafe.Write(testClass._dataTable.outArrayPtr, result);
+ testClass.ValidateResult(_fld1, _fld2, testClass._dataTable.outArrayPtr);
+ }
+ }
+ }
+
+ private static readonly int LargestVectorSize = 16;
+
+ private static readonly int Op1ElementCount = Unsafe.SizeOf>() / sizeof(SByte);
+ private static readonly int Op2ElementCount = Unsafe.SizeOf>() / sizeof(SByte);
+ private static readonly int RetElementCount = Unsafe.SizeOf>() / sizeof(SByte);
+
+ private static SByte[] _data1 = new SByte[Op1ElementCount];
+ private static SByte[] _data2 = new SByte[Op2ElementCount];
+
+ private static Vector128 _clsVar1;
+ private static Vector128 _clsVar2;
+
+ private Vector128 _fld1;
+ private Vector128 _fld2;
+
+ private DataTable _dataTable;
+
+ static SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte()
+ {
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetSByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (SByte)(TestLibrary.Generator.GetSByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+ }
+
+ public SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte()
+ {
+ Succeeded = true;
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetSByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (SByte)(TestLibrary.Generator.GetSByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetSByte(); }
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = (SByte)(TestLibrary.Generator.GetSByte() % 20); }
+ _dataTable = new DataTable(_data1, _data2, new SByte[RetElementCount], LargestVectorSize);
+ }
+
+ public bool IsSupported => AdvSimd.Arm64.IsSupported;
+
+ public bool Succeeded { get; set; }
+
+ public void RunBasicScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ Unsafe.Read>(_dataTable.inArray1Ptr),
+ Unsafe.Read>(_dataTable.inArray2Ptr)
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunBasicScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((SByte*)(_dataTable.inArray1Ptr)),
+ AdvSimd.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunReflectionScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead));
+
+ var result = typeof(AdvSimd.Arm64).GetMethod(nameof(AdvSimd.Arm64.VectorTableLookup), new Type[] { typeof(Vector128), typeof(Vector128) })
+ .Invoke(null, new object[] {
+ Unsafe.Read>(_dataTable.inArray1Ptr),
+ Unsafe.Read>(_dataTable.inArray2Ptr)
+ });
+
+ Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result));
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunReflectionScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_Load));
+
+ var result = typeof(AdvSimd.Arm64).GetMethod(nameof(AdvSimd.Arm64.VectorTableLookup), new Type[] { typeof(Vector128), typeof(Vector128) })
+ .Invoke(null, new object[] {
+ AdvSimd.LoadVector128((SByte*)(_dataTable.inArray1Ptr)),
+ AdvSimd.LoadVector128((SByte*)(_dataTable.inArray2Ptr))
+ });
+
+ Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result));
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunClsVarScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClsVarScenario));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ _clsVar1,
+ _clsVar2
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClsVarScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClsVarScenario_Load));
+
+ fixed (Vector128* pClsVar1 = &_clsVar1)
+ fixed (Vector128* pClsVar2 = &_clsVar2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((SByte*)(pClsVar1)),
+ AdvSimd.LoadVector128((SByte*)(pClsVar2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_clsVar1, _clsVar2, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunLclVarScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead));
+
+ var op1 = Unsafe.Read>(_dataTable.inArray1Ptr);
+ var op2 = Unsafe.Read>(_dataTable.inArray2Ptr);
+ var result = AdvSimd.Arm64.VectorTableLookup(op1, op2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(op1, op2, _dataTable.outArrayPtr);
+ }
+
+ public void RunLclVarScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_Load));
+
+ var op1 = AdvSimd.LoadVector128((SByte*)(_dataTable.inArray1Ptr));
+ var op2 = AdvSimd.LoadVector128((SByte*)(_dataTable.inArray2Ptr));
+ var result = AdvSimd.Arm64.VectorTableLookup(op1, op2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(op1, op2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassLclFldScenario));
+
+ var test = new SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte();
+ var result = AdvSimd.Arm64.VectorTableLookup(test._fld1, test._fld2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassLclFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassLclFldScenario_Load));
+
+ var test = new SimpleBinaryOpTest__VectorTableLookup_Vector128_SByte();
+
+ fixed (Vector128* pFld1 = &test._fld1)
+ fixed (Vector128* pFld2 = &test._fld2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((SByte*)(pFld1)),
+ AdvSimd.LoadVector128((SByte*)(pFld2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunClassFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario));
+
+ var result = AdvSimd.Arm64.VectorTableLookup(_fld1, _fld2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario_Load));
+
+ fixed (Vector128* pFld1 = &_fld1)
+ fixed (Vector128* pFld2 = &_fld2)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((SByte*)(pFld1)),
+ AdvSimd.LoadVector128((SByte*)(pFld2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_fld1, _fld2, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunStructLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario));
+
+ var test = TestStruct.Create();
+ var result = AdvSimd.Arm64.VectorTableLookup(test._fld1, test._fld2);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunStructLclFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario_Load));
+
+ var test = TestStruct.Create();
+ var result = AdvSimd.Arm64.VectorTableLookup(
+ AdvSimd.LoadVector128((SByte*)(&test._fld1)),
+ AdvSimd.LoadVector128((SByte*)(&test._fld2))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, _dataTable.outArrayPtr);
+ }
+
+ public void RunStructFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario));
+
+ var test = TestStruct.Create();
+ test.RunStructFldScenario(this);
+ }
+
+ public void RunStructFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario_Load));
+
+ var test = TestStruct.Create();
+ test.RunStructFldScenario_Load(this);
+ }
+
+ public void RunUnsupportedScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario));
+
+ bool succeeded = false;
+
+ try
+ {
+ RunBasicScenario_UnsafeRead();
+ }
+ catch (PlatformNotSupportedException)
+ {
+ succeeded = true;
+ }
+
+ if (!succeeded)
+ {
+ Succeeded = false;
+ }
+ }
+
+ private void ValidateResult(Vector128 op1, Vector128 op2, void* result, [CallerMemberName] string method = "")
+ {
+ SByte[] inArray1 = new SByte[Op1ElementCount];
+ SByte[] inArray2 = new SByte[Op2ElementCount];
+ SByte[] outArray = new SByte[RetElementCount];
+
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref inArray1[0]), op1);
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref inArray2[0]), op2);
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf>());
+
+ ValidateResult(inArray1, inArray2, outArray, method);
+ }
+
+ private void ValidateResult(void* op1, void* op2, void* result, [CallerMemberName] string method = "")
+ {
+ SByte[] inArray1 = new SByte[Op1ElementCount];
+ SByte[] inArray2 = new SByte[Op2ElementCount];
+ SByte[] outArray = new SByte[RetElementCount];
+
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf>());
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray2[0]), ref Unsafe.AsRef(op2), (uint)Unsafe.SizeOf>());
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf>());
+
+ ValidateResult(inArray1, inArray2, outArray, method);
+ }
+
+ private void ValidateResult(SByte[] left, SByte[] right, SByte[] result, [CallerMemberName] string method = "")
+ {
+ bool succeeded = true;
+
+ if (Helpers.TableVectorLookup(0, right, left) != result[0])
+ {
+ succeeded = false;
+ }
+ else
+ {
+ for (var i = 1; i < RetElementCount; i++)
+ {
+ if (Helpers.TableVectorLookup(i, right, left) != result[i])
+ {
+ succeeded = false;
+ break;
+ }
+ }
+ }
+
+ if (!succeeded)
+ {
+ TestLibrary.TestFramework.LogInformation($"{nameof(AdvSimd.Arm64)}.{nameof(AdvSimd.Arm64.VectorTableLookup)}(Vector128, Vector128): {method} failed:");
+ TestLibrary.TestFramework.LogInformation($" left: ({string.Join(", ", left)})");
+ TestLibrary.TestFramework.LogInformation($" right: ({string.Join(", ", right)})");
+ TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})");
+ TestLibrary.TestFramework.LogInformation(string.Empty);
+
+ Succeeded = false;
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookupExtension.Vector128.Byte.cs b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookupExtension.Vector128.Byte.cs
new file mode 100644
index 0000000000000..b37d91641f84e
--- /dev/null
+++ b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookupExtension.Vector128.Byte.cs
@@ -0,0 +1,571 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx *
+ * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file. *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.Arm;
+
+namespace JIT.HardwareIntrinsics.Arm
+{
+ public static partial class Program
+ {
+ private static void VectorTableLookupExtension_Vector128_Byte()
+ {
+ var test = new SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte();
+
+ if (test.IsSupported)
+ {
+ // Validates basic functionality works, using Unsafe.Read
+ test.RunBasicScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates basic functionality works, using Load
+ test.RunBasicScenario_Load();
+ }
+
+ // Validates calling via reflection works, using Unsafe.Read
+ test.RunReflectionScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates calling via reflection works, using Load
+ test.RunReflectionScenario_Load();
+ }
+
+ // Validates passing a static member works
+ test.RunClsVarScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a static member works, using pinning and Load
+ test.RunClsVarScenario_Load();
+ }
+
+ // Validates passing a local works, using Unsafe.Read
+ test.RunLclVarScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a local works, using Load
+ test.RunLclVarScenario_Load();
+ }
+
+ // Validates passing the field of a local class works
+ test.RunClassLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local class works, using pinning and Load
+ test.RunClassLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a class works
+ test.RunClassFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a class works, using pinning and Load
+ test.RunClassFldScenario_Load();
+ }
+
+ // Validates passing the field of a local struct works
+ test.RunStructLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local struct works, using pinning and Load
+ test.RunStructLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a struct works
+ test.RunStructFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a struct works, using pinning and Load
+ test.RunStructFldScenario_Load();
+ }
+ }
+ else
+ {
+ // Validates we throw on unsupported hardware
+ test.RunUnsupportedScenario();
+ }
+
+ if (!test.Succeeded)
+ {
+ throw new Exception("One or more scenarios did not complete as expected.");
+ }
+ }
+ }
+
+ public sealed unsafe class SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte
+ {
+ private struct DataTable
+ {
+ private byte[] inArray1;
+ private byte[] inArray2;
+ private byte[] inArray3;
+ private byte[] outArray;
+
+ private GCHandle inHandle1;
+ private GCHandle inHandle2;
+ private GCHandle inHandle3;
+ private GCHandle outHandle;
+
+ private ulong alignment;
+
+ public DataTable(Byte[] inArray1, Byte[] inArray2, Byte[] inArray3, Byte[] outArray, int alignment)
+ {
+ int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf();
+ int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf();
+ int sizeOfinArray3 = inArray3.Length * Unsafe.SizeOf();
+ int sizeOfoutArray = outArray.Length * Unsafe.SizeOf();
+ if ((alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2 || (alignment * 2) < sizeOfinArray3 || (alignment * 2) < sizeOfoutArray)
+ {
+ throw new ArgumentException("Invalid value of alignment");
+ }
+
+ this.inArray1 = new byte[alignment * 2];
+ this.inArray2 = new byte[alignment * 2];
+ this.inArray3 = new byte[alignment * 2];
+ this.outArray = new byte[alignment * 2];
+
+ this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned);
+ this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned);
+ this.inHandle3 = GCHandle.Alloc(this.inArray3, GCHandleType.Pinned);
+ this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned);
+
+ this.alignment = (ulong)alignment;
+
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray1Ptr), ref Unsafe.As(ref inArray1[0]), (uint)sizeOfinArray1);
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray2Ptr), ref Unsafe.As(ref inArray2[0]), (uint)sizeOfinArray2);
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef(inArray3Ptr), ref Unsafe.As(ref inArray3[0]), (uint)sizeOfinArray3);
+ }
+
+ public void* inArray1Ptr => Align((byte*)(inHandle1.AddrOfPinnedObject().ToPointer()), alignment);
+ public void* inArray2Ptr => Align((byte*)(inHandle2.AddrOfPinnedObject().ToPointer()), alignment);
+ public void* inArray3Ptr => Align((byte*)(inHandle3.AddrOfPinnedObject().ToPointer()), alignment);
+ public void* outArrayPtr => Align((byte*)(outHandle.AddrOfPinnedObject().ToPointer()), alignment);
+
+ public void Dispose()
+ {
+ inHandle1.Free();
+ inHandle2.Free();
+ inHandle3.Free();
+ outHandle.Free();
+ }
+
+ private static unsafe void* Align(byte* buffer, ulong expectedAlignment)
+ {
+ return (void*)(((ulong)buffer + expectedAlignment - 1) & ~(expectedAlignment - 1));
+ }
+ }
+
+ private struct TestStruct
+ {
+ public Vector128 _fld1;
+ public Vector128 _fld2;
+ public Vector128 _fld3;
+
+ public static TestStruct Create()
+ {
+ var testStruct = new TestStruct();
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref testStruct._fld1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref testStruct._fld2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref testStruct._fld3), ref Unsafe.As(ref _data3[0]), (uint)Unsafe.SizeOf>());
+
+ return testStruct;
+ }
+
+ public void RunStructFldScenario(SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte testClass)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(_fld1, _fld2, _fld3);
+
+ Unsafe.Write(testClass._dataTable.outArrayPtr, result);
+ testClass.ValidateResult(_fld1, _fld2, _fld3, testClass._dataTable.outArrayPtr);
+ }
+
+ public void RunStructFldScenario_Load(SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte testClass)
+ {
+ fixed (Vector128* pFld1 = &_fld1)
+ fixed (Vector128* pFld2 = &_fld2)
+ fixed (Vector128* pFld3 = &_fld3)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ AdvSimd.LoadVector128((Byte*)(pFld1)),
+ AdvSimd.LoadVector128((Byte*)(pFld2)),
+ AdvSimd.LoadVector128((Byte*)(pFld3))
+ );
+
+ Unsafe.Write(testClass._dataTable.outArrayPtr, result);
+ testClass.ValidateResult(_fld1, _fld2, _fld3, testClass._dataTable.outArrayPtr);
+ }
+ }
+ }
+
+ private static readonly int LargestVectorSize = 16;
+
+ private static readonly int Op1ElementCount = Unsafe.SizeOf>() / sizeof(Byte);
+ private static readonly int Op2ElementCount = Unsafe.SizeOf>() / sizeof(Byte);
+ private static readonly int Op3ElementCount = Unsafe.SizeOf>() / sizeof(Byte);
+ private static readonly int RetElementCount = Unsafe.SizeOf>() / sizeof(Byte);
+
+ private static Byte[] _data1 = new Byte[Op1ElementCount];
+ private static Byte[] _data2 = new Byte[Op2ElementCount];
+ private static Byte[] _data3 = new Byte[Op3ElementCount];
+
+ private static Vector128 _clsVar1;
+ private static Vector128 _clsVar2;
+ private static Vector128 _clsVar3;
+
+ private Vector128 _fld1;
+ private Vector128 _fld2;
+ private Vector128 _fld3;
+
+ private DataTable _dataTable;
+
+ static SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte()
+ {
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _clsVar3), ref Unsafe.As(ref _data3[0]), (uint)Unsafe.SizeOf>());
+ }
+
+ public SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte()
+ {
+ Succeeded = true;
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld1), ref Unsafe.As(ref _data1[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = TestLibrary.Generator.GetByte(); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld2), ref Unsafe.As(ref _data2[0]), (uint)Unsafe.SizeOf>());
+ for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As, byte>(ref _fld3), ref Unsafe.As(ref _data3[0]), (uint)Unsafe.SizeOf>());
+
+ for (var i = 0; i < Op1ElementCount; i++) { _data1[i] = TestLibrary.Generator.GetByte(); }
+ for (var i = 0; i < Op2ElementCount; i++) { _data2[i] = TestLibrary.Generator.GetByte(); }
+ for (var i = 0; i < Op3ElementCount; i++) { _data3[i] = (Byte)(TestLibrary.Generator.GetByte() % 20); }
+ _dataTable = new DataTable(_data1, _data2, _data3, new Byte[RetElementCount], LargestVectorSize);
+ }
+
+ public bool IsSupported => AdvSimd.Arm64.IsSupported;
+
+ public bool Succeeded { get; set; }
+
+ public void RunBasicScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead));
+
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ Unsafe.Read>(_dataTable.inArray1Ptr),
+ Unsafe.Read>(_dataTable.inArray2Ptr),
+ Unsafe.Read>(_dataTable.inArray3Ptr)
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunBasicScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_Load));
+
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray1Ptr)),
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray2Ptr)),
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray3Ptr))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunReflectionScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead));
+
+ var result = typeof(AdvSimd.Arm64).GetMethod(nameof(AdvSimd.Arm64.VectorTableLookupExtension), new Type[] { typeof(Vector128), typeof(Vector128), typeof(Vector128) })
+ .Invoke(null, new object[] {
+ Unsafe.Read>(_dataTable.inArray1Ptr),
+ Unsafe.Read>(_dataTable.inArray2Ptr),
+ Unsafe.Read>(_dataTable.inArray3Ptr)
+ });
+
+ Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result));
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunReflectionScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_Load));
+
+ var result = typeof(AdvSimd.Arm64).GetMethod(nameof(AdvSimd.Arm64.VectorTableLookupExtension), new Type[] { typeof(Vector128), typeof(Vector128), typeof(Vector128) })
+ .Invoke(null, new object[] {
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray1Ptr)),
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray2Ptr)),
+ AdvSimd.LoadVector128((Byte*)(_dataTable.inArray3Ptr))
+ });
+
+ Unsafe.Write(_dataTable.outArrayPtr, (Vector128)(result));
+ ValidateResult(_dataTable.inArray1Ptr, _dataTable.inArray2Ptr, _dataTable.inArray3Ptr, _dataTable.outArrayPtr);
+ }
+
+ public void RunClsVarScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClsVarScenario));
+
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ _clsVar1,
+ _clsVar2,
+ _clsVar3
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_clsVar1, _clsVar2, _clsVar3, _dataTable.outArrayPtr);
+ }
+
+ public void RunClsVarScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClsVarScenario_Load));
+
+ fixed (Vector128* pClsVar1 = &_clsVar1)
+ fixed (Vector128* pClsVar2 = &_clsVar2)
+ fixed (Vector128* pClsVar3 = &_clsVar3)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ AdvSimd.LoadVector128((Byte*)(pClsVar1)),
+ AdvSimd.LoadVector128((Byte*)(pClsVar2)),
+ AdvSimd.LoadVector128((Byte*)(pClsVar3))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_clsVar1, _clsVar2, _clsVar3, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunLclVarScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead));
+
+ var op1 = Unsafe.Read>(_dataTable.inArray1Ptr);
+ var op2 = Unsafe.Read>(_dataTable.inArray2Ptr);
+ var op3 = Unsafe.Read>(_dataTable.inArray3Ptr);
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(op1, op2, op3);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(op1, op2, op3, _dataTable.outArrayPtr);
+ }
+
+ public void RunLclVarScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_Load));
+
+ var op1 = AdvSimd.LoadVector128((Byte*)(_dataTable.inArray1Ptr));
+ var op2 = AdvSimd.LoadVector128((Byte*)(_dataTable.inArray2Ptr));
+ var op3 = AdvSimd.LoadVector128((Byte*)(_dataTable.inArray3Ptr));
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(op1, op2, op3);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(op1, op2, op3, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassLclFldScenario));
+
+ var test = new SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte();
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(test._fld1, test._fld2, test._fld3);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, test._fld3, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassLclFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassLclFldScenario_Load));
+
+ var test = new SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_Byte();
+
+ fixed (Vector128* pFld1 = &test._fld1)
+ fixed (Vector128* pFld2 = &test._fld2)
+ fixed (Vector128* pFld3 = &test._fld3)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ AdvSimd.LoadVector128((Byte*)(pFld1)),
+ AdvSimd.LoadVector128((Byte*)(pFld2)),
+ AdvSimd.LoadVector128((Byte*)(pFld3))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, test._fld3, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunClassFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario));
+
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(_fld1, _fld2, _fld3);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_fld1, _fld2, _fld3, _dataTable.outArrayPtr);
+ }
+
+ public void RunClassFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario_Load));
+
+ fixed (Vector128* pFld1 = &_fld1)
+ fixed (Vector128* pFld2 = &_fld2)
+ fixed (Vector128* pFld3 = &_fld3)
+ {
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ AdvSimd.LoadVector128((Byte*)(pFld1)),
+ AdvSimd.LoadVector128((Byte*)(pFld2)),
+ AdvSimd.LoadVector128((Byte*)(pFld3))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(_fld1, _fld2, _fld3, _dataTable.outArrayPtr);
+ }
+ }
+
+ public void RunStructLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario));
+
+ var test = TestStruct.Create();
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(test._fld1, test._fld2, test._fld3);
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, test._fld3, _dataTable.outArrayPtr);
+ }
+
+ public void RunStructLclFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario_Load));
+
+ var test = TestStruct.Create();
+ var result = AdvSimd.Arm64.VectorTableLookupExtension(
+ AdvSimd.LoadVector128((Byte*)(&test._fld1)),
+ AdvSimd.LoadVector128((Byte*)(&test._fld2)),
+ AdvSimd.LoadVector128((Byte*)(&test._fld3))
+ );
+
+ Unsafe.Write(_dataTable.outArrayPtr, result);
+ ValidateResult(test._fld1, test._fld2, test._fld3, _dataTable.outArrayPtr);
+ }
+
+ public void RunStructFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario));
+
+ var test = TestStruct.Create();
+ test.RunStructFldScenario(this);
+ }
+
+ public void RunStructFldScenario_Load()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario_Load));
+
+ var test = TestStruct.Create();
+ test.RunStructFldScenario_Load(this);
+ }
+
+ public void RunUnsupportedScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario));
+
+ bool succeeded = false;
+
+ try
+ {
+ RunBasicScenario_UnsafeRead();
+ }
+ catch (PlatformNotSupportedException)
+ {
+ succeeded = true;
+ }
+
+ if (!succeeded)
+ {
+ Succeeded = false;
+ }
+ }
+
+ private void ValidateResult(Vector128 op1, Vector128 op2, Vector128 op3, void* result, [CallerMemberName] string method = "")
+ {
+ Byte[] inArray1 = new Byte[Op1ElementCount];
+ Byte[] inArray2 = new Byte[Op2ElementCount];
+ Byte[] inArray3 = new Byte[Op3ElementCount];
+ Byte[] outArray = new Byte[RetElementCount];
+
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref inArray1[0]), op1);
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref inArray2[0]), op2);
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref inArray3[0]), op3);
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf>());
+
+ ValidateResult(inArray1, inArray2, inArray3, outArray, method);
+ }
+
+ private void ValidateResult(void* op1, void* op2, void* op3, void* result, [CallerMemberName] string method = "")
+ {
+ Byte[] inArray1 = new Byte[Op1ElementCount];
+ Byte[] inArray2 = new Byte[Op2ElementCount];
+ Byte[] inArray3 = new Byte[Op3ElementCount];
+ Byte[] outArray = new Byte[RetElementCount];
+
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray1[0]), ref Unsafe.AsRef(op1), (uint)Unsafe.SizeOf>());
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray2[0]), ref Unsafe.AsRef(op2), (uint)Unsafe.SizeOf>());
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref inArray3[0]), ref Unsafe.AsRef(op3), (uint)Unsafe.SizeOf>());
+ Unsafe.CopyBlockUnaligned(ref Unsafe.As(ref outArray[0]), ref Unsafe.AsRef(result), (uint)Unsafe.SizeOf>());
+
+ ValidateResult(inArray1, inArray2, inArray3, outArray, method);
+ }
+
+ private void ValidateResult(Byte[] firstOp, Byte[] secondOp, Byte[] thirdOp, Byte[] result, [CallerMemberName] string method = "")
+ {
+ bool succeeded = true;
+
+ for (var i = 0; i < RetElementCount; i++)
+ {
+ if (Helpers.TableVectorExtension(i, firstOp, thirdOp, secondOp) != result[i])
+ {
+ succeeded = false;
+ break;
+ }
+ }
+
+ if (!succeeded)
+ {
+ TestLibrary.TestFramework.LogInformation($"{nameof(AdvSimd.Arm64)}.{nameof(AdvSimd.Arm64.VectorTableLookupExtension)}(Vector128, Vector128, Vector128): {method} failed:");
+ TestLibrary.TestFramework.LogInformation($" firstOp: ({string.Join(", ", firstOp)})");
+ TestLibrary.TestFramework.LogInformation($"secondOp: ({string.Join(", ", secondOp)})");
+ TestLibrary.TestFramework.LogInformation($" thirdOp: ({string.Join(", ", thirdOp)})");
+ TestLibrary.TestFramework.LogInformation($" result: ({string.Join(", ", result)})");
+ TestLibrary.TestFramework.LogInformation(string.Empty);
+
+ Succeeded = false;
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookupExtension.Vector128.SByte.cs b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookupExtension.Vector128.SByte.cs
new file mode 100644
index 0000000000000..a246269498eec
--- /dev/null
+++ b/src/coreclr/tests/src/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/VectorTableLookupExtension.Vector128.SByte.cs
@@ -0,0 +1,571 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx *
+ * script in tests\src\JIT\HardwareIntrinsics.Arm\Shared. In order to make *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file. *
+ ******************************************************************************/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.Arm;
+
+namespace JIT.HardwareIntrinsics.Arm
+{
+ public static partial class Program
+ {
+ private static void VectorTableLookupExtension_Vector128_SByte()
+ {
+ var test = new SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_SByte();
+
+ if (test.IsSupported)
+ {
+ // Validates basic functionality works, using Unsafe.Read
+ test.RunBasicScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates basic functionality works, using Load
+ test.RunBasicScenario_Load();
+ }
+
+ // Validates calling via reflection works, using Unsafe.Read
+ test.RunReflectionScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates calling via reflection works, using Load
+ test.RunReflectionScenario_Load();
+ }
+
+ // Validates passing a static member works
+ test.RunClsVarScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a static member works, using pinning and Load
+ test.RunClsVarScenario_Load();
+ }
+
+ // Validates passing a local works, using Unsafe.Read
+ test.RunLclVarScenario_UnsafeRead();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing a local works, using Load
+ test.RunLclVarScenario_Load();
+ }
+
+ // Validates passing the field of a local class works
+ test.RunClassLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local class works, using pinning and Load
+ test.RunClassLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a class works
+ test.RunClassFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a class works, using pinning and Load
+ test.RunClassFldScenario_Load();
+ }
+
+ // Validates passing the field of a local struct works
+ test.RunStructLclFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing the field of a local struct works, using pinning and Load
+ test.RunStructLclFldScenario_Load();
+ }
+
+ // Validates passing an instance member of a struct works
+ test.RunStructFldScenario();
+
+ if (AdvSimd.IsSupported)
+ {
+ // Validates passing an instance member of a struct works, using pinning and Load
+ test.RunStructFldScenario_Load();
+ }
+ }
+ else
+ {
+ // Validates we throw on unsupported hardware
+ test.RunUnsupportedScenario();
+ }
+
+ if (!test.Succeeded)
+ {
+ throw new Exception("One or more scenarios did not complete as expected.");
+ }
+ }
+ }
+
+ public sealed unsafe class SimpleTernaryOpTest__VectorTableLookupExtension_Vector128_SByte
+ {
+ private struct DataTable
+ {
+ private byte[] inArray1;
+ private byte[] inArray2;
+ private byte[] inArray3;
+ private byte[] outArray;
+
+ private GCHandle inHandle1;
+ private GCHandle inHandle2;
+ private GCHandle inHandle3;
+ private GCHandle outHandle;
+
+ private ulong alignment;
+
+ public DataTable(SByte[] inArray1, SByte[] inArray2, SByte[] inArray3, SByte[] outArray, int alignment)
+ {
+ int sizeOfinArray1 = inArray1.Length * Unsafe.SizeOf();
+ int sizeOfinArray2 = inArray2.Length * Unsafe.SizeOf();
+ int sizeOfinArray3 = inArray3.Length * Unsafe.SizeOf();
+ int sizeOfoutArray = outArray.Length * Unsafe.SizeOf();
+ if ((alignment != 16 && alignment != 8) || (alignment * 2) < sizeOfinArray1 || (alignment * 2) < sizeOfinArray2 || (alignment * 2) < sizeOfinArray3 || (alignment * 2) < sizeOfoutArray)
+ {
+ throw new ArgumentException("Invalid value of alignment");
+ }
+
+ this.inArray1 = new byte[alignment * 2];
+ this.inArray2 = new byte[alignment * 2];
+ this.inArray3 = new byte[alignment * 2];
+ this.outArray = new byte[alignment * 2];
+
+ this.inHandle1 = GCHandle.Alloc(this.inArray1, GCHandleType.Pinned);
+ this.inHandle2 = GCHandle.Alloc(this.inArray2, GCHandleType.Pinned);
+ this.inHandle3 = GCHandle.Alloc(this.inArray3, GCHandleType.Pinned);
+ this.outHandle = GCHandle.Alloc(this.outArray, GCHandleType.Pinned);
+
+ this.alignment = (ulong)alignment;
+
+ Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef