Files
crypto/argon2/blamka_loong64.s
XiaolinZhao a083cb74b6 argon2: add loong64 LSX implementation of blamka
Benchmark results on Loongson-3C6000 @ 2.20GHz:

goos: linux
goarch: loong64
pkg: golang.org/x/crypto/argon2
cpu: Loongson-3C6000/S @ 2200.00MHz
                                             |  old.bench   |              new.bench              |
                                             |    sec/op    |   sec/op     vs base                |
Argon2i/_Time:_3_Memory:_32_MB,_Threads:_1     123.36m ± 0%   76.67m ± 0%  -37.85% (p=0.000 n=10)
Argon2i/_Time:_4_Memory:_32_MB,_Threads:_1      163.7m ± 0%   101.3m ± 0%  -38.09% (p=0.000 n=10)
Argon2i/_Time:_5_Memory:_32_MB,_Threads:_1      195.0m ± 0%   116.5m ± 8%  -40.24% (p=0.000 n=10)
Argon2i/_Time:_3_Memory:_64_MB,_Threads:_4      259.4m ± 0%   165.3m ± 0%  -36.28% (p=0.000 n=10)
Argon2i/_Time:_4_Memory:_64_MB,_Threads:_4      349.9m ± 0%   220.8m ± 0%  -36.92% (p=0.000 n=10)
Argon2i/_Time:_5_Memory:_64_MB,_Threads:_4      437.6m ± 0%   276.0m ± 0%  -36.92% (p=0.000 n=10)
Argon2d/_Time:_3,_Memory:_32_MB,_Threads:_1    116.34m ± 0%   72.07m ± 0%  -38.06% (p=0.000 n=10)
Argon2d/_Time:_4,_Memory:_32_MB,_Threads:_1    154.42m ± 0%   95.28m ± 0%  -38.30% (p=0.000 n=10)
Argon2d/_Time:_5,_Memory:_32_MB,_Threads:_1     192.4m ± 0%   115.9m ± 2%  -39.73% (p=0.000 n=10)
Argon2d/_Time:_3,_Memory:_64_MB,_Threads:_4     254.4m ± 0%   165.2m ± 0%  -35.06% (p=0.000 n=10)
Argon2d/_Time:_4,_Memory:_64_MB,_Threads:_4     345.5m ± 1%   221.0m ± 0%  -36.04% (p=0.000 n=10)
Argon2d/_Time:_5,_Memory:_64_MB,_Threads:_4     432.1m ± 0%   276.2m ± 0%  -36.09% (p=0.000 n=10)
Argon2id/_Time:_3,_Memory:_32_MB,_Threads:_1   116.49m ± 0%   72.38m ± 0%  -37.86% (p=0.000 n=10)
Argon2id/_Time:_4,_Memory:_32_MB,_Threads:_1   154.59m ± 0%   95.71m ± 3%  -38.09% (p=0.000 n=10)
Argon2id/_Time:_5,_Memory:_32_MB,_Threads:_1    192.5m ± 0%   115.0m ± 0%  -40.25% (p=0.000 n=10)
Argon2id/_Time:_3,_Memory:_64_MB,_Threads:_4    255.6m ± 0%   165.7m ± 0%  -35.18% (p=0.000 n=10)
Argon2id/_Time:_4,_Memory:_64_MB,_Threads:_4    345.4m ± 0%   221.5m ± 0%  -35.87% (p=0.000 n=10)
Argon2id/_Time:_5,_Memory:_64_MB,_Threads:_4    431.9m ± 0%   276.6m ± 0%  -35.96% (p=0.000 n=10)
geomean                                         227.7m        142.6m       -37.40%

                                             |  old.bench   |               new.bench               |
                                             |     B/op     |     B/op      vs base                 |
Argon2i/_Time:_3_Memory:_32_MB,_Threads:_1     32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_4_Memory:_32_MB,_Threads:_1     32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_5_Memory:_32_MB,_Threads:_1     32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_3_Memory:_64_MB,_Threads:_4     64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_4_Memory:_64_MB,_Threads:_4     64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_5_Memory:_64_MB,_Threads:_4     64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_3,_Memory:_32_MB,_Threads:_1    32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_4,_Memory:_32_MB,_Threads:_1    32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_5,_Memory:_32_MB,_Threads:_1    32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_3,_Memory:_64_MB,_Threads:_4    64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_4,_Memory:_64_MB,_Threads:_4    64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_5,_Memory:_64_MB,_Threads:_4    64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_3,_Memory:_32_MB,_Threads:_1   32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_4,_Memory:_32_MB,_Threads:_1   32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_5,_Memory:_32_MB,_Threads:_1   32.00Mi ± 0%   32.00Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_3,_Memory:_64_MB,_Threads:_4   64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_4,_Memory:_64_MB,_Threads:_4   64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_5,_Memory:_64_MB,_Threads:_4   64.01Mi ± 0%   64.01Mi ± 0%       ~ (p=1.000 n=10) ¹
geomean                                        45.26Mi        45.26Mi       +0.00%
¹ all samples are equal

                                             | old.bench  |              new.bench              |
                                             | allocs/op  | allocs/op   vs base                 |
Argon2i/_Time:_3_Memory:_32_MB,_Threads:_1     31.00 ± 0%   31.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_4_Memory:_32_MB,_Threads:_1     39.00 ± 0%   39.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_5_Memory:_32_MB,_Threads:_1     47.00 ± 0%   47.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_3_Memory:_64_MB,_Threads:_4     73.00 ± 0%   73.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_4_Memory:_64_MB,_Threads:_4     93.00 ± 0%   93.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2i/_Time:_5_Memory:_64_MB,_Threads:_4     113.0 ± 0%   113.0 ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_3,_Memory:_32_MB,_Threads:_1    31.00 ± 0%   31.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_4,_Memory:_32_MB,_Threads:_1    39.00 ± 0%   39.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_5,_Memory:_32_MB,_Threads:_1    47.00 ± 0%   47.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_3,_Memory:_64_MB,_Threads:_4    73.00 ± 0%   73.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_4,_Memory:_64_MB,_Threads:_4    93.00 ± 0%   93.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2d/_Time:_5,_Memory:_64_MB,_Threads:_4    113.0 ± 0%   113.0 ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_3,_Memory:_32_MB,_Threads:_1   31.00 ± 0%   31.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_4,_Memory:_32_MB,_Threads:_1   39.00 ± 0%   39.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_5,_Memory:_32_MB,_Threads:_1   47.00 ± 0%   47.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_3,_Memory:_64_MB,_Threads:_4   73.00 ± 0%   73.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_4,_Memory:_64_MB,_Threads:_4   93.00 ± 0%   93.00 ± 0%       ~ (p=1.000 n=10) ¹
Argon2id/_Time:_5,_Memory:_64_MB,_Threads:_4   113.0 ± 0%   113.0 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                        59.32        59.32       +0.00%
¹ all samples are equal

Change-Id: Ia814057ddfdefbe1d23431fa366189ffe5852b96
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/826304
Reviewed-by: Junyang Shao <shaojunyang@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: abner chenc <chenguoqi@loongson.cn>
Reviewed-by: Cherry Mui <cherryyz@google.com>
2026-09-19 18:38:21 -07:00

241 lines
6.2 KiB
ArmAsm

// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build loong64 && gc && !purego
#include "textflag.h"
// HALF_ROUND applies one BlaMka half-round (mul-add-xor-rotate) to two
// groups of 4 columns, each packed 2-wide into V registers (v0/v2/v4/v6
// hold one pair of columns in lane0/lane1, v1/v3/v5/v7 hold the other).
// t0, t1 are scratch registers, one per independent column group.
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, t0, t1) \
VMULWEVVWU v0, v2, t0; \
VADDV t0, t0, t0; \
VADDV v0, v2, v0; \
VADDV v0, t0, v0; \
VXORV v6, v0, v6; \
VROTRV $32, v6, v6; \
VMULWEVVWU v1, v3, t1; \
VADDV t1, t1, t1; \
VADDV v1, v3, v1; \
VADDV v1, t1, v1; \
VXORV v7, v1, v7; \
VROTRV $32, v7, v7; \
VMULWEVVWU v4, v6, t0; \
VADDV t0, t0, t0; \
VADDV v4, v6, v4; \
VADDV v4, t0, v4; \
VXORV v2, v4, v2; \
VROTRV $24, v2, v2; \
VMULWEVVWU v5, v7, t1; \
VADDV t1, t1, t1; \
VADDV v5, v7, v5; \
VADDV v5, t1, v5; \
VXORV v3, v5, v3; \
VROTRV $24, v3, v3; \
VMULWEVVWU v0, v2, t0; \
VADDV t0, t0, t0; \
VADDV v0, v2, v0; \
VADDV v0, t0, v0; \
VXORV v6, v0, v6; \
VROTRV $16, v6, v6; \
VMULWEVVWU v1, v3, t1; \
VADDV t1, t1, t1; \
VADDV v1, v3, v1; \
VADDV v1, t1, v1; \
VXORV v7, v1, v7; \
VROTRV $16, v7, v7; \
VMULWEVVWU v4, v6, t0; \
VADDV t0, t0, t0; \
VADDV v4, v6, v4; \
VADDV v4, t0, v4; \
VXORV v2, v4, v2; \
VROTRV $63, v2, v2; \
VMULWEVVWU v5, v7, t1; \
VADDV t1, t1, t1; \
VADDV v5, v7, v5; \
VADDV v5, t1, v5; \
VXORV v3, v5, v3; \
VROTRV $63, v3, v3
// SHUFFLE / SHUFFLE_INV: reused verbatim from blake2b_loong64.s -- amd64
// avo source confirms argon2's SHUFFLE/SHUFFLE_INV and blake2b's SHUFFLE
// are byte-for-byte identical macros (diagonalize / undiagonalize),
// operating only on V2..V7 (V0/V1 untouched), with V13 held at zero.
#define SHUFFLE \
VADDV V2, V13, V12; \
VADDV V6, V13, V16; \
VADDV V4, V13, V14; \
VADDV V5, V13, V4; \
VADDV V14, V13, V5; \
VSHUF4IV $9, V3, V2; \
VSHUF4IV $9, V12, V3; \
VSHUF4IV $3, V7, V6; \
VSHUF4IV $3, V16, V7
#define SHUFFLE_INV \
VADDV V2, V13, V12; \
VADDV V6, V13, V16; \
VADDV V4, V13, V14; \
VADDV V5, V13, V4; \
VADDV V14, V13, V5; \
VSHUF4IV $3, V3, V2; \
VSHUF4IV $3, V12, V3; \
VSHUF4IV $9, V7, V6; \
VSHUF4IV $9, V16, V7
// LOAD_MSG_0 / STORE_MSG_0: row pass. `off` is the byte offset of the
// 128-byte (16-uint64) row group; within the macro the 8 loads step by
// 16B (2 uint64 per V register). Called from blamkaLOONG64 with off
// stepping by 128B across the 8 row groups -- do not confuse the two
// strides.
#define LOAD_MSG_0(off) \
VMOVQ (off+0)(R4), V0; \
VMOVQ (off+16)(R4), V1; \
VMOVQ (off+32)(R4), V2; \
VMOVQ (off+48)(R4), V3; \
VMOVQ (off+64)(R4), V4; \
VMOVQ (off+80)(R4), V5; \
VMOVQ (off+96)(R4), V6; \
VMOVQ (off+112)(R4), V7
#define STORE_MSG_0(off) \
VMOVQ V0, (off+0)(R4); \
VMOVQ V1, (off+16)(R4); \
VMOVQ V2, (off+32)(R4); \
VMOVQ V3, (off+48)(R4); \
VMOVQ V4, (off+64)(R4); \
VMOVQ V5, (off+80)(R4); \
VMOVQ V6, (off+96)(R4); \
VMOVQ V7, (off+112)(R4)
// LOAD_MSG_1 / STORE_MSG_1: diagonal pass. `off` is the byte offset within
// a 128-byte row group; within the macro the 8 loads step by 128B (one
// element from each of the 8 row groups, mirroring blamka_generic.go's
// t[off], t[128+off], t[256+off], ...). Called from blamkaLOONG64 with
// off stepping by 16B (2 uint64) across the 8 diagonal groups -- the
// inner/outer strides are swapped relative to LOAD_MSG_0/STORE_MSG_0.
#define LOAD_MSG_1(off) \
VMOVQ (off+0)(R4), V0; \
VMOVQ (off+128)(R4), V1; \
VMOVQ (off+256)(R4), V2; \
VMOVQ (off+384)(R4), V3; \
VMOVQ (off+512)(R4), V4; \
VMOVQ (off+640)(R4), V5; \
VMOVQ (off+768)(R4), V6; \
VMOVQ (off+896)(R4), V7
#define STORE_MSG_1(off) \
VMOVQ V0, (off+0)(R4); \
VMOVQ V1, (off+128)(R4); \
VMOVQ V2, (off+256)(R4); \
VMOVQ V3, (off+384)(R4); \
VMOVQ V4, (off+512)(R4); \
VMOVQ V5, (off+640)(R4); \
VMOVQ V6, (off+768)(R4); \
VMOVQ V7, (off+896)(R4)
// BLAMKA_ROUND_0(off): one full row pass over the 16-uint64 group at
// byte offset off -- corresponds to a single call to blamkaGeneric
// with the "row" argument order (t[off]..t[off+15]).
#define BLAMKA_ROUND_0(off) \
LOAD_MSG_0(off); \
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
SHUFFLE; \
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
SHUFFLE_INV; \
STORE_MSG_0(off)
// BLAMKA_ROUND_1(off): one full diagonal pass over the 16 uint64s at
// diagonal offset off -- corresponds to a single call to blamkaGeneric
// with the "diagonal" argument order (t[off], t[off+1], t[16+off], ...).
#define BLAMKA_ROUND_1(off) \
LOAD_MSG_1(off); \
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
SHUFFLE; \
HALF_ROUND(V0, V1, V2, V3, V4, V5, V6, V7, V8, V9); \
SHUFFLE_INV; \
STORE_MSG_1(off)
// func blamkaLOONG64(b *block)
TEXT ·blamkaLOONG64(SB), NOSPLIT, $0-8
MOVV b+0(FP), R4
VXORV V13, V13, V13 // V13 = 0, used by SHUFFLE/SHUFFLE_INV
// Round 0: 8 row passes, stride 128 bytes (16 uint64s).
BLAMKA_ROUND_0(0)
BLAMKA_ROUND_0(128)
BLAMKA_ROUND_0(256)
BLAMKA_ROUND_0(384)
BLAMKA_ROUND_0(512)
BLAMKA_ROUND_0(640)
BLAMKA_ROUND_0(768)
BLAMKA_ROUND_0(896)
// Round 1: 8 diagonal passes, stride 16 bytes (2 uint64s).
BLAMKA_ROUND_1(0)
BLAMKA_ROUND_1(16)
BLAMKA_ROUND_1(32)
BLAMKA_ROUND_1(48)
BLAMKA_ROUND_1(64)
BLAMKA_ROUND_1(80)
BLAMKA_ROUND_1(96)
BLAMKA_ROUND_1(112)
RET
// func mixBlocksVX(out, a, b, c *block)
TEXT ·mixBlocksVX(SB), NOSPLIT, $0-32
MOVV out+0(FP), R4
MOVV a+8(FP), R5
MOVV b+16(FP), R6
MOVV c+24(FP), R7
MOVV $1024, R8
PCALIGN $16
mixloop:
VMOVQ (R5), V0
VMOVQ (R6), V1
VMOVQ (R7), V2
VXORV V0, V1, V0
VXORV V0, V2, V0
VMOVQ V0, (R4)
ADDV $16, R4
ADDV $16, R5
ADDV $16, R6
ADDV $16, R7
SUBV $16, R8
BNE R8, R0, mixloop
RET
// func xorBlocksVX(out, a, b, c *block)
TEXT ·xorBlocksVX(SB), NOSPLIT, $0-32
MOVV out+0(FP), R4
MOVV a+8(FP), R5
MOVV b+16(FP), R6
MOVV c+24(FP), R7
MOVV $1024, R8
PCALIGN $16
xorloop:
VMOVQ (R5), V0
VMOVQ (R6), V1
VMOVQ (R7), V2
VMOVQ (R4), V3
VXORV V0, V1, V0
VXORV V0, V2, V0
VXORV V0, V3, V0
VMOVQ V0, (R4)
ADDV $16, R4
ADDV $16, R5
ADDV $16, R6
ADDV $16, R7
SUBV $16, R8
BNE R8, R0, xorloop
RET