Source file src/simd/internal/spec/reductions.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //specgen:category Reductions
     6  
     7  package spec
     8  
     9  // ReduceSumInts returns the scalar sum of the elements of x.
    10  //
    11  //	z = x[0] + x[1] + ...
    12  //
    13  //specgen:name ReduceSum
    14  func ReduceSumInts[E Ints | Uints, W Width](x Vec[E, W]) (z E) {
    15  	// Integer addition wraps and is associative, so the order doesn't matter.
    16  	for _, xi := range x {
    17  		z += xi
    18  	}
    19  	return z
    20  }
    21  
    22  // ReduceSumFloats returns the scalar sum of the elements of x.
    23  //
    24  // {{if eq .xL 2}}
    25  //
    26  //	z = x[0] + x[1]
    27  //
    28  // {{else}}
    29  //
    30  // This uses repeated pairwise summation, which may affect the final precision
    31  // of the result.
    32  //
    33  // {{if eq .xL 4}}
    34  //
    35  //	z = (x[0] + x[1]) + (x[2] + x[3])
    36  //
    37  // {{else}}
    38  //
    39  //	z = ((x[0] + x[1]) + (x[2] + x[3])) + ...
    40  //
    41  // {{end}}
    42  //
    43  // {{end}}
    44  //
    45  //specgen:name ReduceSum
    46  func ReduceSumFloats[E Floats, W Width](x Vec[E, W]) (z E) {
    47  	// Vector lengths are powers of two, so every level pairs up evenly.
    48  	tmp := make([]E, x.len()/2, maxScalableWidth/elemBits[E]()/2)
    49  	for i := range tmp {
    50  		tmp[i] = x[2*i] + x[2*i+1]
    51  	}
    52  	for len(tmp) > 1 {
    53  		half := len(tmp) / 2
    54  		for i := range half {
    55  			tmp[i] = tmp[2*i] + tmp[2*i+1]
    56  		}
    57  		tmp = tmp[:half]
    58  	}
    59  	return tmp[0]
    60  }
    61  
    62  // ReduceMax returns the largest element of x.
    63  //
    64  //	z = max(x[0], x[1], ...)
    65  func ReduceMax[E Elt, W Width](x Vec[E, W]) (z E) {
    66  	// TODO: Do we need per-platform handling of non-finites? Since AMD64 is the
    67  	// only platform that's weird about non-finite values and it doesn't have
    68  	// ReduceMax, maybe we don't have to, but if we ever want to emulate this on
    69  	// AMD64, we'd have to decide whether to match AMD64's Max or Go/IEEE Max.
    70  	z = x[0]
    71  	for _, xi := range x[1:] {
    72  		z = max(z, xi)
    73  	}
    74  	return z
    75  }
    76  
    77  // ReduceMin returns the smallest element of x.
    78  //
    79  //	z = min(x[0], x[1], ...)
    80  func ReduceMin[E Elt, W Width](x Vec[E, W]) (z E) {
    81  	// See ReduceMax about the handling of non-finite values.
    82  	z = x[0]
    83  	for _, xi := range x[1:] {
    84  		z = min(z, xi)
    85  	}
    86  	return z
    87  }
    88  

View as plain text