This is an automated email from the ASF dual-hosted git repository.
zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-go.git
The following commit(s) were added to refs/heads/main by this push:
new efba988f perf(compute): add ARM64 NEON arithmetic kernels (#1265)
efba988f is described below
commit efba988f8c8809712e8841bf5f1299eddfe14ae7
Author: Minh Vu <[email protected]>
AuthorDate: Wed Sep 2 19:44:04 2026 +0200
perf(compute): add ARM64 NEON arithmetic kernels (#1265)
## Summary
- **Added** ARM64 NEON kernels for primitive add, subtract, multiply,
absolute value, and negate.
- **Covered** int32, uint32, int64, uint64, float32, and float64 values.
- **Kept** the generic path for unsupported widths and 64-bit integer
multiply.
- **Added** edge cases for vector tails, wrapping arithmetic, minimum
integers, signed zero, and the no-assembly fallback.
## Benchmark
Apple M1 Pro, 3 MiB input, no nulls, median of 3 runs from
`BenchmarkScalarArithmetic`:
| Case | ARM64 NEON | `-tags noasm` | Speedup |
| --- | ---: | ---: | ---: |
| int64 add, array-array | 179,747 ns/op | 759,116 ns/op | 4.22x |
| int64 add, array-scalar | 160,435 ns/op | 697,497 ns/op | 4.35x |
| float64 add, array-array | 182,029 ns/op | 761,779 ns/op | 4.19x |
| float64 add, array-scalar | 174,644 ns/op | 714,281 ns/op | 4.09x |
## Tests
- `go test ./arrow/compute/... -count=1`
- `go test -tags noasm ./arrow/compute/... -count=1`
- `go test -race ./arrow/compute/internal/kernels ./arrow/compute
-count=1`
- `go vet ./arrow/compute/internal/kernels`
- Linux ARM64 and AMD64 cross-builds for the touched packages
---
.../internal/kernels/base_arithmetic_arm64.go | 164 +++
.../internal/kernels/base_arithmetic_arm64.s | 1430 ++++++++++++++++++++
.../internal/kernels/base_arithmetic_arm64_test.go | 410 ++++++
.../internal/kernels/basic_arithmetic_noasm.go | 2 +-
4 files changed, 2005 insertions(+), 1 deletion(-)
diff --git a/arrow/compute/internal/kernels/base_arithmetic_arm64.go
b/arrow/compute/internal/kernels/base_arithmetic_arm64.go
new file mode 100644
index 00000000..4bf92210
--- /dev/null
+++ b/arrow/compute/internal/kernels/base_arithmetic_arm64.go
@@ -0,0 +1,164 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build go1.18 && arm64 && !noasm && !appengine
+
+package kernels
+
+import (
+ "unsafe"
+
+ "github.com/apache/arrow-go/v18/arrow"
+ "github.com/apache/arrow-go/v18/arrow/compute/exec"
+ "golang.org/x/exp/constraints"
+ "golang.org/x/sys/cpu"
+)
+
+//go:noescape
+func _arithmetic_binary_neon(typ int, op int8, inLeft, inRight, out
unsafe.Pointer, len int)
+
+func arithmeticNeon(typ arrow.Type, op ArithmeticOp, left, right, out []byte,
len int) {
+ if len == 0 {
+ return
+ }
+ _arithmetic_binary_neon(int(typ), int8(op), unsafe.Pointer(&left[0]),
unsafe.Pointer(&right[0]), unsafe.Pointer(&out[0]), len)
+}
+
+//go:noescape
+func _arithmetic_arr_scalar_neon(typ int, op int8, inLeft, inRight, out
unsafe.Pointer, len int)
+
+func arithmeticArrScalarNeon(typ arrow.Type, op ArithmeticOp, left []byte,
right unsafe.Pointer, out []byte, len int) {
+ if len == 0 {
+ return
+ }
+ _arithmetic_arr_scalar_neon(int(typ), int8(op),
unsafe.Pointer(&left[0]), right, unsafe.Pointer(&out[0]), len)
+}
+
+//go:noescape
+func _arithmetic_scalar_arr_neon(typ int, op int8, inLeft, inRight, out
unsafe.Pointer, len int)
+
+func arithmeticScalarArrNeon(typ arrow.Type, op ArithmeticOp, left
unsafe.Pointer, right, out []byte, len int) {
+ if len == 0 {
+ return
+ }
+ _arithmetic_scalar_arr_neon(int(typ), int8(op), left,
unsafe.Pointer(&right[0]), unsafe.Pointer(&out[0]), len)
+}
+
+//go:noescape
+func _arithmetic_unary_same_types_neon(typ int, op int8, input, output
unsafe.Pointer, len int)
+
+func arithmeticUnaryNeon(typ arrow.Type, op ArithmeticOp, input, out []byte,
len int) {
+ if len == 0 {
+ return
+ }
+ _arithmetic_unary_same_types_neon(int(typ), int8(op),
unsafe.Pointer(&input[0]), unsafe.Pointer(&out[0]), len)
+}
+
+func normalizeNeonArithmeticOp(op ArithmeticOp) ArithmeticOp {
+ switch op {
+ case OpAddChecked:
+ return OpAdd
+ case OpSubChecked:
+ return OpSub
+ case OpMulChecked:
+ return OpMul
+ case OpAbsoluteValueChecked:
+ return OpAbsoluteValue
+ case OpNegateChecked:
+ return OpNegate
+ default:
+ return op
+ }
+}
+
+func neonIntegralBinarySupported(typ arrow.Type, op ArithmeticOp) bool {
+ switch typ {
+ case arrow.INT32, arrow.UINT32:
+ return op == OpAdd || op == OpSub || op == OpMul
+ case arrow.INT64, arrow.UINT64:
+ return op == OpAdd || op == OpSub
+ default:
+ return false
+ }
+}
+
+func neonIntegralUnarySupported(typ arrow.Type) bool {
+ switch typ {
+ case arrow.INT32, arrow.UINT32, arrow.INT64, arrow.UINT64:
+ return true
+ default:
+ return false
+ }
+}
+
+func getNeonArithmeticBinaryNumeric[T arrow.NumericType](op ArithmeticOp)
binaryOps[T, T, T] {
+ typ := arrow.GetType[T]()
+ return binaryOps[T, T, T]{
+ arrArr: func(_ *exec.KernelCtx, Arg0, Arg1, Out []T) error {
+ arithmeticNeon(typ, op, arrow.GetBytes(Arg0),
arrow.GetBytes(Arg1), arrow.GetBytes(Out), len(Arg0))
+ return nil
+ },
+ arrScalar: func(_ *exec.KernelCtx, Arg0 []T, Arg1 T, Out []T)
error {
+ arithmeticArrScalarNeon(typ, op, arrow.GetBytes(Arg0),
unsafe.Pointer(&Arg1), arrow.GetBytes(Out), len(Arg0))
+ return nil
+ },
+ scalarArr: func(_ *exec.KernelCtx, Arg0 T, Arg1, Out []T) error
{
+ arithmeticScalarArrNeon(typ, op, unsafe.Pointer(&Arg0),
arrow.GetBytes(Arg1), arrow.GetBytes(Out), len(Arg1))
+ return nil
+ },
+ }
+}
+
+func getArithmeticOpIntegral[InT, OutT arrow.UintType | arrow.IntType](op
ArithmeticOp) exec.ArrayKernelExec {
+ typ := arrow.GetType[InT]()
+ if cpu.ARM64.HasASIMD && typ == arrow.GetType[OutT]() {
+ switch op {
+ case OpAdd, OpSub, OpMul:
+ if neonIntegralBinarySupported(typ, op) {
+ return
ScalarBinary(getNeonArithmeticBinaryNumeric[InT](op))
+ }
+ case OpAbsoluteValue, OpNegate:
+ if neonIntegralUnarySupported(typ) {
+ return ScalarUnary(func(_ *exec.KernelCtx, arg,
out []InT) error {
+ arithmeticUnaryNeon(typ, op,
arrow.GetBytes(arg), arrow.GetBytes(out), len(arg))
+ return nil
+ })
+ }
+ }
+ }
+
+ // no SIMD for POWER or SQRT functions
+ // integral checked funcs need to use NotNull versions
+ return getGoArithmeticOpIntegral[InT, OutT](op)
+}
+
+func getArithmeticOpFloating[InT, OutT constraints.Float](op ArithmeticOp)
exec.ArrayKernelExec {
+ if cpu.ARM64.HasASIMD && arrow.GetType[InT]() == arrow.GetType[OutT]() {
+ typ := arrow.GetType[InT]()
+ switch op {
+ case OpAdd, OpSub, OpAddChecked, OpSubChecked, OpMul,
OpMulChecked:
+ return
ScalarBinary(getNeonArithmeticBinaryNumeric[InT](normalizeNeonArithmeticOp(op)))
+ case OpAbsoluteValue, OpAbsoluteValueChecked, OpNegate,
OpNegateChecked:
+ return ScalarUnary(func(_ *exec.KernelCtx, arg, out
[]InT) error {
+ arithmeticUnaryNeon(typ,
normalizeNeonArithmeticOp(op), arrow.GetBytes(arg), arrow.GetBytes(out),
len(arg))
+ return nil
+ })
+ }
+ }
+
+ // no SIMD for POWER or SQRT functions
+ return getGoArithmeticOpFloating[InT, OutT](op)
+}
diff --git a/arrow/compute/internal/kernels/base_arithmetic_arm64.s
b/arrow/compute/internal/kernels/base_arithmetic_arm64.s
new file mode 100644
index 00000000..bd0e6552
--- /dev/null
+++ b/arrow/compute/internal/kernels/base_arithmetic_arm64.s
@@ -0,0 +1,1430 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build go1.18 && arm64 && !noasm && !appengine
+
+#include "textflag.h"
+
+TEXT ·_arithmetic_binary_neon(SB), NOSPLIT|NOFRAME, $0-48
+ MOVD typ+0(FP), R0
+ MOVB op+8(FP), R1
+ MOVD inLeft+16(FP), R2
+ MOVD inRight+24(FP), R3
+ MOVD out+32(FP), R4
+ MOVD len+40(FP), R5
+
+ CMP $6, R0
+ BEQ LneonBinary32Int
+ CMP $7, R0
+ BEQ LneonBinary32Int
+ CMP $8, R0
+ BEQ LneonBinary64Int
+ CMP $9, R0
+ BEQ LneonBinary64Int
+ CMP $11, R0
+ BEQ LneonBinary32Float
+ CMP $12, R0
+ BEQ LneonBinary64Float
+ RET
+
+LneonBinary32Int:
+ CMP $0, R1
+ BEQ LneonBinary32IntAdd
+ CMP $1, R1
+ BEQ LneonBinary32IntSub
+ CMP $2, R1
+ BEQ LneonBinary32IntMul
+ RET
+
+LneonBinary32IntAdd:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary32IntAddVector:
+ CMP $4, R5
+ BLT LneonBinary32IntAddTail
+ VLD1 (R2), [V0.S4]
+ VLD1 (R3), [V1.S4]
+ VADD V1.S4, V0.S4, V0.S4
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonBinary32IntAddVector
+LneonBinary32IntAddTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ ADDW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary32IntAddTail
+
+LneonBinary32IntSub:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary32IntSubVector:
+ CMP $4, R5
+ BLT LneonBinary32IntSubTail
+ VLD1 (R2), [V0.S4]
+ VLD1 (R3), [V1.S4]
+ VSUB V1.S4, V0.S4, V0.S4
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonBinary32IntSubVector
+LneonBinary32IntSubTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ SUBW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary32IntSubTail
+
+LneonBinary32IntMul:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary32IntMulVector:
+ CMP $4, R5
+ BLT LneonBinary32IntMulTail
+ VLD1 (R2), [V0.S4]
+ VLD1 (R3), [V1.S4]
+ WORD $0x4ea09c20 // mul v0.4s, v1.4s, v0.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonBinary32IntMulVector
+LneonBinary32IntMulTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ MULW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary32IntMulTail
+
+LneonBinary64Int:
+ CMP $0, R1
+ BEQ LneonBinary64IntAdd
+ CMP $1, R1
+ BEQ LneonBinary64IntSub
+ RET
+
+LneonBinary64IntAdd:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary64IntAddVector:
+ CMP $2, R5
+ BLT LneonBinary64IntAddTail
+ VLD1 (R2), [V0.D2]
+ VLD1 (R3), [V1.D2]
+ VADD V1.D2, V0.D2, V0.D2
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonBinary64IntAddVector
+LneonBinary64IntAddTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ MOVD (R2), R6
+ MOVD (R3), R7
+ ADD R7, R6, R6
+ MOVD R6, (R4)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary64IntAddTail
+
+LneonBinary64IntSub:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary64IntSubVector:
+ CMP $2, R5
+ BLT LneonBinary64IntSubTail
+ VLD1 (R2), [V0.D2]
+ VLD1 (R3), [V1.D2]
+ VSUB V1.D2, V0.D2, V0.D2
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonBinary64IntSubVector
+LneonBinary64IntSubTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ MOVD (R2), R6
+ MOVD (R3), R7
+ SUB R7, R6, R6
+ MOVD R6, (R4)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary64IntSubTail
+
+LneonBinary32Float:
+ CMP $0, R1
+ BEQ LneonBinary32FloatAdd
+ CMP $1, R1
+ BEQ LneonBinary32FloatSub
+ CMP $2, R1
+ BEQ LneonBinary32FloatMul
+ RET
+
+LneonBinary32FloatAdd:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary32FloatAddVector:
+ CMP $4, R5
+ BLT LneonBinary32FloatAddTail
+ VLD1 (R2), [V0.S4]
+ VLD1 (R3), [V1.S4]
+ WORD $0x4e21d400 // fadd v0.4s, v0.4s, v1.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonBinary32FloatAddVector
+LneonBinary32FloatAddTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FADDS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary32FloatAddTail
+
+LneonBinary32FloatSub:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary32FloatSubVector:
+ CMP $4, R5
+ BLT LneonBinary32FloatSubTail
+ VLD1 (R2), [V0.S4]
+ VLD1 (R3), [V1.S4]
+ WORD $0x4ea1d400 // fsub v0.4s, v0.4s, v1.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonBinary32FloatSubVector
+LneonBinary32FloatSubTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FSUBS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary32FloatSubTail
+
+LneonBinary32FloatMul:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary32FloatMulVector:
+ CMP $4, R5
+ BLT LneonBinary32FloatMulTail
+ VLD1 (R2), [V0.S4]
+ VLD1 (R3), [V1.S4]
+ WORD $0x6e21dc00 // fmul v0.4s, v0.4s, v1.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonBinary32FloatMulVector
+LneonBinary32FloatMulTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FMULS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary32FloatMulTail
+
+LneonBinary64Float:
+ CMP $0, R1
+ BEQ LneonBinary64FloatAdd
+ CMP $1, R1
+ BEQ LneonBinary64FloatSub
+ CMP $2, R1
+ BEQ LneonBinary64FloatMul
+ RET
+
+LneonBinary64FloatAdd:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary64FloatAddVector:
+ CMP $2, R5
+ BLT LneonBinary64FloatAddTail
+ VLD1 (R2), [V0.D2]
+ VLD1 (R3), [V1.D2]
+ WORD $0x4e61d400 // fadd v0.2d, v0.2d, v1.2d
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonBinary64FloatAddVector
+LneonBinary64FloatAddTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FADDD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary64FloatAddTail
+
+LneonBinary64FloatSub:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary64FloatSubVector:
+ CMP $2, R5
+ BLT LneonBinary64FloatSubTail
+ VLD1 (R2), [V0.D2]
+ VLD1 (R3), [V1.D2]
+ WORD $0x4ee1d400 // fsub v0.2d, v0.2d, v1.2d
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonBinary64FloatSubVector
+LneonBinary64FloatSubTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FSUBD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary64FloatSubTail
+
+LneonBinary64FloatMul:
+ CMP $0, R5
+ BLE LneonBinaryReturn
+LneonBinary64FloatMulVector:
+ CMP $2, R5
+ BLT LneonBinary64FloatMulTail
+ VLD1 (R2), [V0.D2]
+ VLD1 (R3), [V1.D2]
+ WORD $0x6e61dc00 // fmul v0.2d, v0.2d, v1.2d
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonBinary64FloatMulVector
+LneonBinary64FloatMulTail:
+ CMP $0, R5
+ BEQ LneonBinaryReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FMULD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonBinary64FloatMulTail
+
+LneonBinaryReturn:
+ RET
+TEXT ·_arithmetic_arr_scalar_neon(SB), NOSPLIT|NOFRAME, $0-48
+ MOVD typ+0(FP), R0
+ MOVB op+8(FP), R1
+ MOVD inLeft+16(FP), R2
+ MOVD inRight+24(FP), R3
+ MOVD out+32(FP), R4
+ MOVD len+40(FP), R5
+
+ CMP $6, R0
+ BEQ LneonArrScalar32Int
+ CMP $7, R0
+ BEQ LneonArrScalar32Int
+ CMP $8, R0
+ BEQ LneonArrScalar64Int
+ CMP $9, R0
+ BEQ LneonArrScalar64Int
+ CMP $11, R0
+ BEQ LneonArrScalar32Float
+ CMP $12, R0
+ BEQ LneonArrScalar64Float
+ RET
+
+LneonArrScalar32Int:
+ CMP $0, R1
+ BEQ LneonArrScalar32IntAdd
+ CMP $1, R1
+ BEQ LneonArrScalar32IntSub
+ CMP $2, R1
+ BEQ LneonArrScalar32IntMul
+ RET
+
+LneonArrScalar32IntAdd:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.S4]
+LneonArrScalar32IntAddVector:
+ CMP $4, R5
+ BLT LneonArrScalar32IntAddTail
+ VLD1 (R2), [V0.S4]
+ VADD V1.S4, V0.S4, V0.S4
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonArrScalar32IntAddVector
+LneonArrScalar32IntAddTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ ADDW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R2, R2
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar32IntAddTail
+
+LneonArrScalar32IntSub:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.S4]
+LneonArrScalar32IntSubVector:
+ CMP $4, R5
+ BLT LneonArrScalar32IntSubTail
+ VLD1 (R2), [V0.S4]
+ VSUB V1.S4, V0.S4, V0.S4
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonArrScalar32IntSubVector
+LneonArrScalar32IntSubTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ SUBW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R2, R2
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar32IntSubTail
+
+LneonArrScalar32IntMul:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.S4]
+LneonArrScalar32IntMulVector:
+ CMP $4, R5
+ BLT LneonArrScalar32IntMulTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x4ea09c20 // mul v0.4s, v1.4s, v0.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonArrScalar32IntMulVector
+LneonArrScalar32IntMulTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ MULW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R2, R2
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar32IntMulTail
+
+LneonArrScalar64Int:
+ CMP $0, R1
+ BEQ LneonArrScalar64IntAdd
+ CMP $1, R1
+ BEQ LneonArrScalar64IntSub
+ RET
+
+LneonArrScalar64IntAdd:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.D2]
+LneonArrScalar64IntAddVector:
+ CMP $2, R5
+ BLT LneonArrScalar64IntAddTail
+ VLD1 (R2), [V0.D2]
+ VADD V1.D2, V0.D2, V0.D2
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonArrScalar64IntAddVector
+LneonArrScalar64IntAddTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ MOVD (R2), R6
+ MOVD (R3), R7
+ ADD R7, R6, R6
+ MOVD R6, (R4)
+ ADD $8, R2, R2
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar64IntAddTail
+
+LneonArrScalar64IntSub:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.D2]
+LneonArrScalar64IntSubVector:
+ CMP $2, R5
+ BLT LneonArrScalar64IntSubTail
+ VLD1 (R2), [V0.D2]
+ VSUB V1.D2, V0.D2, V0.D2
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonArrScalar64IntSubVector
+LneonArrScalar64IntSubTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ MOVD (R2), R6
+ MOVD (R3), R7
+ SUB R7, R6, R6
+ MOVD R6, (R4)
+ ADD $8, R2, R2
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar64IntSubTail
+
+LneonArrScalar32Float:
+ CMP $0, R1
+ BEQ LneonArrScalar32FloatAdd
+ CMP $1, R1
+ BEQ LneonArrScalar32FloatSub
+ CMP $2, R1
+ BEQ LneonArrScalar32FloatMul
+ RET
+
+LneonArrScalar32FloatAdd:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.S4]
+LneonArrScalar32FloatAddVector:
+ CMP $4, R5
+ BLT LneonArrScalar32FloatAddTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x4e21d400 // fadd v0.4s, v0.4s, v1.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonArrScalar32FloatAddVector
+LneonArrScalar32FloatAddTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FADDS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R2, R2
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar32FloatAddTail
+
+LneonArrScalar32FloatSub:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.S4]
+LneonArrScalar32FloatSubVector:
+ CMP $4, R5
+ BLT LneonArrScalar32FloatSubTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x4ea1d400 // fsub v0.4s, v0.4s, v1.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonArrScalar32FloatSubVector
+LneonArrScalar32FloatSubTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FSUBS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R2, R2
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar32FloatSubTail
+
+LneonArrScalar32FloatMul:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.S4]
+LneonArrScalar32FloatMulVector:
+ CMP $4, R5
+ BLT LneonArrScalar32FloatMulTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x6e21dc00 // fmul v0.4s, v0.4s, v1.4s
+ VST1 [V0.S4], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonArrScalar32FloatMulVector
+LneonArrScalar32FloatMulTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FMULS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R2, R2
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar32FloatMulTail
+
+LneonArrScalar64Float:
+ CMP $0, R1
+ BEQ LneonArrScalar64FloatAdd
+ CMP $1, R1
+ BEQ LneonArrScalar64FloatSub
+ CMP $2, R1
+ BEQ LneonArrScalar64FloatMul
+ RET
+
+LneonArrScalar64FloatAdd:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.D2]
+LneonArrScalar64FloatAddVector:
+ CMP $2, R5
+ BLT LneonArrScalar64FloatAddTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x4e61d400 // fadd v0.2d, v0.2d, v1.2d
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonArrScalar64FloatAddVector
+LneonArrScalar64FloatAddTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FADDD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R2, R2
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar64FloatAddTail
+
+LneonArrScalar64FloatSub:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.D2]
+LneonArrScalar64FloatSubVector:
+ CMP $2, R5
+ BLT LneonArrScalar64FloatSubTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x4ee1d400 // fsub v0.2d, v0.2d, v1.2d
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonArrScalar64FloatSubVector
+LneonArrScalar64FloatSubTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FSUBD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R2, R2
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar64FloatSubTail
+
+LneonArrScalar64FloatMul:
+ CMP $0, R5
+ BLE LneonArrScalarReturn
+ VLD1R (R3), [V1.D2]
+LneonArrScalar64FloatMulVector:
+ CMP $2, R5
+ BLT LneonArrScalar64FloatMulTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x6e61dc00 // fmul v0.2d, v0.2d, v1.2d
+ VST1 [V0.D2], (R4)
+ ADD $16, R2, R2
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonArrScalar64FloatMulVector
+LneonArrScalar64FloatMulTail:
+ CMP $0, R5
+ BEQ LneonArrScalarReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FMULD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R2, R2
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonArrScalar64FloatMulTail
+
+LneonArrScalarReturn:
+ RET
+
+TEXT ·_arithmetic_scalar_arr_neon(SB), NOSPLIT|NOFRAME, $0-48
+ MOVD typ+0(FP), R0
+ MOVB op+8(FP), R1
+ MOVD inLeft+16(FP), R2
+ MOVD inRight+24(FP), R3
+ MOVD out+32(FP), R4
+ MOVD len+40(FP), R5
+
+ CMP $6, R0
+ BEQ LneonScalarArr32Int
+ CMP $7, R0
+ BEQ LneonScalarArr32Int
+ CMP $8, R0
+ BEQ LneonScalarArr64Int
+ CMP $9, R0
+ BEQ LneonScalarArr64Int
+ CMP $11, R0
+ BEQ LneonScalarArr32Float
+ CMP $12, R0
+ BEQ LneonScalarArr64Float
+ RET
+
+LneonScalarArr32Int:
+ CMP $0, R1
+ BEQ LneonScalarArr32IntAdd
+ CMP $1, R1
+ BEQ LneonScalarArr32IntSub
+ CMP $2, R1
+ BEQ LneonScalarArr32IntMul
+ RET
+
+LneonScalarArr32IntAdd:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.S4]
+LneonScalarArr32IntAddVector:
+ CMP $4, R5
+ BLT LneonScalarArr32IntAddTail
+ VLD1 (R3), [V1.S4]
+ VADD V1.S4, V0.S4, V2.S4
+ VST1 [V2.S4], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonScalarArr32IntAddVector
+LneonScalarArr32IntAddTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ ADDW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr32IntAddTail
+
+LneonScalarArr32IntSub:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.S4]
+LneonScalarArr32IntSubVector:
+ CMP $4, R5
+ BLT LneonScalarArr32IntSubTail
+ VLD1 (R3), [V1.S4]
+ VSUB V1.S4, V0.S4, V2.S4
+ VST1 [V2.S4], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonScalarArr32IntSubVector
+LneonScalarArr32IntSubTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ SUBW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr32IntSubTail
+
+LneonScalarArr32IntMul:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.S4]
+LneonScalarArr32IntMulVector:
+ CMP $4, R5
+ BLT LneonScalarArr32IntMulTail
+ VLD1 (R3), [V1.S4]
+ WORD $0x4ea09c22 // mul v2.4s, v1.4s, v0.4s
+ VST1 [V2.S4], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonScalarArr32IntMulVector
+LneonScalarArr32IntMulTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ MOVW (R2), R6
+ MOVW (R3), R7
+ MULW R7, R6, R6
+ MOVW R6, (R4)
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr32IntMulTail
+
+LneonScalarArr64Int:
+ CMP $0, R1
+ BEQ LneonScalarArr64IntAdd
+ CMP $1, R1
+ BEQ LneonScalarArr64IntSub
+ RET
+
+LneonScalarArr64IntAdd:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.D2]
+LneonScalarArr64IntAddVector:
+ CMP $2, R5
+ BLT LneonScalarArr64IntAddTail
+ VLD1 (R3), [V1.D2]
+ VADD V1.D2, V0.D2, V2.D2
+ VST1 [V2.D2], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonScalarArr64IntAddVector
+LneonScalarArr64IntAddTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ MOVD (R2), R6
+ MOVD (R3), R7
+ ADD R7, R6, R6
+ MOVD R6, (R4)
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr64IntAddTail
+
+LneonScalarArr64IntSub:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.D2]
+LneonScalarArr64IntSubVector:
+ CMP $2, R5
+ BLT LneonScalarArr64IntSubTail
+ VLD1 (R3), [V1.D2]
+ VSUB V1.D2, V0.D2, V2.D2
+ VST1 [V2.D2], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonScalarArr64IntSubVector
+LneonScalarArr64IntSubTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ MOVD (R2), R6
+ MOVD (R3), R7
+ SUB R7, R6, R6
+ MOVD R6, (R4)
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr64IntSubTail
+
+LneonScalarArr32Float:
+ CMP $0, R1
+ BEQ LneonScalarArr32FloatAdd
+ CMP $1, R1
+ BEQ LneonScalarArr32FloatSub
+ CMP $2, R1
+ BEQ LneonScalarArr32FloatMul
+ RET
+
+LneonScalarArr32FloatAdd:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.S4]
+LneonScalarArr32FloatAddVector:
+ CMP $4, R5
+ BLT LneonScalarArr32FloatAddTail
+ VLD1 (R3), [V1.S4]
+ WORD $0x4e21d402 // fadd v2.4s, v0.4s, v1.4s
+ VST1 [V2.S4], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonScalarArr32FloatAddVector
+LneonScalarArr32FloatAddTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FADDS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr32FloatAddTail
+
+LneonScalarArr32FloatSub:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.S4]
+LneonScalarArr32FloatSubVector:
+ CMP $4, R5
+ BLT LneonScalarArr32FloatSubTail
+ VLD1 (R3), [V1.S4]
+ WORD $0x4ea1d402 // fsub v2.4s, v0.4s, v1.4s
+ VST1 [V2.S4], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonScalarArr32FloatSubVector
+LneonScalarArr32FloatSubTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FSUBS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr32FloatSubTail
+
+LneonScalarArr32FloatMul:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.S4]
+LneonScalarArr32FloatMulVector:
+ CMP $4, R5
+ BLT LneonScalarArr32FloatMulTail
+ VLD1 (R3), [V1.S4]
+ WORD $0x6e21dc02 // fmul v2.4s, v0.4s, v1.4s
+ VST1 [V2.S4], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $4, R5, R5
+ JMP LneonScalarArr32FloatMulVector
+LneonScalarArr32FloatMulTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ FMOVS (R2), F0
+ FMOVS (R3), F1
+ FMULS F1, F0, F0
+ FMOVS F0, (R4)
+ ADD $4, R3, R3
+ ADD $4, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr32FloatMulTail
+
+LneonScalarArr64Float:
+ CMP $0, R1
+ BEQ LneonScalarArr64FloatAdd
+ CMP $1, R1
+ BEQ LneonScalarArr64FloatSub
+ CMP $2, R1
+ BEQ LneonScalarArr64FloatMul
+ RET
+
+LneonScalarArr64FloatAdd:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.D2]
+LneonScalarArr64FloatAddVector:
+ CMP $2, R5
+ BLT LneonScalarArr64FloatAddTail
+ VLD1 (R3), [V1.D2]
+ WORD $0x4e61d402 // fadd v2.2d, v0.2d, v1.2d
+ VST1 [V2.D2], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonScalarArr64FloatAddVector
+LneonScalarArr64FloatAddTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FADDD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr64FloatAddTail
+
+LneonScalarArr64FloatSub:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.D2]
+LneonScalarArr64FloatSubVector:
+ CMP $2, R5
+ BLT LneonScalarArr64FloatSubTail
+ VLD1 (R3), [V1.D2]
+ WORD $0x4ee1d402 // fsub v2.2d, v0.2d, v1.2d
+ VST1 [V2.D2], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonScalarArr64FloatSubVector
+LneonScalarArr64FloatSubTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FSUBD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr64FloatSubTail
+
+LneonScalarArr64FloatMul:
+ CMP $0, R5
+ BLE LneonScalarArrReturn
+ VLD1R (R2), [V0.D2]
+LneonScalarArr64FloatMulVector:
+ CMP $2, R5
+ BLT LneonScalarArr64FloatMulTail
+ VLD1 (R3), [V1.D2]
+ WORD $0x6e61dc02 // fmul v2.2d, v0.2d, v1.2d
+ VST1 [V2.D2], (R4)
+ ADD $16, R3, R3
+ ADD $16, R4, R4
+ SUB $2, R5, R5
+ JMP LneonScalarArr64FloatMulVector
+LneonScalarArr64FloatMulTail:
+ CMP $0, R5
+ BEQ LneonScalarArrReturn
+ FMOVD (R2), F0
+ FMOVD (R3), F1
+ FMULD F1, F0, F0
+ FMOVD F0, (R4)
+ ADD $8, R3, R3
+ ADD $8, R4, R4
+ SUB $1, R5, R5
+ JMP LneonScalarArr64FloatMulTail
+
+LneonScalarArrReturn:
+ RET
+TEXT ·_arithmetic_unary_same_types_neon(SB), NOSPLIT|NOFRAME, $0-40
+ MOVD typ+0(FP), R0
+ MOVB op+8(FP), R1
+ MOVD input+16(FP), R2
+ MOVD output+24(FP), R3
+ MOVD len+32(FP), R5
+
+ CMP $7, R0
+ BEQ LneonUnary32SignedInt
+ CMP $6, R0
+ BEQ LneonUnary32UnsignedInt
+ CMP $9, R0
+ BEQ LneonUnary64SignedInt
+ CMP $8, R0
+ BEQ LneonUnary64UnsignedInt
+ CMP $11, R0
+ BEQ LneonUnary32Float
+ CMP $12, R0
+ BEQ LneonUnary64Float
+ RET
+
+LneonUnary32SignedInt:
+ CMP $4, R1
+ BEQ LneonUnary32SignedIntAbs
+ CMP $5, R1
+ BEQ LneonUnary32SignedIntNeg
+ RET
+
+LneonUnary32SignedIntAbs:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary32SignedIntAbsVector:
+ CMP $4, R5
+ BLT LneonUnary32SignedIntAbsTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x4ea0b800 // abs v0.4s, v0.4s
+ VST1 [V0.S4], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $4, R5, R5
+ JMP LneonUnary32SignedIntAbsVector
+LneonUnary32SignedIntAbsTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVW (R2), R6
+ CMPW $0, R6
+ BGE LneonUnary32SignedIntAbsStore
+ NEGW R6, R6
+LneonUnary32SignedIntAbsStore:
+ MOVW R6, (R3)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary32SignedIntAbsTail
+
+LneonUnary32SignedIntNeg:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary32SignedIntNegVector:
+ CMP $4, R5
+ BLT LneonUnary32SignedIntNegTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x6ea0b800 // neg v0.4s, v0.4s
+ VST1 [V0.S4], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $4, R5, R5
+ JMP LneonUnary32SignedIntNegVector
+LneonUnary32SignedIntNegTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVW (R2), R6
+ NEGW R6, R6
+ MOVW R6, (R3)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary32SignedIntNegTail
+
+LneonUnary32UnsignedInt:
+ CMP $4, R1
+ BEQ LneonUnary32UnsignedIntAbs
+ CMP $5, R1
+ BEQ LneonUnary32UnsignedIntNeg
+ RET
+
+LneonUnary32UnsignedIntAbs:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary32UnsignedIntAbsVector:
+ CMP $4, R5
+ BLT LneonUnary32UnsignedIntAbsTail
+ VLD1 (R2), [V0.S4]
+ VST1 [V0.S4], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $4, R5, R5
+ JMP LneonUnary32UnsignedIntAbsVector
+LneonUnary32UnsignedIntAbsTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVW (R2), R6
+ MOVW R6, (R3)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary32UnsignedIntAbsTail
+
+LneonUnary32UnsignedIntNeg:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary32UnsignedIntNegVector:
+ CMP $4, R5
+ BLT LneonUnary32UnsignedIntNegTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x6ea0b800 // neg v0.4s, v0.4s
+ VST1 [V0.S4], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $4, R5, R5
+ JMP LneonUnary32UnsignedIntNegVector
+LneonUnary32UnsignedIntNegTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVW (R2), R6
+ NEGW R6, R6
+ MOVW R6, (R3)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary32UnsignedIntNegTail
+
+LneonUnary64SignedInt:
+ CMP $4, R1
+ BEQ LneonUnary64SignedIntAbs
+ CMP $5, R1
+ BEQ LneonUnary64SignedIntNeg
+ RET
+
+LneonUnary64SignedIntAbs:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary64SignedIntAbsVector:
+ CMP $2, R5
+ BLT LneonUnary64SignedIntAbsTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x4ee0b800 // abs v0.2d, v0.2d
+ VST1 [V0.D2], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $2, R5, R5
+ JMP LneonUnary64SignedIntAbsVector
+LneonUnary64SignedIntAbsTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVD (R2), R6
+ CMP $0, R6
+ BGE LneonUnary64SignedIntAbsStore
+ NEG R6, R6
+LneonUnary64SignedIntAbsStore:
+ MOVD R6, (R3)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary64SignedIntAbsTail
+
+LneonUnary64SignedIntNeg:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary64SignedIntNegVector:
+ CMP $2, R5
+ BLT LneonUnary64SignedIntNegTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x6ee0b800 // neg v0.2d, v0.2d
+ VST1 [V0.D2], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $2, R5, R5
+ JMP LneonUnary64SignedIntNegVector
+LneonUnary64SignedIntNegTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVD (R2), R6
+ NEG R6, R6
+ MOVD R6, (R3)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary64SignedIntNegTail
+
+LneonUnary64UnsignedInt:
+ CMP $4, R1
+ BEQ LneonUnary64UnsignedIntAbs
+ CMP $5, R1
+ BEQ LneonUnary64UnsignedIntNeg
+ RET
+
+LneonUnary64UnsignedIntAbs:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary64UnsignedIntAbsVector:
+ CMP $2, R5
+ BLT LneonUnary64UnsignedIntAbsTail
+ VLD1 (R2), [V0.D2]
+ VST1 [V0.D2], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $2, R5, R5
+ JMP LneonUnary64UnsignedIntAbsVector
+LneonUnary64UnsignedIntAbsTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVD (R2), R6
+ MOVD R6, (R3)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary64UnsignedIntAbsTail
+
+LneonUnary64UnsignedIntNeg:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary64UnsignedIntNegVector:
+ CMP $2, R5
+ BLT LneonUnary64UnsignedIntNegTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x6ee0b800 // neg v0.2d, v0.2d
+ VST1 [V0.D2], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $2, R5, R5
+ JMP LneonUnary64UnsignedIntNegVector
+LneonUnary64UnsignedIntNegTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ MOVD (R2), R6
+ NEG R6, R6
+ MOVD R6, (R3)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary64UnsignedIntNegTail
+
+LneonUnary32Float:
+ CMP $4, R1
+ BEQ LneonUnary32FloatAbs
+ CMP $5, R1
+ BEQ LneonUnary32FloatNeg
+ RET
+
+LneonUnary32FloatAbs:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary32FloatAbsVector:
+ CMP $4, R5
+ BLT LneonUnary32FloatAbsTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x4ea0f800 // fabs v0.4s, v0.4s
+ VST1 [V0.S4], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $4, R5, R5
+ JMP LneonUnary32FloatAbsVector
+LneonUnary32FloatAbsTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ FMOVS (R2), F0
+ FABSS F0, F0
+ FMOVS F0, (R3)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary32FloatAbsTail
+
+LneonUnary32FloatNeg:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary32FloatNegVector:
+ CMP $4, R5
+ BLT LneonUnary32FloatNegTail
+ VLD1 (R2), [V0.S4]
+ WORD $0x6ea0f800 // fneg v0.4s, v0.4s
+ VST1 [V0.S4], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $4, R5, R5
+ JMP LneonUnary32FloatNegVector
+LneonUnary32FloatNegTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ FMOVS (R2), F0
+ FNEGS F0, F0
+ FMOVS F0, (R3)
+ ADD $4, R2, R2
+ ADD $4, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary32FloatNegTail
+
+LneonUnary64Float:
+ CMP $4, R1
+ BEQ LneonUnary64FloatAbs
+ CMP $5, R1
+ BEQ LneonUnary64FloatNeg
+ RET
+
+LneonUnary64FloatAbs:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary64FloatAbsVector:
+ CMP $2, R5
+ BLT LneonUnary64FloatAbsTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x4ee0f800 // fabs v0.2d, v0.2d
+ VST1 [V0.D2], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $2, R5, R5
+ JMP LneonUnary64FloatAbsVector
+LneonUnary64FloatAbsTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ FMOVD (R2), F0
+ FABSD F0, F0
+ FMOVD F0, (R3)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary64FloatAbsTail
+
+LneonUnary64FloatNeg:
+ CMP $0, R5
+ BLE LneonUnaryReturn
+LneonUnary64FloatNegVector:
+ CMP $2, R5
+ BLT LneonUnary64FloatNegTail
+ VLD1 (R2), [V0.D2]
+ WORD $0x6ee0f800 // fneg v0.2d, v0.2d
+ VST1 [V0.D2], (R3)
+ ADD $16, R2, R2
+ ADD $16, R3, R3
+ SUB $2, R5, R5
+ JMP LneonUnary64FloatNegVector
+LneonUnary64FloatNegTail:
+ CMP $0, R5
+ BEQ LneonUnaryReturn
+ FMOVD (R2), F0
+ FNEGD F0, F0
+ FMOVD F0, (R3)
+ ADD $8, R2, R2
+ ADD $8, R3, R3
+ SUB $1, R5, R5
+ JMP LneonUnary64FloatNegTail
+
+LneonUnaryReturn:
+ RET
diff --git a/arrow/compute/internal/kernels/base_arithmetic_arm64_test.go
b/arrow/compute/internal/kernels/base_arithmetic_arm64_test.go
new file mode 100644
index 00000000..07f5ec39
--- /dev/null
+++ b/arrow/compute/internal/kernels/base_arithmetic_arm64_test.go
@@ -0,0 +1,410 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build go1.18 && arm64 && !noasm && !appengine
+
+package kernels
+
+import (
+ "fmt"
+ "math"
+ "runtime"
+ "testing"
+ "unsafe"
+
+ "github.com/apache/arrow-go/v18/arrow"
+ "golang.org/x/sys/cpu"
+)
+
+func checkNeonBinary[T arrow.NumericType](t *testing.T, typ arrow.Type, op
ArithmeticOp, left, right []T, want func(T, T) T) {
+ t.Helper()
+
+ got := make([]T, len(left))
+ wantValues := make([]T, len(left))
+ for i := range left {
+ wantValues[i] = want(left[i], right[i])
+ }
+ arithmeticNeon(typ, op, arrow.GetBytes(left), arrow.GetBytes(right),
arrow.GetBytes(got), len(left))
+ if !neonArithmeticEqual(got, wantValues) {
+ t.Fatalf("array-array: got %v, want %v", got, wantValues)
+ }
+
+ var scalar T
+ if len(right) != 0 {
+ scalar = right[0]
+ }
+ wantValues = make([]T, len(left))
+ for i := range left {
+ wantValues[i] = want(left[i], scalar)
+ }
+ arithmeticArrScalarNeon(typ, op, arrow.GetBytes(left),
unsafe.Pointer(&scalar), arrow.GetBytes(got), len(left))
+ if !neonArithmeticEqual(got, wantValues) {
+ t.Fatalf("array-scalar: got %v, want %v", got, wantValues)
+ }
+
+ if len(left) != 0 {
+ scalar = left[0]
+ }
+ wantValues = make([]T, len(right))
+ for i := range right {
+ wantValues[i] = want(scalar, right[i])
+ }
+ arithmeticScalarArrNeon(typ, op, unsafe.Pointer(&scalar),
arrow.GetBytes(right), arrow.GetBytes(got), len(right))
+ if !neonArithmeticEqual(got[:len(right)], wantValues) {
+ t.Fatalf("scalar-array: got %v, want %v", got[:len(right)],
wantValues)
+ }
+}
+
+func checkNeonUnary[T arrow.NumericType](t *testing.T, typ arrow.Type, op
ArithmeticOp, input []T, want func(T) T) {
+ t.Helper()
+
+ got := make([]T, len(input))
+ wantValues := make([]T, len(input))
+ for i, value := range input {
+ wantValues[i] = want(value)
+ }
+ arithmeticUnaryNeon(typ, op, arrow.GetBytes(input),
arrow.GetBytes(got), len(input))
+ if !neonArithmeticEqual(got, wantValues) {
+ t.Fatalf("got %v, want %v", got, wantValues)
+ }
+}
+
+func neonArithmeticEqual[T arrow.NumericType](got, want []T) bool {
+ if len(got) != len(want) {
+ return false
+ }
+ for i, value := range got {
+ if value == want[i] {
+ if value == 0 && math.Signbit(float64(value)) !=
math.Signbit(float64(want[i])) {
+ return false
+ }
+ } else if !math.IsNaN(float64(value)) ||
!math.IsNaN(float64(want[i])) {
+ return false
+ }
+ }
+ return true
+}
+
+func TestNeonArithmeticFloatingSpecialValues(t *testing.T) {
+ if !cpu.ARM64.HasASIMD {
+ t.Skip("ARM64 SIMD is not available")
+ }
+ t.Run("float32", func(t *testing.T) {
+ testNeonArithmeticFloatingSpecialValues(t, arrow.FLOAT32,
[]float32{
+ float32(math.Copysign(0, -1)), 0, float32(math.Inf(1)),
float32(math.Inf(-1)),
+ float32(math.NaN()), math.SmallestNonzeroFloat32,
-math.SmallestNonzeroFloat32,
+ math.MaxFloat32, -math.MaxFloat32, 1, -1,
+ })
+ })
+ t.Run("float64", func(t *testing.T) {
+ testNeonArithmeticFloatingSpecialValues(t, arrow.FLOAT64,
[]float64{
+ math.Copysign(0, -1), 0, math.Inf(1), math.Inf(-1),
math.NaN(),
+ math.SmallestNonzeroFloat64,
-math.SmallestNonzeroFloat64,
+ math.MaxFloat64, -math.MaxFloat64, 1, -1,
+ })
+ })
+}
+
+func testNeonArithmeticFloatingSpecialValues[T float32 | float64](t
*testing.T, typ arrow.Type, pattern []T) {
+ for _, length := range []int{1, 2, 3, 4, 5, 7, 8, 9, 17, 65} {
+ for shift := range pattern {
+ left, right := make([]T, length), make([]T, length)
+ for i := range left {
+ left[i] = pattern[(i+shift)%len(pattern)]
+ right[i] = pattern[(2*i+shift)%len(pattern)]
+ }
+ checkNeonBinary(t, typ, OpAdd, left, right, func(l, r
T) T { return l + r })
+ checkNeonBinary(t, typ, OpSub, left, right, func(l, r
T) T { return l - r })
+ checkNeonBinary(t, typ, OpMul, left, right, func(l, r
T) T { return l * r })
+ checkNeonUnary(t, typ, OpAbsoluteValue, left, func(v T)
T { return T(math.Abs(float64(v))) })
+ checkNeonUnary(t, typ, OpNegate, left, func(v T) T {
return -v })
+ }
+ }
+}
+
+func TestNeonArithmeticBinary(t *testing.T) {
+ if !cpu.ARM64.HasASIMD {
+ t.Skip("ARM64 SIMD is not available")
+ }
+
+ lengths := []int{0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33}
+ for _, n := range lengths {
+ t.Run(fmt.Sprintf("length=%d", n), func(t *testing.T) {
+ int32Left := make([]int32, n)
+ int32Right := make([]int32, n)
+ uint32Left := make([]uint32, n)
+ uint32Right := make([]uint32, n)
+ int64Left := make([]int64, n)
+ int64Right := make([]int64, n)
+ uint64Left := make([]uint64, n)
+ uint64Right := make([]uint64, n)
+ float32Left := make([]float32, n)
+ float32Right := make([]float32, n)
+ float64Left := make([]float64, n)
+ float64Right := make([]float64, n)
+ for i := 0; i < n; i++ {
+ int32Left[i], int32Right[i] = int32(i*3-10),
int32(i+2)
+ uint32Left[i], uint32Right[i] = uint32(i*3+10),
uint32(i+2)
+ int64Left[i], int64Right[i] = int64(i*3-10),
int64(i+2)
+ uint64Left[i], uint64Right[i] = uint64(i*3+10),
uint64(i+2)
+ float32Left[i], float32Right[i] =
float32(i)+0.25, float32(i)*0.5+1.5
+ float64Left[i], float64Right[i] =
float64(i)+0.25, float64(i)*0.5+1.5
+ }
+
+ for _, op := range []ArithmeticOp{OpAdd, OpSub} {
+ checkNeonBinary(t, arrow.INT32, op, int32Left,
int32Right, func(a, b int32) int32 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.UINT32, op,
uint32Left, uint32Right, func(a, b uint32) uint32 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.INT64, op, int64Left,
int64Right, func(a, b int64) int64 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.UINT64, op,
uint64Left, uint64Right, func(a, b uint64) uint64 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.FLOAT32, op,
float32Left, float32Right, func(a, b float32) float32 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.FLOAT64, op,
float64Left, float64Right, func(a, b float64) float64 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ }
+
+ checkNeonBinary(t, arrow.INT32, OpMul, int32Left,
int32Right, func(a, b int32) int32 { return a * b })
+ checkNeonBinary(t, arrow.UINT32, OpMul, uint32Left,
uint32Right, func(a, b uint32) uint32 { return a * b })
+ checkNeonBinary(t, arrow.FLOAT32, OpMul, float32Left,
float32Right, func(a, b float32) float32 { return a * b })
+ checkNeonBinary(t, arrow.FLOAT64, OpMul, float64Left,
float64Right, func(a, b float64) float64 { return a * b })
+ })
+ }
+}
+
+func TestNeonArithmeticUnary(t *testing.T) {
+ if !cpu.ARM64.HasASIMD {
+ t.Skip("ARM64 SIMD is not available")
+ }
+
+ lengths := []int{0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33}
+ for _, n := range lengths {
+ t.Run(fmt.Sprintf("length=%d", n), func(t *testing.T) {
+ int32Values := make([]int32, n)
+ uint32Values := make([]uint32, n)
+ int64Values := make([]int64, n)
+ uint64Values := make([]uint64, n)
+ float32Values := make([]float32, n)
+ float64Values := make([]float64, n)
+ for i := 0; i < n; i++ {
+ int32Values[i] = []int32{0, 1, -1, -1 << 31,
1<<31 - 1}[i%5]
+ uint32Values[i] = []uint32{0, 1, ^uint32(0), 2,
17}[i%5]
+ int64Values[i] = []int64{0, 1, -1, -1 << 63,
1<<63 - 1}[i%5]
+ uint64Values[i] = []uint64{0, 1, ^uint64(0), 2,
17}[i%5]
+ float32Values[i] = []float32{0,
float32(math.Copysign(0, -1)), 1.5, -2.25, 3.75}[i%5]
+ float64Values[i] = []float64{0,
math.Copysign(0, -1), 1.5, -2.25, 3.75}[i%5]
+ }
+
+ checkNeonUnary(t, arrow.INT32, OpAbsoluteValue,
int32Values, func(v int32) int32 {
+ if v < 0 {
+ return -v
+ }
+ return v
+ })
+ checkNeonUnary(t, arrow.INT32, OpNegate, int32Values,
func(v int32) int32 { return -v })
+ checkNeonUnary(t, arrow.UINT32, OpAbsoluteValue,
uint32Values, func(v uint32) uint32 { return v })
+ checkNeonUnary(t, arrow.UINT32, OpNegate, uint32Values,
func(v uint32) uint32 { return -v })
+ checkNeonUnary(t, arrow.INT64, OpAbsoluteValue,
int64Values, func(v int64) int64 {
+ if v < 0 {
+ return -v
+ }
+ return v
+ })
+ checkNeonUnary(t, arrow.INT64, OpNegate, int64Values,
func(v int64) int64 { return -v })
+ checkNeonUnary(t, arrow.UINT64, OpAbsoluteValue,
uint64Values, func(v uint64) uint64 { return v })
+ checkNeonUnary(t, arrow.UINT64, OpNegate, uint64Values,
func(v uint64) uint64 { return -v })
+ checkNeonUnary(t, arrow.FLOAT32, OpAbsoluteValue,
float32Values, func(v float32) float32 {
+ return math.Float32frombits(math.Float32bits(v)
&^ (uint32(1) << 31))
+ })
+ checkNeonUnary(t, arrow.FLOAT32, OpNegate,
float32Values, func(v float32) float32 { return -v })
+ checkNeonUnary(t, arrow.FLOAT64, OpAbsoluteValue,
float64Values, func(v float64) float64 {
+ return math.Float64frombits(math.Float64bits(v)
&^ (uint64(1) << 63))
+ })
+ checkNeonUnary(t, arrow.FLOAT64, OpNegate,
float64Values, func(v float64) float64 { return -v })
+ })
+ }
+}
+
+func TestNeonArithmeticWrapping(t *testing.T) {
+ if !cpu.ARM64.HasASIMD {
+ t.Skip("ARM64 SIMD is not available")
+ }
+
+ const (
+ minInt32 = -1 << 31
+ maxInt32 = 1<<31 - 1
+ minInt64 = -1 << 63
+ maxInt64 = 1<<63 - 1
+ )
+
+ int32Left := []int32{maxInt32, minInt32, -1, 12345, -12345}
+ int32Right := []int32{2, -1, maxInt32, -7, 2}
+ uint32Left := []uint32{^uint32(0), 0, 1, 12345, 17}
+ uint32Right := []uint32{2, ^uint32(0), ^uint32(0), 7, 2}
+ int64Left := []int64{maxInt64, minInt64, -1, 12345, -12345}
+ int64Right := []int64{2, -1, maxInt64, -7, 2}
+ uint64Left := []uint64{^uint64(0), 0, 1, 12345, 17}
+ uint64Right := []uint64{2, ^uint64(0), ^uint64(0), 7, 2}
+
+ for _, op := range []ArithmeticOp{OpAdd, OpSub} {
+ checkNeonBinary(t, arrow.INT32, op, int32Left, int32Right,
func(a, b int32) int32 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.UINT32, op, uint32Left, uint32Right,
func(a, b uint32) uint32 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.INT64, op, int64Left, int64Right,
func(a, b int64) int64 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ checkNeonBinary(t, arrow.UINT64, op, uint64Left, uint64Right,
func(a, b uint64) uint64 {
+ if op == OpAdd {
+ return a + b
+ }
+ return a - b
+ })
+ }
+ checkNeonBinary(t, arrow.INT32, OpMul, int32Left, int32Right, func(a, b
int32) int32 { return a * b })
+ checkNeonBinary(t, arrow.UINT32, OpMul, uint32Left, uint32Right,
func(a, b uint32) uint32 { return a * b })
+}
+
+func BenchmarkNeonArithmetic(b *testing.B) {
+ if !cpu.ARM64.HasASIMD {
+ b.Skip("ARM64 SIMD is not available")
+ }
+
+ const n = 1 << 20
+ benchInt64 := func(b *testing.B, shape string, neon bool) {
+ left := make([]int64, n)
+ right := make([]int64, n)
+ out := make([]int64, n)
+ for i := range left {
+ left[i] = int64(i)
+ right[i] = int64(i + 1)
+ }
+ scalar := int64(7)
+ b.SetBytes(int64(n * 8))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if neon {
+ switch shape {
+ case "array-scalar":
+ arithmeticArrScalarNeon(arrow.INT64,
OpAdd, arrow.GetBytes(left), unsafe.Pointer(&scalar), arrow.GetBytes(out), n)
+ case "scalar-array":
+ arithmeticScalarArrNeon(arrow.INT64,
OpAdd, unsafe.Pointer(&scalar), arrow.GetBytes(right), arrow.GetBytes(out), n)
+ default:
+ arithmeticNeon(arrow.INT64, OpAdd,
arrow.GetBytes(left), arrow.GetBytes(right), arrow.GetBytes(out), n)
+ }
+ } else {
+ switch shape {
+ case "array-scalar":
+ for j, value := range left {
+ out[j] = value + scalar
+ }
+ case "scalar-array":
+ for j, value := range right {
+ out[j] = scalar + value
+ }
+ default:
+ for j, value := range left {
+ out[j] = value + right[j]
+ }
+ }
+ }
+ }
+ b.StopTimer()
+ runtime.KeepAlive(out)
+ }
+
+ benchFloat64 := func(b *testing.B, op ArithmeticOp, neon bool) {
+ left := make([]float64, n)
+ right := make([]float64, n)
+ out := make([]float64, n)
+ for i := range left {
+ left[i] = float64(i) + 0.25
+ right[i] = float64(i) + 1.5
+ }
+ b.SetBytes(int64(n * 8))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if neon {
+ arithmeticNeon(arrow.FLOAT64, op,
arrow.GetBytes(left), arrow.GetBytes(right), arrow.GetBytes(out), n)
+ } else {
+ for j, value := range left {
+ switch op {
+ case OpAdd:
+ out[j] = value + right[j]
+ case OpMul:
+ out[j] = value * right[j]
+ }
+ }
+ }
+ }
+ b.StopTimer()
+ runtime.KeepAlive(out)
+ }
+
+ for _, shape := range []string{"array-array", "array-scalar",
"scalar-array"} {
+ shape := shape
+ b.Run("int64/add/"+shape+"/neon", func(b *testing.B) {
benchInt64(b, shape, true) })
+ b.Run("int64/add/"+shape+"/scalar", func(b *testing.B) {
benchInt64(b, shape, false) })
+ }
+ for _, op := range []struct {
+ name string
+ op ArithmeticOp
+ }{
+ {"add", OpAdd},
+ {"mul", OpMul},
+ } {
+ op := op
+ b.Run("float64/"+op.name+"/array-array/neon", func(b
*testing.B) { benchFloat64(b, op.op, true) })
+ b.Run("float64/"+op.name+"/array-array/scalar", func(b
*testing.B) { benchFloat64(b, op.op, false) })
+ }
+}
diff --git a/arrow/compute/internal/kernels/basic_arithmetic_noasm.go
b/arrow/compute/internal/kernels/basic_arithmetic_noasm.go
index 74d64b69..84f8780a 100644
--- a/arrow/compute/internal/kernels/basic_arithmetic_noasm.go
+++ b/arrow/compute/internal/kernels/basic_arithmetic_noasm.go
@@ -14,7 +14,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//go:build go1.18 && (noasm || !amd64)
+//go:build go1.18 && (noasm || (!amd64 && !arm64) || (appengine && arm64))
package kernels