code wiki / (root) / nx_simd_minmax_test.nx

nx_simd_minmax_test.nx source

↩ module page · 146 lines · 5829 B

1// nx_simd_minmax_test.nx -- i16x16 per-lane and horizontal min/max. 2// 3// 4 ops: vmin / vmax (per-lane) and vreduce_min / vreduce_max 4// (horizontal scalar). Useful for: argmax-class ML reductions, 5// audio peak detect, image histogram extremes, clamping pipelines. 6 7import "nx_kernel_v2.nx" 8import "nx_log.nx" 9 10func pack4_i16(a: i64, b: i64, c: i64, d: i64) -> i64 { 11 return (a & 0xFFFF) 12 | ((b & 0xFFFF) << 16) 13 | ((c & 0xFFFF) << 32) 14 | ((d & 0xFFFF) << 48) 15} 16 17func t1_vmin_lanewise() -> i64 { 18 // a = [10, 20, 30, ..., 160], b = [5, 25, 5, 45, ...] 19 // pairwise min = mix. 20 let a_raw: *u8 = sys_mmap(64) 21 let b_raw: *u8 = sys_mmap(64) 22 let r_raw: *u8 = sys_mmap(64) 23 let a: *i64 = a_raw as *i64 24 let b: *i64 = b_raw as *i64 25 let r: *i64 = r_raw as *i64 26 a[0] = pack4_i16(10, 20, 30, 40) 27 a[1] = pack4_i16(50, 60, 70, 80) 28 a[2] = pack4_i16(90, 100, 110, 120) 29 a[3] = pack4_i16(130, 140, 150, 160) 30 b[0] = pack4_i16(5, 25, 5, 45) 31 b[1] = pack4_i16(45, 65, 65, 85) 32 b[2] = pack4_i16(85, 105, 105, 125) 33 b[3] = pack4_i16(125, 145, 145, 165) 34 let va: i64 = __simd_vload_i16_x16(a as *i64) 35 let vb: i64 = __simd_vload_i16_x16(b as *i64) 36 let vmin: i64 = __simd_vmin_i16_x16(va, vb) 37 __simd_vstore_i16_x16(vmin, r as *i64) 38 // expected lanes: min(10,5)=5 min(20,25)=20 min(30,5)=5 min(40,45)=40 39 if r[0] != pack4_i16(5, 20, 5, 40) { return 1 } 40 // min(50,45)=45 min(60,65)=60 min(70,65)=65 min(80,85)=80 41 if r[1] != pack4_i16(45, 60, 65, 80) { return 2 } 42 // min(90,85)=85 min(100,105)=100 min(110,105)=105 min(120,125)=120 43 if r[2] != pack4_i16(85, 100, 105, 120) { return 3 } 44 // min(130,125)=125 min(140,145)=140 min(150,145)=145 min(160,165)=160 45 if r[3] != pack4_i16(125, 140, 145, 160) { return 4 } 46 return 0 47} 48 49func t2_vmax_lanewise() -> i64 { 50 let a_raw: *u8 = sys_mmap(64) 51 let b_raw: *u8 = sys_mmap(64) 52 let r_raw: *u8 = sys_mmap(64) 53 let a: *i64 = a_raw as *i64 54 let b: *i64 = b_raw as *i64 55 let r: *i64 = r_raw as *i64 56 a[0] = pack4_i16(10, 20, 30, 40) 57 a[1] = pack4_i16(50, 60, 70, 80) 58 a[2] = pack4_i16(90, 100, 110, 120) 59 a[3] = pack4_i16(130, 140, 150, 160) 60 b[0] = pack4_i16(5, 25, 5, 45) 61 b[1] = pack4_i16(45, 65, 65, 85) 62 b[2] = pack4_i16(85, 105, 105, 125) 63 b[3] = pack4_i16(125, 145, 145, 165) 64 let va: i64 = __simd_vload_i16_x16(a as *i64) 65 let vb: i64 = __simd_vload_i16_x16(b as *i64) 66 let vmax: i64 = __simd_vmax_i16_x16(va, vb) 67 __simd_vstore_i16_x16(vmax, r as *i64) 68 if r[0] != pack4_i16(10, 25, 30, 45) { return 10 } 69 if r[1] != pack4_i16(50, 65, 70, 85) { return 11 } 70 if r[2] != pack4_i16(90, 105, 110, 125) { return 12 } 71 if r[3] != pack4_i16(130, 145, 150, 165) { return 13 } 72 return 0 73} 74 75func t3_vreduce_min() -> i64 { 76 let a_raw: *u8 = sys_mmap(64) 77 let a: *i64 = a_raw as *i64 78 a[0] = pack4_i16(100, 200, 300, 400) 79 a[1] = pack4_i16(500, 600, 700, 800) 80 a[2] = pack4_i16(50, 900, 1000, 1100) // 50 is the min 81 a[3] = pack4_i16(1200, 1300, 1400, 1500) 82 let v: i64 = __simd_vload_i16_x16(a as *i64) 83 let m: i64 = __simd_vreduce_min_i16_x16(v) 84 if m != 50 { return 20 } 85 return 0 86} 87 88func t4_vreduce_max() -> i64 { 89 let a_raw: *u8 = sys_mmap(64) 90 let a: *i64 = a_raw as *i64 91 a[0] = pack4_i16(100, 200, 300, 400) 92 a[1] = pack4_i16(500, 600, 700, 800) 93 a[2] = pack4_i16(50, 900, 1000, 1100) 94 a[3] = pack4_i16(1200, 1300, 1400, 9999) // 9999 is the max 95 let v: i64 = __simd_vload_i16_x16(a as *i64) 96 let m: i64 = __simd_vreduce_max_i16_x16(v) 97 if m != 9999 { return 30 } 98 return 0 99} 100 101func t5_vreduce_min_negative() -> i64 { 102 // Tests sign-extension: min across mixed-sign values. 103 let a_raw: *u8 = sys_mmap(64) 104 let a: *i64 = a_raw as *i64 105 a[0] = pack4_i16(100, -200, 300, -400) 106 a[1] = pack4_i16(500, -600, 700, -800) 107 a[2] = pack4_i16(50, -900, 1000, -1100) 108 a[3] = pack4_i16(1200, -1300, 1400, -1500) 109 let v: i64 = __simd_vload_i16_x16(a as *i64) 110 let m: i64 = __simd_vreduce_min_i16_x16(v) 111 if m != -1500 { return 40 } 112 return 0 113} 114 115func t6_vreduce_max_negative() -> i64 { 116 // All-negative vector -- max should still be sign-extended correctly. 117 let a_raw: *u8 = sys_mmap(64) 118 let a: *i64 = a_raw as *i64 119 a[0] = pack4_i16(-100, -200, -300, -400) 120 a[1] = pack4_i16(-500, -600, -700, -800) 121 a[2] = pack4_i16(-50, -900, -1000, -1100) // -50 is the max 122 a[3] = pack4_i16(-1200, -1300, -1400, -1500) 123 let v: i64 = __simd_vload_i16_x16(a as *i64) 124 let m: i64 = __simd_vreduce_max_i16_x16(v) 125 if m != -50 { return 50 } 126 return 0 127} 128 129func main() -> nx_exit { 130 println("=== nx_simd_minmax smoke (i16x16 min/max + reduce) ===" as *u8) 131 if t1_vmin_lanewise() != 0 { println("FAIL T1" as *u8); return 1 } 132 println("T1 vmin lanewise PASS 16 i16 pairwise minima" as *u8) 133 if t2_vmax_lanewise() != 0 { println("FAIL T2" as *u8); return 2 } 134 println("T2 vmax lanewise PASS 16 i16 pairwise maxima" as *u8) 135 if t3_vreduce_min() != 0 { println("FAIL T3" as *u8); return 3 } 136 println("T3 vreduce_min PASS min(100..1500 plus 50) = 50" as *u8) 137 if t4_vreduce_max() != 0 { println("FAIL T4" as *u8); return 4 } 138 println("T4 vreduce_max PASS max(...,9999) = 9999" as *u8) 139 if t5_vreduce_min_negative() != 0 { println("FAIL T5" as *u8); return 5 } 140 println("T5 vred_min mixed PASS min mixed-sign = -1500 (sign-ext correct)" as *u8) 141 if t6_vreduce_max_negative() != 0 { println("FAIL T6" as *u8); return 6 } 142 println("T6 vred_max negs PASS max all-negative = -50 (sign-ext correct)" as *u8) 143 println("" as *u8) 144 println("i16x16 min/max OPTIMAL on RV-V (vmin/vmax.vv + vred*) and x86 (vpminsw/vpmaxsw + cascade)." as *u8) 145 return 0 146}