Commit 96b16b9a61 for openssl.org
commit 96b16b9a611fdd4f75b86acb837a09eb7a17fc90
Author: Danny Tsen <dtsen@us.ibm.com>
Date: Mon Jan 12 11:37:39 2026 -0500
ppc64le: Optimized MLDSA NTT, supports p8 and above architectures.
1. rebased.
2. fixed tab.
Optimized MLDSA NTT implementation for ppc64le (p8+).
Supporting files include,
ppc64/mldsa_ntt_ppc_4x.S: supports ossl_ml_dsa_poly_ntt.
ppc64/mldsa_intt_ppc_4x.S: supports ossl_ml_dsa_poly_ntt_inverse.
ppc64/mldsa_poly_ntt_mul.S: supports ossl_poly_ntt_mult.
ppc64/mldsa_poly_add.S: supports poly_add and poly_sub.
Modified build.info to support ppc64le assembly implementation.
Added new definitions of MLDSA_NTT_ASM and MLDSA_POLY_ADD_ASM for
NTT, inverse NTT and poly_add and poly_sub for optimized assembly
implementation.
This is the initial archtecture specific implementation so can be mdified
to adapt to a new build structures.
Baseline speed test:
keygen signs verify keygens/s sign/s verify/s
ML-DSA-44 0.000141s 0.000932s 0.000171s 7115.2 1073.3 5860.9
ML-DSA-65 0.000238s 0.001471s 0.000255s 4207.2 679.6 3920.2
ML-DSA-87 0.000340s 0.001719s 0.000387s 2939.4 581.8 2584.8
Optimized:
ML-DSA-44 0.000072s 0.000253s 0.000060s 13866.8 3954.7 16735.7
ML-DSA-65 0.000138s 0.000400s 0.000095s 7253.2 2500.2 10532.4
ML-DSA-87 0.000187s 0.000485s 0.000159s 5338.5 2061.4 6269.8
The optimized code runs around 3.6 times faster than the original C implementation.
Signed-off-by: Danny Tsen <dtsen@us.ibm.com>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Merge-date: Thu Aug 27 16:26:36 2026
Merged-from: https://github.com/openssl/openssl/pull/29611
diff --git a/crypto/ml_dsa/asm/mldsa_intt_ppc64le.S b/crypto/ml_dsa/asm/mldsa_intt_ppc64le.S
new file mode 100644
index 0000000000..4b33930caf
--- /dev/null
+++ b/crypto/ml_dsa/asm/mldsa_intt_ppc64le.S
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+/*
+ * Copyright IBM Corp. 2025, 2026
+ *
+ * ===================================================================================
+ * Written by Danny Tsen <dtsen@us.ibm.com>
+ *
+ * Optimized inverse NTT implementation for ppc64le.
+ */
+
+#define Q_NEG_INV_OFFSET 16
+#define FCONST_OFFSET 32
+#define ZETA_INTT_OFFSET 48
+
+#define V_F 2
+#define V_ZETA 2
+#define V_Z0 2
+#define V_Z1 3
+#define V_Z2 4
+#define V_Z3 5
+
+#include "mldsa_ppc_macros_asm.inc"
+
+.machine "any"
+.text
+
+.macro SAVE_REGS
+ stdu 1, -352(1)
+ mflr 0
+ std 14, 56(1)
+ std 15, 64(1)
+ std 16, 72(1)
+ std 17, 80(1)
+ std 18, 88(1)
+ std 19, 96(1)
+ std 20, 104(1)
+ std 21, 112(1)
+ li 10, 128
+ li 11, 144
+ li 12, 160
+ li 14, 176
+ li 15, 192
+ li 16, 208
+ stxvx 32+20, 10, 1
+ stxvx 32+21, 11, 1
+ stxvx 32+22, 12, 1
+ stxvx 32+23, 14, 1
+ stxvx 32+24, 15, 1
+ stxvx 32+25, 16, 1
+ li 10, 224
+ li 11, 240
+ li 12, 256
+ li 14, 272
+ stxvx 32+26, 10, 1
+ stxvx 32+27, 11, 1
+ stxvx 32+28, 12, 1
+ stxvx 32+29, 14, 1
+.endm
+
+.macro RESTORE_REGS
+ li 10, 128
+ li 11, 144
+ li 12, 160
+ li 14, 176
+ li 15, 192
+ li 16, 208
+ lxvx 32+20, 10, 1
+ lxvx 32+21, 11, 1
+ lxvx 32+22, 12, 1
+ lxvx 32+23, 14, 1
+ lxvx 32+24, 15, 1
+ lxvx 32+25, 16, 1
+ li 10, 224
+ li 11, 240
+ li 12, 256
+ li 14, 272
+ lxvx 32+26, 10, 1
+ lxvx 32+27, 11, 1
+ lxvx 32+28, 12, 1
+ lxvx 32+29, 14, 1
+ ld 14, 56(1)
+ ld 15, 64(1)
+ ld 16, 72(1)
+ ld 17, 80(1)
+ ld 18, 88(1)
+ ld 19, 96(1)
+ ld 20, 104(1)
+ ld 21, 112(1)
+
+ mtlr 0
+ addi 1, 1, 352
+.endm
+
+/*
+ * Compute updated curr and peer coefficients.
+ *
+ * Input curr : V14, V15, V16, V17
+ * Input peer : V18, V19, V20, V21
+ *
+ * updated curr: V26, V27, V28, V29
+ * updated peer: V6, V7, V8, V9
+ */
+.macro _Compute_4Coeff
+ vadduwm 26, 14, 18
+ vsubuwm 10, 14, 18
+ vadduwm 6, 10, V_Q
+
+ vadduwm 27, 15, 19
+ vsubuwm 11, 15, 19
+ vadduwm 7, 11, V_Q
+
+ vadduwm 28, 16, 20
+ vsubuwm 12, 16, 20
+ vadduwm 8, 12, V_Q
+
+ vadduwm 29, 17, 21
+ vsubuwm 13, 17, 21
+ vadduwm 9, 13, V_Q
+
+ reduce_once_1x 26
+ reduce_once_1x 27
+ reduce_once_1x 28
+ reduce_once_1x 29
+.endm
+
+/*
+ * For Len=2,
+ * Transpose the final coefficients of 2-2 layout to the original
+ * coefficient array order.
+ *
+ * updated peer : V15, V17, V19, V21
+ * updated curr : V14, V16, V18, V20
+ */
+.macro _delayed_writes_xxperm
+ xxpermdi 32+14, 32+26, 32+10, 0
+ xxpermdi 32+15, 32+26, 32+10, 3
+ xxpermdi 32+16, 32+27, 32+11, 0
+ xxpermdi 32+17, 32+27, 32+11, 3
+ xxpermdi 32+18, 32+28, 32+12, 0
+ xxpermdi 32+19, 32+28, 32+12, 3
+ xxpermdi 32+20, 32+29, 32+13, 0
+ xxpermdi 32+21, 32+29, 32+13, 3
+ delayed_writes_curr_peer 14, 15, 16, 17, 18, 19, 20, 21
+.endm
+
+/*
+ * For Len=1,
+ * Transpose the final coefficients of 1-1-1-1 layout to the original
+ * coefficient array order.
+ *
+ * updated peer : V15, V17, V19, V21
+ * updated curr : V14, V16, V18, V20
+ */
+.macro _delayed_writes_vmrg
+ vmrgew 14, 10, 26
+ vmrgow 15, 10, 26
+ vmrgew 16, 11, 27
+ vmrgow 17, 11, 27
+ vmrgew 18, 12, 28
+ vmrgow 19, 12, 28
+ vmrgew 20, 13, 29
+ vmrgow 21, 13, 29
+ delayed_writes_curr_peer 14, 15, 16, 17, 18, 19, 20, 21
+.endm
+
+/*
+ * For Layer 1, Len= 1, layer with 1-1-1-1 layout.
+ */
+.macro INTT_Layer1_4x
+ lxvd2x 32+V_Z0, 0, 14
+ lxvd2x 32+V_Z1, 10, 14
+ lxvd2x 32+V_Z2, 11, 14
+ lxvd2x 32+V_Z3, 12, 14
+ addi 14, 14, 64
+ Load_vmrg_coeffs 14, 15, 16, 17, 18, 19, 20, 21
+ _Compute_4Coeff
+ mont_mul_reduce_4x V_Z0, V_Z1, V_Z2, V_Z3
+ _delayed_writes_vmrg
+ addi 5, 5, 128
+.endm
+
+/*
+ * For Layer 2, Len= 2, layer with 2-2 layout.
+ */
+.macro INTT_Layer2_4x
+ lxvd2x 32+V_Z0, 0, 14
+ lxvd2x 32+V_Z1, 10, 14
+ lxvd2x 32+V_Z2, 11, 14
+ lxvd2x 32+V_Z3, 12, 14
+ addi 14, 14, 64
+ Load_xxperm_coeffs 14, 15, 16, 17, 18, 19, 20, 21
+ _Compute_4Coeff
+ mont_mul_reduce_4x V_Z0, V_Z1, V_Z2, V_Z3
+ _delayed_writes_xxperm
+ addi 5, 5, 128
+.endm
+
+.macro _load_curr_peer_coeffs
+ lxvd2x 32+18, GPR_OFFSET_P0, 5
+ lxvd2x 32+19, GPR_OFFSET_P1, 5
+ lxvd2x 32+20, GPR_OFFSET_P2, 5
+ lxvd2x 32+21, GPR_OFFSET_P3, 5
+
+ lxvd2x 32+14, GPR_OFFSET_C0, 5
+ lxvd2x 32+15, GPR_OFFSET_C1, 5
+ lxvd2x 32+16, GPR_OFFSET_C2, 5
+ lxvd2x 32+17, GPR_OFFSET_C3, 5
+.endm
+
+/*
+ * For Layer 3, 4, 5, Len=4, 8, 16
+ */
+.macro INTT_Layer345_4x _vz0 _vz1 _vz2 _vz3
+ _load_curr_peer_coeffs
+ _Compute_4Coeff
+ mont_mul_reduce_4x \_vz0, \_vz1, \_vz2, \_vz3
+ delayed_writes_curr_peer 26, 10, 27, 11, 28, 12, 29, 13
+ addi 5, 5, 128
+.endm
+
+/*
+ * For Layer 6, 7, 8, Len=32, 64, 128
+ */
+.macro INTT_Layer678 _vz0 _vz1 _vz2 _vz3
+ _load_curr_peer_coeffs
+ _Compute_4Coeff
+ mont_mul_reduce_4x \_vz0, \_vz1, \_vz2, \_vz3
+ delayed_writes_curr_peer 26, 10, 27, 11, 28, 12, 29, 13
+ addi 5, 5, 64
+.endm
+
+/* ===================================================================== */
+/*
+ * Supporting macros for Montgomery reduce loops with
+ * constant f=41978 (mont^2/256).
+ */
+.macro _Reload_4coeffs
+ lxvd2x 32+6, 0, 6
+ lxvd2x 32+7, 10, 6
+ lxvd2x 32+8, 11, 6
+ lxvd2x 32+9, 12, 6
+.endm
+
+.macro _update_curr_coeffs_4x
+ stxvd2x 32+10, 0, 6
+ stxvd2x 32+11, 10, 6
+ stxvd2x 32+12, 11, 6
+ stxvd2x 32+13, 12, 6
+ addi 6, 6, 64
+.endm
+
+.macro POLY_Mont_Reduce_4x
+ _Reload_4coeffs
+ mont_mul_reduce_4x V_F, V_F, V_F, V_F
+ _update_curr_coeffs_4x
+
+ _Reload_4coeffs
+ mont_mul_reduce_4x V_F, V_F, V_F, V_F
+ _update_curr_coeffs_4x
+
+ _Reload_4coeffs
+ mont_mul_reduce_4x V_F, V_F, V_F, V_F
+ _update_curr_coeffs_4x
+
+ _Reload_4coeffs
+ mont_mul_reduce_4x V_F, V_F, V_F, V_F
+ _update_curr_coeffs_4x
+.endm
+/* ===================================================================== */
+
+/*
+ * mldsa_poly_ntt_inverse_ppc(int32_t *r)
+ *
+ * Compute Inverse NTT based on the following 8 layers -
+ * len = 1, 2, 4, 8, 16, 32, 64, 128.
+ *
+ * Each layer compute the coefficients on 2 legs, start and start + len*2 offsets.
+ *
+ * leg 1 leg 2
+ * ----- -----
+ * start start+len*2
+ * start+next start+len*2+next
+ * start+next+next start+len*2+next+next
+ * start+next+next+next start+len*2+next+next+next
+ *
+ * Each computation loads 8 vectors, 4 for each leg.
+ * The final coefficient (t) from each vector of leg1 and leg2 then do the
+ * add/sub operations to obtain the final results.
+ *
+ * -> leg1 = leg1 + t, leg2 = leg1 - t
+ *
+ * The resulting coefficients then store back to each leg's offset.
+ *
+ * Each vector has the same corresponding zeta except len=2.
+ *
+ * len=2 has 2-2 layout which means every 2 32-bit coefficients has the same zeta.
+ * e.g.
+ * coeff vector a1 a2 a3 a4
+ * zeta vector z1 z1 z2 z2
+ *
+ * For len=2, each vector will get permuted to leg1 and leg2. Zeta is
+ * pre-arranged for the leg1 and leg2. After the computation, each vector needs
+ * to transpose back to its original 2-2 layout.
+ *
+ * For len=1, each vector will get permuted to leg1 and leg2. Zeta is
+ * pre-arranged for the leg1 and leg2. After the computation, each vector needs
+ * to transpose back to its original 1-1-1-1 layout.
+ *
+ * Registers used for offsets to coefficients, curr and peer. Each layer may
+ * reset these registers.
+ *
+ * R9: offset to curr
+ * R11: offset to R9 + next curr offset
+ * R15: offset to R11 + next curr offset
+ * R17: offset to R15 + next curr offset
+ *
+ * R10: offset to peer
+ * R12: offset to R10 + next peer offset
+ * R16: offset to R12 + next peer offset
+ * R18: offset to R16 + next peer offset
+ */
+.global mldsa_poly_ntt_inverse_ppc
+.align 4
+mldsa_poly_ntt_inverse_ppc:
+
+ SAVE_REGS
+
+ /* load Q and Q_NEG_INV */
+ addis 8,2,.mldsa_consts@toc@ha
+ addi 8,8,.mldsa_consts@toc@l
+ lvx V_Q, 0, 8
+ li 10, Q_NEG_INV_OFFSET
+ lvx Q_NEG_INV, 10, 8
+
+ /* set zetas array */
+ addi 14, 8, ZETA_INTT_OFFSET
+
+.align 4
+ /*
+ * Layer 1. len = 1
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_C0, 0
+ li GPR_OFFSET_C1, 0x20
+ li GPR_OFFSET_C2, 0x40
+ li GPR_OFFSET_C3, 0x60
+
+ li GPR_OFFSET_P0, 0x10
+ li GPR_OFFSET_P1, 0x30
+ li GPR_OFFSET_P2, 0x50
+ li GPR_OFFSET_P3, 0x70
+
+ INTT_Layer1_4x
+ INTT_Layer1_4x
+ INTT_Layer1_4x
+ INTT_Layer1_4x
+ INTT_Layer1_4x
+ INTT_Layer1_4x
+ INTT_Layer1_4x
+ INTT_Layer1_4x
+
+.align 4
+ /*
+ * Layer 2. len = 2
+ */
+ mr 5, 3
+
+ INTT_Layer2_4x
+ INTT_Layer2_4x
+ INTT_Layer2_4x
+ INTT_Layer2_4x
+ INTT_Layer2_4x
+ INTT_Layer2_4x
+ INTT_Layer2_4x
+ INTT_Layer2_4x
+
+.align 4
+ /*
+ * Layer 3. len = 4
+ */
+ mr 5, 3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+.align 4
+ /*
+ * Layer 4. len = 8
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_C1, 0x10
+ li GPR_OFFSET_C2, 0x40
+ li GPR_OFFSET_C3, 0x50
+
+ li GPR_OFFSET_P0, 0x20
+ li GPR_OFFSET_P1, 0x30
+ li GPR_OFFSET_P2, 0x60
+ li GPR_OFFSET_P3, 0x70
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z0, V_Z1, V_Z1
+ INTT_Layer345_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z0, V_Z1, V_Z1
+ INTT_Layer345_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z0, V_Z1, V_Z1
+ INTT_Layer345_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z0, V_Z1, V_Z1
+ INTT_Layer345_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+.align 4
+ /*
+ * Layer 5. len = 16
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_C1, 0x10
+ li GPR_OFFSET_C2, 0x20
+ li GPR_OFFSET_C3, 0x30
+
+ li GPR_OFFSET_P0, 0x40
+ li GPR_OFFSET_P1, 0x50
+ li GPR_OFFSET_P2, 0x60
+ li GPR_OFFSET_P3, 0x70
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z0, V_Z0, V_Z0
+ INTT_Layer345_4x V_Z1, V_Z1, V_Z1, V_Z1
+ INTT_Layer345_4x V_Z2, V_Z2, V_Z2, V_Z2
+ INTT_Layer345_4x V_Z3, V_Z3, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer345_4x V_Z0, V_Z0, V_Z0, V_Z0
+ INTT_Layer345_4x V_Z1, V_Z1, V_Z1, V_Z1
+ INTT_Layer345_4x V_Z2, V_Z2, V_Z2, V_Z2
+ INTT_Layer345_4x V_Z3, V_Z3, V_Z3, V_Z3
+
+.align 4
+ /*
+ * Layer 6. len = 32
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_P0, 0x80
+ li GPR_OFFSET_P1, 0x90
+ li GPR_OFFSET_P2, 0xa0
+ li GPR_OFFSET_P3, 0xb0
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ INTT_Layer678 V_Z0, V_Z0, V_Z0, V_Z0
+ INTT_Layer678 V_Z0, V_Z0, V_Z0, V_Z0
+ addi 5, 5, 0x80
+
+ INTT_Layer678 V_Z1, V_Z1, V_Z1, V_Z1
+ INTT_Layer678 V_Z1, V_Z1, V_Z1, V_Z1
+ addi 5, 5, 0x80
+
+ INTT_Layer678 V_Z2, V_Z2, V_Z2, V_Z2
+ INTT_Layer678 V_Z2, V_Z2, V_Z2, V_Z2
+ addi 5, 5, 0x80
+
+ INTT_Layer678 V_Z3, V_Z3, V_Z3, V_Z3
+ INTT_Layer678 V_Z3, V_Z3, V_Z3, V_Z3
+
+.align 4
+ /*
+ * Layer 7. len = 64
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_P0, 0x100
+ li GPR_OFFSET_P1, 0x110
+ li GPR_OFFSET_P2, 0x120
+ li GPR_OFFSET_P3, 0x130
+
+ lvx V_ZETA, 0, 14
+ addi 14, 14, 16
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ addi 5, 5, 0x100
+
+ lvx V_ZETA, 0, 14
+ addi 14, 14, 16
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+
+ /*
+ * Layer 8. len = 128
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_P0, 0x200
+ li GPR_OFFSET_P1, 0x210
+ li GPR_OFFSET_P2, 0x220
+ li GPR_OFFSET_P3, 0x230
+
+ lvx V_ZETA, 0, 14
+ addi 14, 14, 16
+
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ INTT_Layer678 V_ZETA, V_ZETA, V_ZETA, V_ZETA
+
+ /*
+ * Montgomery reduce loops with
+ * constant inverse_degree_montgomery f=41978 (mont^2/256)
+ *
+ * updated coeff = montgomery_reduce((int64_t)f * coeff)
+ */
+ addi 10, 8, FCONST_OFFSET
+ lvx V_F, 0, 10
+
+ li 10, 16
+ li 11, 32
+ li 12, 48
+
+ mr 6, 3
+
+ POLY_Mont_Reduce_4x
+ POLY_Mont_Reduce_4x
+ POLY_Mont_Reduce_4x
+ POLY_Mont_Reduce_4x
+
+ RESTORE_REGS
+ blr
+.size mldsa_poly_ntt_inverse_ppc,.-mldsa_poly_ntt_inverse_ppc
+
+.rodata
+.align 4
+.mldsa_consts:
+.long ML_DSA_Q, ML_DSA_Q, ML_DSA_Q, ML_DSA_Q
+.long ML_DSA_Q_NEG_INV, ML_DSA_Q_NEG_INV, ML_DSA_Q_NEG_INV, ML_DSA_Q_NEG_INV
+/* Constant for INTT, f=mont^2/256 */
+.long 41978, 41978, 41978, 41978
+
+.mldsa_izetas:
+/* Zetas for Lane=1: setup as (3, 2, 1, 4) order */
+.long 6979993, 6403635, 4442679, 846154, 4460757, 1362209, 554416, 48306
+.long 976891, 3545687, 8196974, 6767575, 2235985, 2286327, 2939036, 420899
+.long 1104333, 3833893, 1667432, 260646, 6656817, 6470041, 426683, 1803090
+.long 975884, 7908339, 6167306, 6662682, 4856520, 8110657, 3038916, 4513516
+.long 6727783, 1799107, 7570268, 3694233, 8217573, 5366416, 3183426, 6764025
+.long 5011305, 1207385, 6423145, 8194886, 5948022, 164721, 2013608, 5925962
+.long 3724270, 3776993, 2584293, 7786281, 2831860, 1846953, 542412, 1671176
+.long 7603226, 4974386, 6880252, 6144537, 6463336, 1374803, 1279661, 2546312
+.long 7067962, 1962642, 451100, 5074302, 7143142, 1430225, 1333058, 3318210
+.long 6511298, 1050970, 2994039, 6476982, 7129923, 3548272, 3767016, 5744496
+.long 7132797, 6784443, 4325093, 5894064, 5688936, 7115408, 5538076, 2590150
+.long 3342277, 8177373, 4943130, 6644538, 8093429, 4272102, 8038120, 2437823
+.long 525098, 3595838, 3556995, 768622, 3122442, 5173371, 655327, 6348669
+.long 1613174, 522500, 7884926, 43260, 6521319, 7561383, 7479715, 7470875
+.long 3759364, 3193378, 3520352, 1197226, 5945978, 4867236, 8113420, 1235728
+.long 6136326, 3562462, 3342478, 2446433, 4972711, 4562441, 6288750, 6063917
+/* For Len=2 */
+.long 4540456, 4540456, 3628969, 3628969, 3881060, 3881060, 3019102, 3019102
+.long 1439742, 1439742, 812732, 812732, 1584928, 1584928, 7094748, 7094748
+.long 7039087, 7039087, 7064828, 7064828, 177440, 177440, 2409325, 2409325
+.long 1851402, 1851402, 5220671, 5220671, 3553272, 3553272, 8190869, 8190869
+.long 1316856, 1316856, 7620448, 7620448, 210977, 210977, 5991061, 5991061
+.long 3249728, 3249728, 6727353, 6727353, 8578, 8578, 3724342, 3724342
+.long 4421799, 4421799, 7475901, 7475901, 1100098, 1100098, 8336129, 8336129
+.long 5282425, 5282425, 7871466, 7871466, 8115473, 8115473, 3343383, 3343383
+.long 1430430, 1430430, 6527646, 6527646, 7031341, 7031341, 381987, 381987
+.long 1308169, 1308169, 22981, 22981, 1228525, 1228525, 671102, 671102
+.long 2477047, 2477047, 411027, 411027, 3693493, 3693493, 2967645, 2967645
+.long 5665122, 5665122, 6232521, 6232521, 983419, 983419, 4968207, 4968207
+.long 8253495, 8253495, 3632928, 3632928, 3157330, 3157330, 3190144, 3190144
+.long 1000202, 1000202, 4083598, 4083598, 6441103, 6441103, 1257611, 1257611
+.long 1585221, 1585221, 6203962, 6203962, 4904467, 4904467, 1452451, 1452451
+.long 3041255, 3041255, 3677745, 3677745, 1528703, 1528703, 3930395, 3930395
+/* For Lane=4 */
+.long 2797779, 2797779, 2797779, 2797779, 6308525, 6308525, 6308525, 6308525
+.long 2556880, 2556880, 2556880, 2556880, 4479693, 4479693, 4479693, 4479693
+.long 4499374, 4499374, 4499374, 4499374, 7426187, 7426187, 7426187, 7426187
+.long 7849063, 7849063, 7849063, 7849063, 7568473, 7568473, 7568473, 7568473
+.long 4680821, 4680821, 4680821, 4680821, 1600420, 1600420, 1600420, 1600420
+.long 2140649, 2140649, 2140649, 2140649, 4873154, 4873154, 4873154, 4873154
+.long 3821735, 3821735, 3821735, 3821735, 4874723, 4874723, 4874723, 4874723
+.long 1643818, 1643818, 1643818, 1643818, 1699267, 1699267, 1699267, 1699267
+.long 539299, 539299, 539299, 539299, 6031717, 6031717, 6031717, 6031717
+.long 300467, 300467, 300467, 300467, 4840449, 4840449, 4840449, 4840449
+.long 2867647, 2867647, 2867647, 2867647, 4805995, 4805995, 4805995, 4805995
+.long 3043716, 3043716, 3043716, 3043716, 3861115, 3861115, 3861115, 3861115
+.long 4464978, 4464978, 4464978, 4464978, 2537516, 2537516, 2537516, 2537516
+.long 3592148, 3592148, 3592148, 3592148, 1661693, 1661693, 1661693, 1661693
+.long 4849980, 4849980, 4849980, 4849980, 5303092, 5303092, 5303092, 5303092
+.long 8284641, 8284641, 8284641, 8284641, 5674394, 5674394, 5674394, 5674394
+/* zetas for other len */
+.long 8100412, 8100412, 8100412, 8100412, 4369920, 4369920, 4369920, 4369920
+.long 19422, 19422, 19422, 19422, 6623180, 6623180, 6623180, 6623180
+.long 3277672, 3277672, 3277672, 3277672, 1399561, 1399561, 1399561, 1399561
+.long 3859737, 3859737, 3859737, 3859737, 2118186, 2118186, 2118186, 2118186
+.long 2108549, 2108549, 2108549, 2108549, 5760665, 5760665, 5760665, 5760665
+.long 1119584, 1119584, 1119584, 1119584, 549488, 549488, 549488, 549488
+.long 4794489, 4794489, 4794489, 4794489, 1079900, 1079900, 1079900, 1079900
+.long 7356305, 7356305, 7356305, 7356305, 5654953, 5654953, 5654953, 5654953
+.long 5700314, 5700314, 5700314, 5700314, 5268920, 5268920, 5268920, 5268920
+.long 2884855, 2884855, 2884855, 2884855, 5260684, 5260684, 5260684, 5260684
+.long 2091905, 2091905, 2091905, 2091905, 359251, 359251, 359251, 359251
+.long 6026966, 6026966, 6026966, 6026966, 6554070, 6554070, 6554070, 6554070
+.long 7913949, 7913949, 7913949, 7913949, 876248, 876248, 876248, 876248
+.long 777960, 777960, 777960, 777960, 8143293, 8143293, 8143293, 8143293
+.long 518909, 518909, 518909, 518909, 2608894, 2608894, 2608894, 2608894
+.long 8354570, 8354570, 8354570, 8354570
diff --git a/crypto/ml_dsa/asm/mldsa_ntt_ppc64le.S b/crypto/ml_dsa/asm/mldsa_ntt_ppc64le.S
new file mode 100644
index 0000000000..c57a852df4
--- /dev/null
+++ b/crypto/ml_dsa/asm/mldsa_ntt_ppc64le.S
@@ -0,0 +1,700 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+/*
+ * Copyright IBM Corp. 2025, 2026
+ *
+ * ===================================================================================
+ * Written by Danny Tsen <dtsen@us.ibm.com>
+ *
+ * Optimized NTT implementation for ppc64le.
+ */
+
+#define Q_NEG_INV_OFFSET 16
+#define ZETA_NTT_OFFSET 32
+
+#define V_ZETA 2
+#define V_Z0 2
+#define V_Z1 3
+#define V_Z2 4
+#define V_Z3 5
+
+#include "mldsa_ppc_macros_asm.inc"
+
+.machine "any"
+.text
+
+.macro SAVE_REGS
+ stdu 1, -352(1)
+ mflr 0
+ std 14, 56(1)
+ std 15, 64(1)
+ std 16, 72(1)
+ std 17, 80(1)
+ std 18, 88(1)
+ std 19, 96(1)
+ std 20, 104(1)
+ std 21, 112(1)
+ li 10, 128
+ li 11, 144
+ li 12, 160
+ li 14, 176
+ li 15, 192
+ li 16, 208
+ stxvx 32+20, 10, 1
+ stxvx 32+21, 11, 1
+ stxvx 32+22, 12, 1
+ stxvx 32+23, 14, 1
+ stxvx 32+24, 15, 1
+ stxvx 32+25, 16, 1
+ li 10, 224
+ li 11, 240
+ li 12, 256
+ li 14, 272
+ stxvx 32+26, 10, 1
+ stxvx 32+27, 11, 1
+ stxvx 32+28, 12, 1
+ stxvx 32+29, 14, 1
+.endm
+
+.macro RESTORE_REGS
+ li 10, 128
+ li 11, 144
+ li 12, 160
+ li 14, 176
+ li 15, 192
+ li 16, 208
+ lxvx 32+20, 10, 1
+ lxvx 32+21, 11, 1
+ lxvx 32+22, 12, 1
+ lxvx 32+23, 14, 1
+ lxvx 32+24, 15, 1
+ lxvx 32+25, 16, 1
+ li 10, 224
+ li 11, 240
+ li 12, 256
+ li 14, 272
+ lxvx 32+26, 10, 1
+ lxvx 32+27, 11, 1
+ lxvx 32+28, 12, 1
+ lxvx 32+29, 14, 1
+ ld 14, 56(1)
+ ld 15, 64(1)
+ ld 16, 72(1)
+ ld 17, 80(1)
+ ld 18, 88(1)
+ ld 19, 96(1)
+ ld 20, 104(1)
+ ld 21, 112(1)
+
+ mtlr 0
+ addi 1, 1, 352
+.endm
+
+/* ===================================================================== */
+/*
+ * Compute updated curr and peer coefficients.
+ * updated curr = reduce_once(curr + peer);
+ * updated peer = mod_sub(curr, peer);
+ *
+ * Input curr : V26, V27, V28, V29
+ * Input peer : V10, V11, V12, V13
+ *
+ * updated peer : V18, V19, V20, V21
+ * updated curr : V22, V23, V24, V25
+ */
+.macro _update_curr_peer_coeffs
+ vadduwm 22, 26, 10
+ mod_sub 18, 26, 10
+
+ vadduwm 23, 27, 11
+ mod_sub 19, 27, 11
+
+ vadduwm 24, 28, 12
+ mod_sub 20, 28, 12
+
+ vadduwm 25, 29, 13
+ mod_sub 21, 29, 13
+
+ reduce_once_1x 22
+ reduce_once_1x 23
+ reduce_once_1x 24
+ reduce_once_1x 25
+.endm
+
+/*
+ * Transpose the final coefficients of 2-2 layout to the original
+ * coefficient array order.
+ *
+ * updated peer : V14, V15, V16, V17
+ * updated curr : V10, V11, V12, V13
+ */
+.macro _delayed_writes_xxperm
+ xxpermdi 32+10, 32+22, 32+18, 0
+ xxpermdi 32+14, 32+22, 32+18, 3
+ xxpermdi 32+11, 32+23, 32+19, 0
+ xxpermdi 32+15, 32+23, 32+19, 3
+ xxpermdi 32+12, 32+24, 32+20, 0
+ xxpermdi 32+16, 32+24, 32+20, 3
+ xxpermdi 32+13, 32+25, 32+21, 0
+ xxpermdi 32+17, 32+25, 32+21, 3
+ delayed_writes_curr_peer 10, 14, 11, 15, 12, 16, 13, 17
+.endm
+
+/*
+ * Transpose the final coefficients of 1-1-1-1 layout to the original
+ * coefficient array order.
+ *
+ * updated peer : V14, V15, V16, V17
+ * updated curr : V10, V11, V12, V13
+ */
+.macro _delayed_writes_vmrg
+ vmrgew 10, 18, 22
+ vmrgow 11, 18, 22
+ vmrgew 12, 19, 23
+ vmrgow 13, 19, 23
+ vmrgew 14, 20, 24
+ vmrgow 15, 20, 24
+ vmrgew 16, 21, 25
+ vmrgow 17, 21, 25
+ delayed_writes_curr_peer 10, 11, 12, 13, 14, 15, 16, 17
+.endm
+
+/*
+ * NTT computation for layers, 1, 2, 3, 4, 5, 6.
+ */
+.macro _ntt_body _vz0, _vz1, _vz2, _vz3
+ lxvd2x 32+6, 5, GPR_OFFSET_P0
+ lxvd2x 32+7, 5, GPR_OFFSET_P1
+ lxvd2x 32+8, 5, GPR_OFFSET_P2
+ lxvd2x 32+9, 5, GPR_OFFSET_P3
+ mont_mul_reduce_4x \_vz0, \_vz1, \_vz2, \_vz3
+ lxvd2x 32+26, 5, GPR_OFFSET_C0
+ lxvd2x 32+27, 5, GPR_OFFSET_C1
+ lxvd2x 32+28, 5, GPR_OFFSET_C2
+ lxvd2x 32+29, 5, GPR_OFFSET_C3
+
+ /* update curr and peer coefficients */
+ _update_curr_peer_coeffs
+.endm
+
+/*
+ * For Layer 1, 2, 3, Len=32, 64, 128
+ */
+.macro NTT_Layer123_4x _vz0, _vz1, _vz2, _vz3
+ _ntt_body \_vz0, \_vz1, \_vz2, \_vz3
+ delayed_writes_curr_peer 22, 18, 23, 19, 24, 20, 25, 21
+ addi 5, 5, 64
+.endm
+
+/*
+ * For Layer 4, 5, 6, Len=4, 8, 16
+ */
+.macro NTT_Layer456_4x _vz0, _vz1, _vz2, _vz3
+ _ntt_body \_vz0, \_vz1, \_vz2, \_vz3
+ delayed_writes_curr_peer 22, 18, 23, 19, 24, 20, 25, 21
+ addi 5, 5, 128
+.endm
+
+/*
+ * For Layer 7, Len= 2, layer with 2-2 layout.
+ */
+.macro NTT_Layer7_4x
+ lxvd2x 32+V_Z0, 0, 14
+ lxvd2x 32+V_Z1, 10, 14
+ lxvd2x 32+V_Z2, 11, 14
+ lxvd2x 32+V_Z3, 12, 14
+ addi 14, 14, 64
+ Load_xxperm_coeffs 26, 27, 28, 29, 6, 7, 8, 9
+ mont_mul_reduce_4x V_Z0, V_Z1, V_Z2, V_Z3
+ _update_curr_peer_coeffs
+ _delayed_writes_xxperm
+ addi 5, 5, 128
+.endm
+
+/*
+ * For Layer 8, Len= 1, layer with 1-1-1-1 layout.
+ */
+.macro NTT_Layer8_4x
+ lxvd2x 32+V_Z0, 0, 14
+ lxvd2x 32+V_Z1, 10, 14
+ lxvd2x 32+V_Z2, 11, 14
+ lxvd2x 32+V_Z3, 12, 14
+ addi 14, 14, 64
+ Load_vmrg_coeffs 26, 27, 28, 29, 6, 7, 8, 9
+ mont_mul_reduce_4x V_Z0, V_Z1, V_Z2, V_Z3
+ _update_curr_peer_coeffs
+ _delayed_writes_vmrg
+ addi 5, 5, 128
+.endm
+
+/* ===================================================================== */
+/*
+ * mldsa_poly_ntt_ppc(int32_t *r)
+ * Compute forward NTT based on the following 8 layers -
+ * len = 128, 64, 32, 16, 8, 4, 2, 1.
+ *
+ * Each layer compute the coefficients on 2 legs, start and start + len*2 offsets.
+ *
+ * leg 1 leg 2
+ * ----- -----
+ * start start+len*2
+ * start+next start+len*2+next
+ * start+next+next start+len*2+next+next
+ * start+next+next+next start+len*2+next+next+next
+ *
+ * Each computation loads 8 vectors, 4 for each leg.
+ * The final coefficient (t) from each vector of leg1 and leg2 then do the
+ * add/sub operations to obtain the final results.
+ *
+ * -> leg1 = leg1 + t, leg2 = leg1 - t
+ *
+ * The resulting coefficients then store back to each leg's offset.
+ *
+ * Each vector has the same corresponding zeta except len=2.
+ *
+ * len=2 has 2-2 layout which means every 2 32-bit coefficients has the same zeta.
+ * e.g.
+ * coeff vector a1 a2 a3 a4
+ * zeta vector z1 z1 z2 z2
+ *
+ * For len=2, each vector will get permuted to leg1 and leg2. Zeta is
+ * pre-arranged for the leg1 and leg2. After the computation, each vector needs
+ * to transpose back to its original 2-2 layout.
+ *
+ * For len=1, each vector will get permuted to leg1 and leg2. Zeta is
+ * pre-arranged for the leg1 and leg2. After the computation, each vector needs
+ * to transpose back to its original 1-1-1-1 layout.
+ *
+ * Registers used for offsets to coefficients, curr and peer. Each layer may
+ * reset these registers.
+ *
+ * R9: offset to curr
+ * R11: offset to R9 + next curr offset
+ * R15: offset to R11 + next curr offset
+ * R17: offset to R15 + next curr offset
+ *
+ * R10: offset to peer
+ * R12: offset to R10 + next peer offset
+ * R16: offset to R12 + next peer offset
+ * R18: offset to R16 + next peer offset
+ */
+.global mldsa_poly_ntt_ppc
+.align 4
+mldsa_poly_ntt_ppc:
+
+ SAVE_REGS
+
+ /* load Q and Q_NEG_INV */
+ addis 8,2,.mldsa_consts@toc@ha
+ addi 8,8,.mldsa_consts@toc@l
+ lvx V_Q, 0, 8
+ li 10, Q_NEG_INV_OFFSET
+ lvx Q_NEG_INV, 10, 8
+
+ /* set zetas array */
+ addi 14, 8, ZETA_NTT_OFFSET
+
+ /*
+ * 1. Layer 1, len = 128
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_C0, 0
+ li GPR_OFFSET_C1, 0x10
+ li GPR_OFFSET_C2, 0x20
+ li GPR_OFFSET_C3, 0x30
+
+ li GPR_OFFSET_P0, 0x200
+ li GPR_OFFSET_P1, 0x210
+ li GPR_OFFSET_P2, 0x220
+ li GPR_OFFSET_P3, 0x230
+
+ lvx V_ZETA, 0, 14
+ addi 14, 14, 16
+
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+
+.align 4
+ /*
+ * Layer 2. len = 64
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_P0, 0x100
+ li GPR_OFFSET_P1, 0x110
+ li GPR_OFFSET_P2, 0x120
+ li GPR_OFFSET_P3, 0x130
+
+ lvx V_ZETA, 0, 14
+ addi 14, 14, 16
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ addi 5, 5, 0x100
+
+ lvx V_ZETA, 0, 14
+ addi 14, 14, 16
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+ NTT_Layer123_4x V_ZETA, V_ZETA, V_ZETA, V_ZETA
+
+.align 4
+ /*
+ * Layer 3. len = 32
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_P0, 0x80
+ li GPR_OFFSET_P1, 0x90
+ li GPR_OFFSET_P2, 0xa0
+ li GPR_OFFSET_P3, 0xb0
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer123_4x V_Z0, V_Z0, V_Z0, V_Z0
+ NTT_Layer123_4x V_Z0, V_Z0, V_Z0, V_Z0
+ addi 5, 5, 0x80
+
+ NTT_Layer123_4x V_Z1, V_Z1, V_Z1, V_Z1
+ NTT_Layer123_4x V_Z1, V_Z1, V_Z1, V_Z1
+ addi 5, 5, 0x80
+
+ NTT_Layer123_4x V_Z2, V_Z2, V_Z2, V_Z2
+ NTT_Layer123_4x V_Z2, V_Z2, V_Z2, V_Z2
+ addi 5, 5, 0x80
+
+ NTT_Layer123_4x V_Z3, V_Z3, V_Z3, V_Z3
+ NTT_Layer123_4x V_Z3, V_Z3, V_Z3, V_Z3
+
+.align 4
+ /*
+ * Layer 4. len = 16
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_P0, 0x40
+ li GPR_OFFSET_P1, 0x50
+ li GPR_OFFSET_P2, 0x60
+ li GPR_OFFSET_P3, 0x70
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z0, V_Z0, V_Z0
+ NTT_Layer456_4x V_Z1, V_Z1, V_Z1, V_Z1
+ NTT_Layer456_4x V_Z2, V_Z2, V_Z2, V_Z2
+ NTT_Layer456_4x V_Z3, V_Z3, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z0, V_Z0, V_Z0
+ NTT_Layer456_4x V_Z1, V_Z1, V_Z1, V_Z1
+ NTT_Layer456_4x V_Z2, V_Z2, V_Z2, V_Z2
+ NTT_Layer456_4x V_Z3, V_Z3, V_Z3, V_Z3
+
+.align 4
+ /*
+ * Layer 5. len = 8
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_C1, 0x10
+ li GPR_OFFSET_C2, 0x40
+ li GPR_OFFSET_C3, 0x50
+
+ li GPR_OFFSET_P0, 0x20
+ li GPR_OFFSET_P1, 0x30
+ li GPR_OFFSET_P2, 0x60
+ li GPR_OFFSET_P3, 0x70
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z0, V_Z1, V_Z1
+ NTT_Layer456_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z0, V_Z1, V_Z1
+ NTT_Layer456_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z0, V_Z1, V_Z1
+ NTT_Layer456_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z0, V_Z1, V_Z1
+ NTT_Layer456_4x V_Z2, V_Z2, V_Z3, V_Z3
+
+.align 4
+ /*
+ * Layer 6. len = 4
+ */
+ mr 5, 3
+
+ li GPR_OFFSET_C1, 0x20
+ li GPR_OFFSET_C2, 0x40
+ li GPR_OFFSET_C3, 0x60
+
+ li GPR_OFFSET_P0, 0x10
+ li GPR_OFFSET_P1, 0x30
+ li GPR_OFFSET_P2, 0x50
+ li GPR_OFFSET_P3, 0x70
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+ Load_4zetas V_Z0, V_Z1, V_Z2, V_Z3
+ NTT_Layer456_4x V_Z0, V_Z1, V_Z2, V_Z3
+
+.align 4
+ /*
+ * Layer 7. len = 2
+ * Layer 7 uses the same offset as Layer 6.
+ */
+ mr 5, 3
+
+ NTT_Layer7_4x
+ NTT_Layer7_4x
+ NTT_Layer7_4x
+ NTT_Layer7_4x
+ NTT_Layer7_4x
+ NTT_Layer7_4x
+ NTT_Layer7_4x
+ NTT_Layer7_4x
+
+.align 4
+ /*
+ * Layer 8. len = 1
+ * Layer 8 uses the same offset as Layer 6.
+ */
+ mr 5, 3
+
+ NTT_Layer8_4x
+ NTT_Layer8_4x
+ NTT_Layer8_4x
+ NTT_Layer8_4x
+ NTT_Layer8_4x
+ NTT_Layer8_4x
+ NTT_Layer8_4x
+ NTT_Layer8_4x
+
+ RESTORE_REGS
+ blr
+.size mldsa_poly_ntt_ppc,.-mldsa_poly_ntt_ppc
+
+/* ===================================================================== */
+.macro POLY_SAVE_REGS
+ stdu 1, -256(1)
+ mflr 0
+ li 9, 128
+ li 10, 144
+ li 11, 160
+ li 12, 176
+ stxvx 32+20, 9, 1
+ stxvx 32+21, 10, 1
+ stxvx 32+22, 11, 1
+ stxvx 32+23, 12, 1
+ li 9, 192
+ li 10, 208
+ stxvx 32+24, 9, 1
+ stxvx 32+25, 10, 1
+.endm
+
+.macro POLY_RESTORE_REGS
+ li 9, 128
+ li 10, 144
+ li 11, 160
+ li 12, 176
+ lxvx 32+20, 9, 1
+ lxvx 32+21, 10, 1
+ lxvx 32+22, 11, 1
+ lxvx 32+23, 12, 1
+ li 9, 192
+ li 10, 208
+ lxvx 32+24, 9, 1
+ lxvx 32+25, 10, 1
+
+ mtlr 0
+ addi 1, 1, 256
+.endm
+
+.macro _Load_poly_points_vectors
+ lxvd2x 32+2, 0, 4
+ lxvd2x 32+3, 10, 4
+ lxvd2x 32+4, 11, 4
+ lxvd2x 32+5, 12, 4
+
+ lxvd2x 32+6, 0, 5
+ lxvd2x 32+7, 10, 5
+ lxvd2x 32+8, 11, 5
+ lxvd2x 32+9, 12, 5
+ addi 4, 4, 64
+ addi 5, 5, 64
+.endm
+
+.macro _update_poly_coeffs
+ stxvd2x 32+10, 0, 3
+ stxvd2x 32+11, 10, 3
+ stxvd2x 32+12, 11, 3
+ stxvd2x 32+13, 12, 3
+ addi 3, 3, 64
+.endm
+
+.macro _poly_ntt_mul_4x
+ _Load_poly_points_vectors
+ //_Mont_point_mul
+ Mont_scalar_mul_4x 2, 3, 4, 5
+ Mont_reduce_4x
+ _update_poly_coeffs
+.endm
+
+/*
+ * mldsa_poly_ntt_mult(poly *c, const poly *a, const poly *b)
+ */
+.global mldsa_poly_ntt_mult_ppc
+.align 4
+mldsa_poly_ntt_mult_ppc:
+
+ POLY_SAVE_REGS
+
+ /* load Q and Q_NEG_INV */
+ addis 8,2,.mldsa_consts@toc@ha
+ addi 8,8,.mldsa_consts@toc@l
+ lvx V_Q, 0, 8
+ li 10, 16
+ lvx Q_NEG_INV, 10, 8
+
+ /*
+ * Montgomery reduce loops for a * b
+ *
+ * c[j] = montgomery_reduce(a[j] * b[j])
+ */
+ li 10, 16
+ li 11, 32
+ li 12, 48
+
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+ _poly_ntt_mul_4x
+
+ POLY_RESTORE_REGS
+ blr
+.size mldsa_poly_ntt_mult_ppc,.-mldsa_poly_ntt_mult_ppc
+/* ===================================================================== */
+
+.rodata
+.align 4
+.mldsa_consts:
+.long ML_DSA_Q, ML_DSA_Q, ML_DSA_Q, ML_DSA_Q
+.long ML_DSA_Q_NEG_INV, ML_DSA_Q_NEG_INV, ML_DSA_Q_NEG_INV, ML_DSA_Q_NEG_INV
+
+/* zetas */
+.mldsa_zetas:
+/* For Len=128, 64, 32, 16, 8 */
+.long 25847, 25847, 25847, 25847, 5771523, 5771523, 5771523, 5771523
+.long 7861508, 7861508, 7861508, 7861508, 237124, 237124, 237124, 237124
+.long 7602457, 7602457, 7602457, 7602457, 7504169, 7504169, 7504169, 7504169
+.long 466468, 466468, 466468, 466468, 1826347, 1826347, 1826347, 1826347
+.long 2353451, 2353451, 2353451, 2353451, 8021166, 8021166, 8021166, 8021166
+.long 6288512, 6288512, 6288512, 6288512, 3119733, 3119733, 3119733, 3119733
+.long 5495562, 5495562, 5495562, 5495562, 3111497, 3111497, 3111497, 3111497
+.long 2680103, 2680103, 2680103, 2680103, 2725464, 2725464, 2725464, 2725464
+.long 1024112, 1024112, 1024112, 1024112, 7300517, 7300517, 7300517, 7300517
+.long 3585928, 3585928, 3585928, 3585928, 7830929, 7830929, 7830929, 7830929
+.long 7260833, 7260833, 7260833, 7260833, 2619752, 2619752, 2619752, 2619752
+.long 6271868, 6271868, 6271868, 6271868, 6262231, 6262231, 6262231, 6262231
+.long 4520680, 4520680, 4520680, 4520680, 6980856, 6980856, 6980856, 6980856
+.long 5102745, 5102745, 5102745, 5102745, 1757237, 1757237, 1757237, 1757237
+.long 8360995, 8360995, 8360995, 8360995, 4010497, 4010497, 4010497, 4010497
+.long 280005, 280005, 280005, 280005
+/*For Len=4 */
+.long 2706023, 2706023, 2706023, 2706023, 95776, 95776, 95776, 95776
+.long 3077325, 3077325, 3077325, 3077325, 3530437, 3530437, 3530437, 3530437
+.long 6718724, 6718724, 6718724, 6718724, 4788269, 4788269, 4788269, 4788269
+.long 5842901, 5842901, 5842901, 5842901, 3915439, 3915439, 3915439, 3915439
+.long 4519302, 4519302, 4519302, 4519302, 5336701, 5336701, 5336701, 5336701
+.long 3574422, 3574422, 3574422, 3574422, 5512770, 5512770, 5512770, 5512770
+.long 3539968, 3539968, 3539968, 3539968, 8079950, 8079950, 8079950, 8079950
+.long 2348700, 2348700, 2348700, 2348700, 7841118, 7841118, 7841118, 7841118
+.long 6681150, 6681150, 6681150, 6681150, 6736599, 6736599, 6736599, 6736599
+.long 3505694, 3505694, 3505694, 3505694, 4558682, 4558682, 4558682, 4558682
+.long 3507263, 3507263, 3507263, 3507263, 6239768, 6239768, 6239768, 6239768
+.long 6779997, 6779997, 6779997, 6779997, 3699596, 3699596, 3699596, 3699596
+.long 811944, 811944, 811944, 811944, 531354, 531354, 531354, 531354
+.long 954230, 954230, 954230, 954230, 3881043, 3881043, 3881043, 3881043
+.long 3900724, 3900724, 3900724, 3900724, 5823537, 5823537, 5823537, 5823537
+.long 2071892, 2071892, 2071892, 2071892, 5582638, 5582638, 5582638, 5582638
+/* For Len=2 */
+.long 4450022, 4450022, 6851714, 6851714, 4702672, 4702672, 5339162, 5339162
+.long 6927966, 6927966, 3475950, 3475950, 2176455, 2176455, 6795196, 6795196
+.long 7122806, 7122806, 1939314, 1939314, 4296819, 4296819, 7380215, 7380215
+.long 5190273, 5190273, 5223087, 5223087, 4747489, 4747489, 126922, 126922
+.long 3412210, 3412210, 7396998, 7396998, 2147896, 2147896, 2715295, 2715295
+.long 5412772, 5412772, 4686924, 4686924, 7969390, 7969390, 5903370, 5903370
+.long 7709315, 7709315, 7151892, 7151892, 8357436, 8357436, 7072248, 7072248
+.long 7998430, 7998430, 1349076, 1349076, 1852771, 1852771, 6949987, 6949987
+.long 5037034, 5037034, 264944, 264944, 508951, 508951, 3097992, 3097992
+.long 44288, 44288, 7280319, 7280319, 904516, 904516, 3958618, 3958618
+.long 4656075, 4656075, 8371839, 8371839, 1653064, 1653064, 5130689, 5130689
+.long 2389356, 2389356, 8169440, 8169440, 759969, 759969, 7063561, 7063561
+.long 189548, 189548, 4827145, 4827145, 3159746, 3159746, 6529015, 6529015
+.long 5971092, 5971092, 8202977, 8202977, 1315589, 1315589, 1341330, 1341330
+.long 1285669, 1285669, 6795489, 6795489, 7567685, 7567685, 6940675, 6940675
+.long 5361315, 5361315, 4499357, 4499357, 4751448, 4751448, 3839961, 3839961
+/* Setup zetas for Len=1 as (3, 2, 1, 4) order */
+.long 2316500, 2091667, 3817976, 3407706, 5933984, 5037939, 4817955, 2244091
+.long 7144689, 266997, 3513181, 2434439, 7183191, 4860065, 5187039, 4621053
+.long 909542, 900702, 819034, 1859098, 8337157, 495491, 7857917, 6767243
+.long 2031748, 7725090, 3207046, 5257975, 7611795, 4823422, 4784579, 7855319
+.long 5942594, 342297, 4108315, 286988, 1735879, 3437287, 203044, 5038140
+.long 5790267, 2842341, 1265009, 2691481, 2486353, 4055324, 1595974, 1247620
+.long 2635921, 4613401, 4832145, 1250494, 1903435, 5386378, 7329447, 1869119
+.long 5062207, 7047359, 6950192, 1237275, 3306115, 7929317, 6417775, 1312455
+.long 5834105, 7100756, 7005614, 1917081, 2235880, 1500165, 3406031, 777191
+.long 6709241, 7838005, 6533464, 5548557, 594136, 5796124, 4603424, 4656147
+.long 2454455, 6366809, 8215696, 2432395, 185531, 1957272, 7173032, 3369112
+.long 1616392, 5196991, 3014001, 162844, 4686184, 810149, 6581310, 1652634
+.long 3866901, 5341501, 269760, 3523897, 1717735, 2213111, 472078, 7404533
+.long 6577327, 7953734, 1910376, 1723600, 8119771, 6712985, 4546524, 7276084
+.long 7959518, 5441381, 6094090, 6144432, 1612842, 183443, 4834730, 7403526
+.long 8332111, 7826001, 7018208, 3919660, 7534263, 3937738, 1976782, 1400424
diff --git a/crypto/ml_dsa/asm/mldsa_ppc_macros_asm.inc b/crypto/ml_dsa/asm/mldsa_ppc_macros_asm.inc
new file mode 100644
index 0000000000..e0eadff690
--- /dev/null
+++ b/crypto/ml_dsa/asm/mldsa_ppc_macros_asm.inc
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+/*
+ * Copyright IBM Corp. 2025, 2026
+ *
+ * ===================================================================================
+ * Written by Danny Tsen <dtsen@us.ibm.com>
+ */
+
+#define ML_DSA_Q 8380417 /* The modulus is 23 bits (2^23 - 2^13 + 1) */
+#define ML_DSA_Q_NEG_INV 4236238847 /* Inverse of -q modulo 2^32 */
+
+#define Q_NEG_INV 0
+#define V_Q 1
+
+#define GPR_OFFSET_C0 9
+#define GPR_OFFSET_C1 11
+#define GPR_OFFSET_C2 15
+#define GPR_OFFSET_C3 17
+
+#define GPR_OFFSET_P0 10
+#define GPR_OFFSET_P1 12
+#define GPR_OFFSET_P2 16
+#define GPR_OFFSET_P3 18
+
+/* ===================================================================== */
+/*
+ * For NTT implementation, initial peer and curr vectors setup as
+ * peer = V6, V7, V8, V9
+ * curr = V26, V27, V28, V29
+ *
+ * For Inverse NTT implementation, initial peer and curr vectors setup as
+ * peer = V18, V19, V20, V21
+ * curr = V14, V15, V16, V17
+ */
+/*
+ * Load coefficients with 1-1-1-1 layout,
+ * each load contains 2 curr and 2 peer elements.
+ * -> curr1 peer1 curr2 peer2
+ * curr3 peer3 curr4 peer4
+ * vmrgew and vmrgow ->
+ * curr vector contains 4 curr elements and
+ * peer vector contains 4 peer elements
+ *
+ * In order to do the coefficients computation, zeta vector will arrange
+ * in the proper order to match the multiplication.
+ */
+.macro Load_vmrg_coeffs _c1, _c2, _c3, _c4, _p1, _p2, _p3, _p4
+ lxvd2x 32+10, GPR_OFFSET_C0, 5
+ lxvd2x 32+11, GPR_OFFSET_P0, 5
+ vmrgew \_p1, 10, 11
+ vmrgow \_c1, 10, 11
+ lxvd2x 32+12, GPR_OFFSET_C1, 5
+ lxvd2x 32+13, GPR_OFFSET_P1, 5
+ vmrgew \_p2, 12, 13
+ vmrgow \_c2, 12, 13
+ lxvd2x 32+10, GPR_OFFSET_C2, 5
+ lxvd2x 32+11, GPR_OFFSET_P2, 5
+ vmrgew \_p3, 10, 11
+ vmrgow \_c3, 10, 11
+ lxvd2x 32+12, GPR_OFFSET_C3, 5
+ lxvd2x 32+13, GPR_OFFSET_P3, 5
+ vmrgew \_p4, 12, 13
+ vmrgow \_c4, 12, 13
+.endm
+
+/*
+ * Load coefficients with 2-2 layout,
+ * each load contains 4 curr and 4 peer elements
+ * -> curr1 curr2 peer1 peer2
+ * curr3 curr3 pee43 peer4
+ * xxpermidi -> 4 curr elements and 4 peer elements
+ * curr vector contains 4 curr elements and
+ * peer vector contains 4 peer elements
+ *
+ * In order to do the coefficients computation, zeta vector will arrange
+ * in the proper order to match the multiplication.
+ */
+.macro Load_xxperm_coeffs _c1, _c2, _c3, _c4, _p1, _p2, _p3, _p4
+ lxvd2x 1, GPR_OFFSET_C0, 5
+ lxvd2x 2, GPR_OFFSET_P0, 5
+ xxpermdi 32+\_p1, 1, 2, 3
+ xxpermdi 32+\_c1, 1, 2, 0
+ lxvd2x 3, GPR_OFFSET_C1, 5
+ lxvd2x 4, GPR_OFFSET_P1, 5
+ xxpermdi 32+\_p2, 3, 4, 3
+ xxpermdi 32+\_c2, 3, 4, 0
+ lxvd2x 1, GPR_OFFSET_C2, 5
+ lxvd2x 2, GPR_OFFSET_P2, 5
+ xxpermdi 32+\_p3, 1, 2, 3
+ xxpermdi 32+\_c3, 1, 2, 0
+ lxvd2x 3, GPR_OFFSET_C3, 5
+ lxvd2x 4, GPR_OFFSET_P3, 5
+ xxpermdi 32+\_p4, 3, 4, 3
+ xxpermdi 32+\_c4, 3, 4, 0
+.endm
+
+/*
+ * Reduces x mod q in constant time
+ * i.e. return x < q ? x : x - q;
+ * @param x Where x is assumed to be in the range 0 <= x < 2*q
+ * @returns the difference in the range 0..q-1
+ */
+.macro reduce_once_1x _vt
+ vsubuwm 16, \_vt, V_Q
+ vcmpgtsw 17, V_Q, \_vt
+ xxsel 32+\_vt, 32+16, 32+\_vt, 32+17
+.endm
+
+/*
+ * Calculate The positive value of (a-b) mod q in constant time.
+ *
+ * a - b mod q gives a value in the range -(q-1)..(q-1)
+ * By adding q we get a range of 1..(2q-1).
+ * Reducing this once then gives the range 0..q-1
+ *
+ * returns The value (q + a - b) mod q
+ */
+.macro mod_sub _x _a _b
+ vadduwm 14, \_a, V_Q
+ vsubuwm \_x, 14, \_b
+ reduce_once_1x \_x
+.endm
+
+/* Delayed writes resulting curr and peer coefficients */
+.macro delayed_writes_curr_peer _c1, _p1, _c2, _p2, _c3, _p3, _c4, _p4
+ stxvd2x 32+\_c1, GPR_OFFSET_C0, 5
+ stxvd2x 32+\_p1, GPR_OFFSET_P0, 5
+ stxvd2x 32+\_c2, GPR_OFFSET_C1, 5
+ stxvd2x 32+\_p2, GPR_OFFSET_P1, 5
+ stxvd2x 32+\_c3, GPR_OFFSET_C2, 5
+ stxvd2x 32+\_p3, GPR_OFFSET_P2, 5
+ stxvd2x 32+\_c4, GPR_OFFSET_C3, 5
+ stxvd2x 32+\_p4, GPR_OFFSET_P3, 5
+.endm
+
+.macro Load_4zetas _vz0, _vz1, _vz2, _vz3
+ lvx \_vz0, 0, 14
+ addi 14, 14, 16
+ lvx \_vz1, 0, 14
+ addi 14, 14, 16
+ lvx \_vz2, 0, 14
+ addi 14, 14, 16
+ lvx \_vz3, 0, 14
+ addi 14, 14, 16
+.endm
+
+/*
+ * Montgomery multiply:
+ * a = zeta * peer or a = b * c
+ * Multiply of 2 numbers in montgomery form, in the range 0...(2^32)*q
+ */
+.macro Mont_scalar_mul_4x _vz0 _vz1 _vz2 _vz3
+ vmuleuw 10, 6, \_vz0
+ vmulouw 11, 6, \_vz0
+ vmuleuw 12, 7, \_vz1
+ vmulouw 13, 7, \_vz1
+ vmuleuw 14, 8, \_vz2
+ vmulouw 15, 8, \_vz2
+ vmuleuw 16, 9, \_vz3
+ vmulouw 17, 9, \_vz3
+.endm
+
+/*
+ * @brief When multiplying 2 numbers mod q that are in montgomery form, the
+ * product mod q needs to be multiplied by 2^-32 to be in montgomery form.
+ * See FIPS 204, Algorithm 49, MontgomeryReduce()
+ * Note it is slightly different due to the input range being positive
+ *
+ * @param a is the result of a multiply of 2 numbers in montgomery form,
+ * in the range 0...(2^32)*q
+ * @returns The Montgomery form of 'a' with multiplier 2^32 in the range 0..q-1
+ * The result is congruent to x * 2^-32 mod q
+ */
+.macro Mont_reduce_4x
+ vmulouw 18, 10, Q_NEG_INV
+ vmulouw 19, 11, Q_NEG_INV
+ vmulouw 20, 12, Q_NEG_INV
+ vmulouw 21, 13, Q_NEG_INV
+ vmulouw 22, 14, Q_NEG_INV
+ vmulouw 23, 15, Q_NEG_INV
+ vmulouw 24, 16, Q_NEG_INV
+ vmulouw 25, 17, Q_NEG_INV
+
+ vmulouw 18, 18, V_Q
+ vmulouw 19, 19, V_Q
+ vmulouw 20, 20, V_Q
+ vmulouw 21, 21, V_Q
+ vmulouw 22, 22, V_Q
+ vmulouw 23, 23, V_Q
+ vmulouw 24, 24, V_Q
+ vmulouw 25, 25, V_Q
+
+ vaddudm 18, 18, 10
+ vaddudm 19, 19, 11
+ vaddudm 20, 20, 12
+ vaddudm 21, 21, 13
+ vaddudm 22, 22, 14
+ vaddudm 23, 23, 15
+ vaddudm 24, 24, 16
+ vaddudm 25, 25, 17
+
+ vmrgew 10, 18, 19
+ vmrgew 11, 20, 21
+ vmrgew 12, 22, 23
+ vmrgew 13, 24, 25
+
+ reduce_once_1x 10
+ reduce_once_1x 11
+ reduce_once_1x 12
+ reduce_once_1x 13
+.endm
+
+/*
+ * -----------------------------------
+ * mont_mul_reduce_4x(_vz0, _vz1, _vz2, _vz3)
+ */
+.macro mont_mul_reduce_4x _vz0 _vz1 _vz2 _vz3
+ Mont_scalar_mul_4x \_vz0, \_vz1, \_vz2, \_vz3
+ Mont_reduce_4x
+.endm
+/* ===================================================================== */
diff --git a/crypto/ml_dsa/build.info b/crypto/ml_dsa/build.info
index e41867f573..3c74aef2f7 100644
--- a/crypto/ml_dsa/build.info
+++ b/crypto/ml_dsa/build.info
@@ -8,6 +8,12 @@ $ML_DSA_ASM=
IF[{- !$disabled{asm} -}]
$ML_DSA_ASM_x86_64=ml_dsa_ntt-x86_64.s
+ IF[{- $target{sys_id} ne "AIX" && $target{sys_id} ne "MACOSX" -}]
+ $ML_DSA_ASM_ppc64=asm/mldsa_ntt_ppc64le.S asm/mldsa_intt_ppc64le.S
+ ENDIF
+
+ # Now that we have defined all the arch specific variables, use the
+ # appropriate one, and define the appropriate macros
IF[$ML_DSA_ASM_{- $target{asm_arch} -}]
$ML_DSA_ASM=$ML_DSA_ASM_{- $target{asm_arch} -}
ENDIF
@@ -27,9 +33,6 @@ IF[{- !$disabled{'ml-dsa'} -}]
ENDIF
ENDIF
-DEFINE[../../libcrypto]=$ML_DSA_DEF
-DEFINE[../../providers/libfips.a]=$ML_DSA_DEF
-
IF[{- !$disabled{'ml-dsa'} -}]
SOURCE[../../libcrypto]=$COMMON $ML_DSA_ASM $ML_DSA_VX
SOURCE[../../providers/libfips.a]=$COMMON $ML_DSA_ASM $ML_DSA_VX
diff --git a/crypto/ml_dsa/ml_dsa_ntt.c b/crypto/ml_dsa/ml_dsa_ntt.c
index 3f5ebd5206..17a63802f3 100644
--- a/crypto/ml_dsa/ml_dsa_ntt.c
+++ b/crypto/ml_dsa/ml_dsa_ntt.c
@@ -227,6 +227,34 @@ static void poly_ntt_inverse_avx2_wrapper(POLY *p)
}
#endif
+/*
+ * PPC64le wrapper functions.
+ */
+#if !defined(OPENSSL_NO_ASM) && defined(_ARCH_PPC64) && (defined(__LITTLE_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+#define MLDSA_NTT_PPC_ASM
+#include "arch/ppc_arch.h"
+#endif
+
+#if defined(MLDSA_NTT_PPC_ASM)
+extern void mldsa_poly_ntt_mult_ppc(POLY *out, const POLY *lhs, const POLY *rhs);
+static void poly_ntt_mult_ppc64le_wrapper(const POLY *lhs, const POLY *rhs, POLY *out)
+{
+ mldsa_poly_ntt_mult_ppc(out, lhs, rhs);
+}
+
+extern void mldsa_poly_ntt_ppc(uint32_t *p);
+static void poly_ntt_ppc64le_wrapper(POLY *p)
+{
+ mldsa_poly_ntt_ppc(p->coeff);
+}
+
+extern void mldsa_poly_ntt_inverse_ppc(uint32_t *p);
+static void poly_ntt_inverse_ppc64le_wrapper(POLY *p)
+{
+ mldsa_poly_ntt_inverse_ppc(p->coeff);
+}
+#endif
+
/*
* Initialize NTT function pointers to AVX2 implementations if available.
* Scalar implementations are used by default.
@@ -240,6 +268,15 @@ static void ml_dsa_ntt_init(void)
poly_ntt_mult_impl = poly_ntt_mult_avx2_wrapper;
}
#endif
+
+#if defined(MLDSA_NTT_PPC_ASM)
+ if (OPENSSL_ppccap_P & PPC_CRYPTO207) {
+ poly_ntt_impl = poly_ntt_ppc64le_wrapper;
+ poly_ntt_inverse_impl = poly_ntt_inverse_ppc64le_wrapper;
+ poly_ntt_mult_impl = poly_ntt_mult_ppc64le_wrapper;
+ }
+#endif
+
#ifdef VX_COMPILER_SUPPORT_VEC128
if (S390X_VX_CAPABLE) {
poly_ntt_impl = ossl_ml_dsa_poly_ntt_vec128;
diff --git a/crypto/ml_dsa/ml_dsa_poly.h b/crypto/ml_dsa/ml_dsa_poly.h
index 7998fdd9c1..ca9a0c00a8 100644
--- a/crypto/ml_dsa/ml_dsa_poly.h
+++ b/crypto/ml_dsa/ml_dsa_poly.h
@@ -20,6 +20,8 @@
struct poly_st {
#if defined(VX_COMPILER_SUPPORT_VEC128)
ALIGN16 uint32_t coeff[ML_DSA_NUM_POLY_COEFFICIENTS];
+#elif defined(_ARCH_PPC64)
+ ALIGN16 uint32_t coeff[ML_DSA_NUM_POLY_COEFFICIENTS];
#else
uint32_t coeff[ML_DSA_NUM_POLY_COEFFICIENTS];
#endif